LINE Solver (C++)
Templated C++ port of the LINE queueing solver
Loading...
Searching...
No Matches
method_type.h
Go to the documentation of this file.
1#ifndef LINE_UTIL_METHOD_TYPE_H
2#define LINE_UTIL_METHOD_TYPE_H
3
4/**
5 * @file
6 * @ingroup line_util
7 * Classification of a solution method, as printed in the solver banner:
8 * "<accuracy>, <randomness>" with accuracy in {exact, approximate, bound} and
9 * randomness in {deterministic, randomized}.
10 *
11 * Conventions, applied uniformly across the four codebases (MATLAB
12 * line_method_type.m, JAR MethodType.java, python solvers/base.py):
13 * - exact: the algorithm targets the metric with no modeling
14 * approximation. Numerical truncation and floating-point
15 * error do not make a method approximate, so an integral
16 * representation or a transform inversion is exact while an
17 * asymptotic expansion is not.
18 * - approximate: the algorithm introduces a heuristic, an asymptotic
19 * expansion, a decomposition, or a statistical estimate.
20 * - bound: the algorithm returns a formal one-sided bound on the
21 * metric, not a point estimate: the value is guaranteed to
22 * lie on the stated side of the exact one, and the two sides
23 * of a family bracket it. The side is read off the method
24 * label and printed with it ('gb.upper' -> 'upper bound').
25 * - randomized: the algorithm consumes pseudo-random numbers, so two runs
26 * agree only if the seed does.
27 *
28 * Perfect sampling (cftp) is classified by the law it samples from, which is
29 * the stationary one, hence exact; the ordinary simulators are approximate
30 * because a finite horizon leaves warm-up bias on top of the sampling error.
31 *
32 * The C++ CLI keeps its own key=value banner grammar, so this classification is
33 * printed there as `type=exact,deterministic` rather than inside the bracketed
34 * field list the other three codebases use.
35 */
36
37#include <map>
38#include <string>
39
40namespace line {
41namespace util {
42
43namespace detail {
44
45inline const std::map<std::string, std::string>& method_type_registry() {
46 static const std::map<std::string, std::string> reg = [] {
47 std::map<std::string, std::string> r;
48 struct Group {
49 const char* label;
50 const char* tokens; // space separated
51 };
52 // product-form evaluation and the exact single-queue closed forms
53 const Group groups[] = {
54 {"exact,deterministic",
55 "exact mva mvac recal conv ca comom comomld rd nrp nrl nre clw gleint mmint2 rgf "
56 "ger "
57 "lcfsqn.ca nc.oi.exact mm1 mmk mxm1 mm1k mg1 gm1 mapm1ps pas mg1.prio mg1.fb "
58 "mg1.srpt mg1.psjf mg1.setf mg1.lrpt mm1.dps ctmc sync flat fd "
59 "uniformization exact.mapmap1 jmva jmva.mva jmva.recal jmva.comom lossn.exact "
60 // Krzesinski state-dependent routing: eq. (16) by enumeration, and
61 // its Section 4 MVA and convolution
62 "sdr sdr.mva "
63 // MDD-rec: the normalising constant of a product form, summed
64 // EXACTLY over the reachable set by one memoised walk of the
65 // decision diagram that holds it. On a Petri net
66 // (solver_nc_spn_analyzer) and on a loss network (lossn_rec) alike
67 "rec lossn.rec mdd.rec "
68 // discrete-time (slotted) product form, Daduna (2001)
69 "dt.bernoulli1 dt.cycle dt.cycleld "
70 // mixed open/closed limited load dependence: the Bruell-Balbo-Afshari
71 // effective-capacity MVA evaluates the product form itself, no expansion
72 "ncldmx"},
73 // coupling from the past samples the stationary law itself
74 {"exact,randomized", "cftp nc.cftp"},
75 // approximate MVA, open-network decomposition, expansions, mean-field,
76 // phase-type decomposition, layered and environment decomposition
77 {"approximate,deterministic",
78 "amva bs aql qsa lin gflin egflin dmlin qd qdlin qdaql qli fli ab schmidt "
79 "schmidt-ext schmidtext sqni sum esum cl chandy-lakshmi shadow seidmann "
80 "linearizerms conway rolia zhou suri reiser.ms chow marie sqd mapqn interp highvar "
81 "balanced qna rqna rqt gig1 gigk klb kraemer mg1k mm1k.approx le ble dir aghq cub kt bkt lekt pana "
82 "panald mem mem.blocking gm erlangfp propfair fpi spm ttl oi "
83 "balancedfairness stationtime fld fluid matrix closing statedep softmin pnorm "
84 "mfq rmf tbi diffusion minnormal refined kp "
85 "mam dec mna inap inaprc inapinf ldqbd qbd qiu cdf "
86 "reneging retrial ln layers ln.dec enhanced ln.fld moment3 lqns srvn "
87 "lqns.default exactmva srvn.exactmva ln.mva qns env env.blend blend blending "
88 "mean meancov env.meancov dec.avg jmva.amva "
89 "jmva.chow jmva.bs jmva.aql jmva.lin jmva.dmlin auto tree auto.tree forest cart"},
90 // formal one-sided bounds; 'cub.upper', 'qrf.mem' and 'qrf.bas.mem' are
91 // spelt out because their tails 'cub' and 'mem' are NC method names
92 {"bound,deterministic",
93 "ba aba bjb mbjb gb pb sb mw mw.upper mw.lower pbh bjbh cbh ssd sib scb "
94 "ldac qr lr qrf harel bpt bgt cub.upper qrf.mem qrf.bas.mem spnlp1 spnlp2"},
95 // discrete-event simulation, Monte Carlo normalizing constants, and the
96 // sampler that stops before coalescence
97 {"approximate,randomized",
98 "ssa ldes serial para pana parallel nrm jsim jmt lqsim sim uq mci imci "
99 "ls is sampling lossn.mci cftp.approx "
100 // Markov chain Monte Carlo on the regularized network (Chen-O'Cinneide)
101 "mcmc nc.mcmc"},
102 // per-solver default for a method with no entry of its own; '#' keeps the
103 // solver name out of the method namespace, since 'mva' is also a method
104 {"exact,deterministic", "#ctmc"},
105 {"approximate,randomized", "#ssa #ldes #jmt"},
106 {"bound,deterministic", "#ba"},
107 {"approximate,deterministic",
108 "#mva #nc #fld #fluid #mam #ln #env #lqns #auto #uq"},
109 };
110 for (std::size_t g = 0; g < sizeof(groups) / sizeof(groups[0]); ++g) {
111 const std::string toks(groups[g].tokens);
112 std::size_t i = 0;
113 while (i < toks.size()) {
114 const std::size_t j = toks.find(' ', i);
115 const std::string tok = toks.substr(i, j == std::string::npos ? j : j - i);
116 if (!tok.empty()) r[tok] = groups[g].label;
117 if (j == std::string::npos) break;
118 i = j + 1;
119 }
120 }
121 return r;
122 }();
123 return reg;
124}
125
126inline std::string lower_trim(const std::string& s) {
127 std::size_t b = s.find_first_not_of(" \t");
128 if (b == std::string::npos) return std::string();
129 std::size_t e = s.find_last_not_of(" \t");
130 std::string out = s.substr(b, e - b + 1);
131 for (std::size_t i = 0; i < out.size(); ++i) {
132 if (out[i] >= 'A' && out[i] <= 'Z') out[i] = char(out[i] - 'A' + 'a');
133 }
134 return out;
135}
136
137// A bound is reported with the side it lies on, which the method label carries
138// as its last component ('gb.upper' -> 'upper bound'). A family with no sided
139// variant (the qrf reductions) stays the unqualified 'bound'.
140inline std::string bound_side(const std::string& label, const std::string& method) {
141 if (label.compare(0, 5, "bound") != 0) return label;
142 const std::size_t n = method.size();
143 if (n >= 6 && method.compare(n - 6, 6, ".upper") == 0) return "upper " + label;
144 if (n >= 6 && method.compare(n - 6, 6, ".lower") == 0) return "lower " + label;
145 return label;
146}
147
148inline std::string method_type_lookup(const std::string& solvername, const std::string& method) {
149 const std::map<std::string, std::string>& reg = detail::method_type_registry();
150 std::string solver = detail::lower_trim(solvername);
151 if (solver.compare(0, 6, "solver") == 0) solver = solver.substr(6);
152 std::string m = detail::lower_trim(method);
153 // the banner label is 'default/<resolved>' once a default has been resolved
154 const std::size_t slash = m.find_last_of('/');
155 if (slash != std::string::npos) m = m.substr(slash + 1);
156
157 std::map<std::string, std::string>::const_iterator it;
158 if (!solver.empty() && !m.empty()) {
159 it = reg.find(solver + "." + m);
160 if (it != reg.end()) return it->second;
161 }
162 if (!m.empty()) {
163 it = reg.find(m);
164 if (it != reg.end()) return it->second;
165 const std::size_t dot = m.find('.');
166 std::size_t from = dot;
167 while (from != std::string::npos) { // 'a.b.c' -> 'b.c', 'c'
168 it = reg.find(m.substr(from + 1));
169 if (it != reg.end()) return it->second;
170 from = m.find('.', from + 1);
171 }
172 if (dot != std::string::npos) {
173 it = reg.find(m.substr(0, dot));
174 if (it != reg.end()) return it->second;
175 }
176 }
177 if (!solver.empty()) {
178 it = reg.find("#" + solver);
179 if (it != reg.end()) return it->second;
180 }
181 return "approximate,deterministic";
182}
183
184} // namespace detail
185
186/**
187 * Banner classification of a solution method.
188 *
189 * Lookup order: "<solver>.<method>", "<method>", the tail after each dot of the
190 * method (longest suffix first), the head before its first dot, "#<solver>",
191 * then "approximate,deterministic". The unknown-method default is the
192 * conservative one: claiming exactness a method does not have is the costlier
193 * error.
194 *
195 * @param solvername banner solver name, with or without the "Solver" prefix
196 * @param method resolved method label, possibly carrying a "default/" prefix
197 */
198inline std::string method_type(const std::string& solvername, const std::string& method) {
199 std::string m = detail::lower_trim(method);
200 const std::size_t slash0 = m.find_last_of('/');
201 if (slash0 != std::string::npos) m = m.substr(slash0 + 1);
202 return detail::bound_side(detail::method_type_lookup(solvername, method), m);
203}
204
205} // namespace util
206} // namespace line
207
208#endif // LINE_UTIL_METHOD_TYPE_H
double dot(const std::vector< double > &a, const std::vector< double > &b)
The inner product of a row vector with a column held as a vector.
Definition mg1.h:240
std::string method_type(const std::string &solvername, const std::string &method)
Banner classification of a solution method.
Conservation laws of a layered queueing network, enumerated from its structure.
Definition aoi_dist2ph.h:52