5#ifndef LINE_SOLVERS_BA_SOLVER_BA_RUNNER_H
6#define LINE_SOLVERS_BA_SOLVER_BA_RUNNER_H
53using lang::GlobalConstants;
61 if (method ==
"default")
return "gb.upper";
62 if (method ==
"auto")
return "auto.upper";
63 if (method ==
"lr")
return "lr.upper";
64 if (method ==
"qr")
return "qrf.mmi";
87 return !(method.rfind(
"qrf.bas", 0) == 0 || method.rfind(
"qrf.rsrd", 0) == 0 ||
88 method.rfind(
"spnlp", 0) == 0);
108 "auto.upper",
"auto.lower",
109 "aba.upper",
"aba.lower",
110 "bjb.upper",
"bjb.lower",
111 "pb.upper",
"pb.lower",
112 "gb.upper",
"gb.lower",
113 "sb.upper",
"sb.lower",
114 "harel.upper",
"harel.lower",
115 "mwba.upper",
"mwba.lower",
116 "pbh.upper",
"pbh.lower",
117 "pbk.upper",
"pbk.lower",
118 "bjbk.upper",
"bjbk.lower",
119 "cbh.upper",
"cbh.lower",
120 "ssd.upper",
"ssd.lower",
121 "cub.upper",
"mbjb.lower",
122 "looping.upper",
"looping.lower",
123 "bpt.lower",
"bgt.upper",
"snc.upper",
124 "sib.upper",
"sib.lower",
125 "scb.upper",
"scb.lower",
127 "lr",
"lr.upper",
"lr.lower",
128 "mapamva.upper",
"mapamva.lower",
129 "qr",
"qrf.mmi",
"qrf.mem",
"qrf.bethe",
130 "qrf.mmi.ld",
"qrf.mmi.linear",
131 "qrf.bas",
"qrf.bas.mmi",
"qrf.bas.mem",
"qrf.bas.bethe",
133 "spnlp.upper",
"spnlp.lower",
"spnlp.op.upper",
"spnlp.op.lower"};
174 typedef std::pair<std::string, std::string> R;
177 return R(
"",
"the QRF blocking bounds are derived for a single-class closed network,"
178 " which this model is not.");
179 for (std::size_t r = 0; r < L.
classes.size(); ++r)
180 if (!std::isfinite(L.
classes[r].population))
181 return R(
"",
"the QRF blocking bounds are derived for a single-class closed"
182 " network, which this model is not.");
183 for (std::size_t i = 0; i < L.
stations.size(); ++i) {
184 if (L.
stations[i].sched == SchedStrategy::INF)
185 return R(
"",
"the QRF blocking bounds model every station as a single server and"
186 " have no infinite-server notion, so a delay station rules them out.");
188 return R(
"",
"the QRF blocking bounds model every station as a single server, so a"
189 " multiserver station rules them out.");
194 for (std::size_t i = 0; i < L.
stations.size(); ++i) {
196 Ktot += mp.
D0.rows() == 0 ? 1 :
static_cast<int>(mp.
D0.rows());
199 if (!blk.msg.
empty())
return R(
"", blk.msg);
200 return R(
"qrf.bas",
"");
205 return method ==
"default" || method ==
"auto" || method ==
"auto.upper";
226 std::vector<std::string> structural;
227 for (std::size_t i = 0; i < all.size(); ++i)
229 structural.push_back(all[i]);
240 bool isPetri =
false;
241 for (std::size_t i = 0; i < L.
nodes.size() && !isPetri; ++i)
244 std::vector<std::string> sieved;
245 for (std::size_t i = 0; i < all.size(); ++i)
248 if (isPetri)
return all;
251 for (std::size_t r = 0; reducible && r < L.
classes.size(); ++r)
252 if (!std::isfinite(L.
classes[r].population)) reducible =
false;
253 bool closedSingleClass = reducible;
254 for (std::size_t i = 0; reducible && i < L.
stations.size(); ++i) {
255 if (L.
stations[i].sched == SchedStrategy::INF)
257 else if (L.
stations[i].nservers > 1.0)
268 const bool ldReducible =
275 bool fullyOpen = !L.
classes.empty();
276 for (std::size_t r = 0; fullyOpen && r < L.
classes.size(); ++r)
277 if (std::isfinite(L.
classes[r].population)) fullyOpen =
false;
278 bool bptOk = fullyOpen;
279 for (std::size_t i = 0; bptOk && i < L.
stations.size(); ++i) {
280 if (L.
stations[i].nodetype == qn::NodeType::Source)
continue;
281 if (L.
stations[i].sched == SchedStrategy::INF)
283 else if (L.
stations[i].nservers > 1.0)
286 std::vector<std::string> keep;
294 const char*
const open_fams[] = {
"bpt.lower",
302 "bgt.upper",
"snc.upper"};
303 for (
const char* nm : open_fams)
304 if (std::find(all.begin(), all.end(), std::string(nm)) != all.end())
308 for (std::size_t i = 0; i < all.size(); ++i) {
309 const std::string& m = all[i];
310 if (m ==
"bpt.lower" || m ==
"bgt.upper" || m ==
"snc.upper")
continue;
315 if (ldReducible && (m ==
"qrf.mmi.ld" || m ==
"qrf.mmi.linear")) {
324 if (m ==
"qr" || m ==
"lr" || m.compare(0, 3,
"lr.") == 0 ||
325 m.compare(0, 4,
"qrf.") == 0 || m.compare(0, 7,
"mapamva") == 0)
337 std::vector<std::string> unblocked;
343 for (std::size_t i = 0; i < keep.size(); ++i)
357 if (std::find(valid.begin(), valid.end(), m) != valid.end())
return;
376 opt_in.
method.compare(0, 3,
"bgt") != 0 && opt_in.
method.compare(0, 3,
"snc") != 0 &&
390 std::string blocking_why;
398 blocking_why = routed.second;
399 if (!routed.first.empty())
opt.method = routed.first;
404 "SolverBA: method '" + opt_in.
method +
405 "' does not support finite-buffer blocking: every SolverBA bound family but the"
406 " QRF blocking ones is parameterized by demands and a population alone, so it"
407 " bounds the model as if its buffers were unbounded. Use SolverMVA with method"
408 " 'sqd', an exact solver (CTMC, SSA, JMT, LDES), or the QRF blocking bounds"
409 " 'qrf.bas'/'qrf.rsrd', which model the finite buffer" +
410 (blocking_why.empty()
412 :
". The QRF blocking bounds do not apply here either: " + blocking_why));
421 const bool qrf = detail::is_qrf_noblo_method(
opt.method) ||
422 detail::is_qrf_lp_method(
opt.method) ||
423 detail::is_qrf_bas_nlp_method(
opt.method);
430 std::vector<std::vector<bool>> mask(M, std::vector<bool>(K,
false));
431 for (std::size_t i = 0; i < M; ++i)
432 for (std::size_t k = 0; k < K; ++k)
446 std::vector<std::vector<bool>> srcmask(M, std::vector<bool>(K,
false));
447 for (std::size_t i = 0; i < M; ++i)
448 if (L.
stations[i].nodetype == qn::NodeType::Source)
449 for (std::size_t k = 0; k < K; ++k) srcmask[i][k] =
true;
478 std::vector<std::vector<bool>>
keep;
493 const std::string fam = m.substr(0, m.find(
'.'));
500 if (!routed.first.empty())
502 "SolverBA: '" +
opt.method +
"' resolves to '" + routed.first +
503 "' on this model, which has a binding finite buffer, and that bound is"
504 " UPPER-only: there is no bracket to return. Call getAvg for the upper"
505 " bound, or SolverMVA with method 'sqd' for a point estimate");
508 auto listed = [&](
const std::string& x) {
509 return std::find(valid.begin(), valid.end(), x) != valid.end();
511 const double nan = std::numeric_limits<double>::quiet_NaN();
519 if (listed(fam +
".lower")) {
521 o.
method = fam +
".lower";
527 if (listed(fam +
".upper")) {
529 o.
method = fam +
".upper";
535 b.
keep.assign(M, std::vector<bool>(K,
true));
536 for (std::size_t i = 0; i < M; ++i)
537 for (std::size_t k = 0; k < K; ++k) {
539 bool any_present =
false, any_nonzero =
false;
540 for (
const T& x : v) {
545 b.
keep[i][k] = !any_present || any_nonzero;
UnsupportedError(const std::string &what)
A network plus its refreshed NetworkStruct.
std::vector< std::vector< Distrib< T > > > service
service[i][r], 0-based station and class; a disabled entry marks a pair never visited.
std::vector< JobClass > classes
std::vector< Station< T > > stations
stations[k-1] is the k-th station
std::vector< NodeDef > nodes
every node, in creation order
double nclosedjobs() const
sn.nclosedjobs: the total population of the closed classes.
bool sn_has_blocking(const qn::NetworkStruct< T > &sn)
Defined below, after sn_is_mm1k_loss, which it exempts.
BaBounds< T > ba_bounds(const qn::NetworkStruct< T > &L, const BaOptions &opt)
Port of SolverBA.getBounds.
BaSolution< T > solver_ba_qrf_analyzer(const qn::NetworkStruct< T > &L, const BaOptions &opt)
Port of solver_ba_qrf_analyzer.
bool is_default_request(const std::string &method)
True for the aliases whose meaning a blocked model is allowed to change.
bool ignores_blocking(const std::string &method)
Whether METHOD bounds a model as if its buffers were unbounded.
std::pair< std::string, std::string > blocking_default(const qn::NetworkStruct< T > &L)
The same list, narrowed to what THIS model can run.
std::string method_refusal(const qn::NetworkStruct< T > &L, const std::string &method)
The STRUCTURAL premises of the SolverBA bound families, in one place: the reason METHOD cannot bound ...
BaSolution< T > solver_ba_analyzer(const qn::NetworkStruct< T > &L, const BaOptions &opt)
Port of solver_ba_analyzer.
void check_method(const std::string &method)
Port of runAnalyzer's method gate: an unlisted name is refused by name.
BaSolution< T > solver_ba_spnlp_analyzer(const qn::NetworkStruct< T > &L, const BaOptions &opt)
Moment-relaxation LP bounds for a stochastic Petri net.
mva::AvgResult< T > solver_ba_run_analyzer(const qn::NetworkStruct< T > &L, const BaOptions &opt_in)
Port of @@SolverBA/runAnalyzer.m for the lang='matlab' path.
std::string method_degenerate(const qn::NetworkStruct< T > &L, const std::string &method)
Whether METHOD APPLIES to L but its bound carries no information there, and why.
std::vector< std::string > list_valid_methods()
Port of SolverBA.listValidMethods.
bool is_spnlp_method(const std::string &method)
Whether a resolved method name belongs to the Petri-net LP family.
std::string resolve_method(const std::string &method)
Port of runAnalyzer's method aliases: default is the geometric upper bound, bare auto is the AUTO com...
mam::Map< T > dist_to_map(const Distrib< T > &d)
Matrix< T > filter_metric(const qn::NetworkStruct< T > &L, const Matrix< T > &metric, MetricKind kind, const std::vector< std::vector< bool > > *zero_mask)
Port of filterMetric: what @@NetworkSolver/getAvg does between the analyzer and the caller.
Matrix< T > sn_get_arvr_from_tput(const qn::NetworkStruct< T > &L, const Matrix< T > &TN)
QrfBlocking sn_to_qrf_blocking(const qn::NetworkStruct< T > &sn, int Ktot, double max_vars=kQrfDefaultMaxVars)
The QRF BAS blocking tables (f, MR, BB, MM, ZZ, MM1), derived from an sn.
QrfAlpha sn_to_qrf_alpha(const qn::NetworkStruct< T > &L)
ld stays TRUE through a refusal: the model IS load dependent, and the caller has to tell "no arm serv...
A queueing network and its refreshed NetworkStruct.
Ports of the sn_has_* / sn_is_* predicate family of matlab/src/api/sn.
The QRF load-dependent rate scaling alpha(i,n), derived from an sn.
The QRF BAS blocking tables (f, MR, BB, MM, ZZ, MM1), derived from an sn.
Port of matlab/src/solvers/BA/solver_ba_analyzer.m, the bound-analysis handler behind SolverBA.
Port of matlab/src/solvers/BA/solver_ba_qrf_analyzer.m, the adapter that bridges the sn struct to the...
Linear-programming bounds on the mean marking and the throughputs of a stochastic timed Petri net.
The DECLARED side of the gate: one feature set per solver.
The SolverMVA class surface: @@SolverMVA/runAnalyzer.m and the gates around it.
Port of SolverBA.getBounds: the {lower,upper} bracket of a family.
std::vector< std::vector< bool > > keep
getBoundsTable's row filter, (M x K): whether the (station, class) pair earns a row.
Matrix< T > Qupper
(M x K), all-NaN on a side the family lacks
The options SolverBA reads.
std::string method
Bound method; default resolves to gb.upper in the runner.
Class-level results, the [Q,U,R,T,C,X] of solver_ba_analyzer.
static constexpr double FineTol
A MAP as the pair of matrices (D0, D1).
The metrics getAvg returns, after filtering.
Matrix< T > RN
response time, per visit
Matrix< T > UN
utilization
Matrix< T > WN
residence time, per job
std::string method
the method asked for
std::string actualmethod
the algorithm that ran
Matrix< T > QN
queue length
std::vector< T > CN
system response time per class
std::vector< T > XN
system throughput per class
Matrix< T > AN
arrival rate
std::string msg
empty on success
The derived blocking tables, in the reference's 1-based queue indexing.