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 gpu 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 ctmc.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 inapplus inapinf ldqbd qbd qiu cdf "
86 "reneging retrial ln layers ln.dec enhanced ln.fluid moment3 lqns srvn "
87 "lqnsdefault exactmva srvn.exactmva ln.mva qns env env.blend blend dec.avg jmva.amva "
88 "jmva.chow jmva.bs jmva.aql jmva.lin jmva.dmlin auto tree auto.tree forest cart"},
89 // formal one-sided bounds; 'cub.upper', 'qrf.mem' and 'qrf.bas.mem' are
90 // spelt out because their tails 'cub' and 'mem' are NC method names
91 {"bound,deterministic",
92 "ba aba bjb mbjb gb pb sb mwba mwba.upper mwba.lower pbh pbk bjbk cbh ssd sib scb "
93 "ldbcmp qr lr qrf harel bpt bgt cub.upper qrf.mem qrf.bas.mem spnlp"},
94 // discrete-event simulation, Monte Carlo normalizing constants, and the
95 // sampler that stops before coalescence
96 {"approximate,randomized",
97 "ssa ldes serial para parallel nrm jsim replication jmt lqsim sim uq mci imci "
98 "ls is sampling lossn.mci cftp.approx "
99 // Markov chain Monte Carlo on the regularized network (Chen-O'Cinneide)
100 "mcmc nc.mcmc"},
101 // per-solver default for a method with no entry of its own; '#' keeps the
102 // solver name out of the method namespace, since 'mva' is also a method
103 {"exact,deterministic", "#ctmc"},
104 {"approximate,randomized", "#ssa #ldes #jmt"},
105 {"bound,deterministic", "#ba"},
106 {"approximate,deterministic",
107 "#mva #nc #fld #fluid #mam #ln #env #lqns #qns #auto #uq"},
108 };
109 for (std::size_t g = 0; g < sizeof(groups) / sizeof(groups[0]); ++g) {
110 const std::string toks(groups[g].tokens);
111 std::size_t i = 0;
112 while (i < toks.size()) {
113 const std::size_t j = toks.find(' ', i);
114 const std::string tok = toks.substr(i, j == std::string::npos ? j : j - i);
115 if (!tok.empty()) r[tok] = groups[g].label;
116 if (j == std::string::npos) break;
117 i = j + 1;
118 }
119 }
120 return r;
121 }();
122 return reg;
123}
124
125inline std::string lower_trim(const std::string& s) {
126 std::size_t b = s.find_first_not_of(" \t");
127 if (b == std::string::npos) return std::string();
128 std::size_t e = s.find_last_not_of(" \t");
129 std::string out = s.substr(b, e - b + 1);
130 for (std::size_t i = 0; i < out.size(); ++i) {
131 if (out[i] >= 'A' && out[i] <= 'Z') out[i] = char(out[i] - 'A' + 'a');
132 }
133 return out;
134}
135
136// A bound is reported with the side it lies on, which the method label carries
137// as its last component ('gb.upper' -> 'upper bound'). A family with no sided
138// variant (the qrf reductions) stays the unqualified 'bound'.
139inline std::string bound_side(const std::string& label, const std::string& method) {
140 if (label.compare(0, 5, "bound") != 0) return label;
141 const std::size_t n = method.size();
142 if (n >= 6 && method.compare(n - 6, 6, ".upper") == 0) return "upper " + label;
143 if (n >= 6 && method.compare(n - 6, 6, ".lower") == 0) return "lower " + label;
144 return label;
145}
146
147inline std::string method_type_lookup(const std::string& solvername, const std::string& method) {
148 const std::map<std::string, std::string>& reg = detail::method_type_registry();
149 std::string solver = detail::lower_trim(solvername);
150 if (solver.compare(0, 6, "solver") == 0) solver = solver.substr(6);
151 std::string m = detail::lower_trim(method);
152 // the banner label is 'default/<resolved>' once a default has been resolved
153 const std::size_t slash = m.find_last_of('/');
154 if (slash != std::string::npos) m = m.substr(slash + 1);
155
156 std::map<std::string, std::string>::const_iterator it;
157 if (!solver.empty() && !m.empty()) {
158 it = reg.find(solver + "." + m);
159 if (it != reg.end()) return it->second;
160 }
161 if (!m.empty()) {
162 it = reg.find(m);
163 if (it != reg.end()) return it->second;
164 const std::size_t dot = m.find('.');
165 std::size_t from = dot;
166 while (from != std::string::npos) { // 'a.b.c' -> 'b.c', 'c'
167 it = reg.find(m.substr(from + 1));
168 if (it != reg.end()) return it->second;
169 from = m.find('.', from + 1);
170 }
171 if (dot != std::string::npos) {
172 it = reg.find(m.substr(0, dot));
173 if (it != reg.end()) return it->second;
174 }
175 }
176 if (!solver.empty()) {
177 it = reg.find("#" + solver);
178 if (it != reg.end()) return it->second;
179 }
180 return "approximate,deterministic";
181}
182
183} // namespace detail
184
185/**
186 * Banner classification of a solution method.
187 *
188 * Lookup order: "<solver>.<method>", "<method>", the tail after each dot of the
189 * method (longest suffix first), the head before its first dot, "#<solver>",
190 * then "approximate,deterministic". The unknown-method default is the
191 * conservative one: claiming exactness a method does not have is the costlier
192 * error.
193 *
194 * @param solvername banner solver name, with or without the "Solver" prefix
195 * @param method resolved method label, possibly carrying a "default/" prefix
196 */
197inline std::string method_type(const std::string& solvername, const std::string& method) {
198 std::string m = detail::lower_trim(method);
199 const std::size_t slash0 = m.find_last_of('/');
200 if (slash0 != std::string::npos) m = m.substr(slash0 + 1);
201 return detail::bound_side(detail::method_type_lookup(solvername, method), m);
202}
203
204} // namespace util
205} // namespace line
206
207#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:236
std::string method_type(const std::string &solvername, const std::string &method)
Banner classification of a solution method.