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 "mw.upper",
"mw.lower",
116 "pbh.upper",
"pbh.lower",
117 "bjbh.upper",
"bjbh.lower",
118 "cbh.upper",
"cbh.lower",
119 "ssd.upper",
"ssd.lower",
120 "cub.upper",
"mbjb.lower",
121 "looping.upper",
"looping.lower",
122 "bpt.lower",
"bgt.upper",
"snc.upper",
123 "sib.upper",
"sib.lower",
124 "scb.upper",
"scb.lower",
126 "lr",
"lr.upper",
"lr.lower",
127 "mapamva.upper",
"mapamva.lower",
128 "qr",
"qrf.mmi",
"qrf.mem",
"qrf.bethe",
129 "qrf.mmi.ld",
"qrf.mmi.linear",
130 "qrf.bas",
"qrf.bas.mmi",
"qrf.bas.mem",
"qrf.bas.bethe",
132 "spnlp2.upper",
"spnlp2.lower",
"spnlp1.upper",
"spnlp1.lower"};
173 typedef std::pair<std::string, std::string> R;
176 return R(
"",
"the QRF blocking bounds are derived for a single-class closed network,"
177 " which this model is not.");
178 for (std::size_t r = 0; r < L.
classes.size(); ++r)
179 if (!std::isfinite(L.
classes[r].population))
180 return R(
"",
"the QRF blocking bounds are derived for a single-class closed"
181 " network, which this model is not.");
182 for (std::size_t i = 0; i < L.
stations.size(); ++i) {
183 if (L.
stations[i].sched == SchedStrategy::INF)
184 return R(
"",
"the QRF blocking bounds model every station as a single server and"
185 " have no infinite-server notion, so a delay station rules them out.");
187 return R(
"",
"the QRF blocking bounds model every station as a single server, so a"
188 " multiserver station rules them out.");
193 for (std::size_t i = 0; i < L.
stations.size(); ++i) {
195 Ktot += mp.
D0.rows() == 0 ? 1 :
static_cast<int>(mp.
D0.rows());
198 if (!blk.msg.
empty())
return R(
"", blk.msg);
199 return R(
"qrf.bas",
"");
204 return method ==
"default" || method ==
"auto" || method ==
"auto.upper";
225 std::vector<std::string> structural;
226 for (std::size_t i = 0; i < all.size(); ++i)
228 structural.push_back(all[i]);
239 bool isPetri =
false;
240 for (std::size_t i = 0; i < L.
nodes.size() && !isPetri; ++i)
243 std::vector<std::string> sieved;
244 for (std::size_t i = 0; i < all.size(); ++i)
247 if (isPetri)
return all;
250 for (std::size_t r = 0; reducible && r < L.
classes.size(); ++r)
251 if (!std::isfinite(L.
classes[r].population)) reducible =
false;
252 bool closedSingleClass = reducible;
253 for (std::size_t i = 0; reducible && i < L.
stations.size(); ++i) {
254 if (L.
stations[i].sched == SchedStrategy::INF)
256 else if (L.
stations[i].nservers > 1.0)
267 const bool ldReducible =
274 bool fullyOpen = !L.
classes.empty();
275 for (std::size_t r = 0; fullyOpen && r < L.
classes.size(); ++r)
276 if (std::isfinite(L.
classes[r].population)) fullyOpen =
false;
277 bool bptOk = fullyOpen;
278 for (std::size_t i = 0; bptOk && i < L.
stations.size(); ++i) {
279 if (L.
stations[i].nodetype == qn::NodeType::Source)
continue;
280 if (L.
stations[i].sched == SchedStrategy::INF)
282 else if (L.
stations[i].nservers > 1.0)
285 std::vector<std::string> keep;
293 const char*
const open_fams[] = {
"bpt.lower",
301 "bgt.upper",
"snc.upper"};
302 for (
const char* nm : open_fams)
303 if (std::find(all.begin(), all.end(), std::string(nm)) != all.end())
307 for (std::size_t i = 0; i < all.size(); ++i) {
308 const std::string& m = all[i];
309 if (m ==
"bpt.lower" || m ==
"bgt.upper" || m ==
"snc.upper")
continue;
314 if (ldReducible && (m ==
"qrf.mmi.ld" || m ==
"qrf.mmi.linear")) {
323 if (m ==
"qr" || m ==
"lr" || m.compare(0, 3,
"lr.") == 0 ||
324 m.compare(0, 4,
"qrf.") == 0 || m.compare(0, 7,
"mapamva") == 0)
336 std::vector<std::string> unblocked;
342 for (std::size_t i = 0; i < keep.size(); ++i)
356 if (std::find(valid.begin(), valid.end(), m) != valid.end())
return;
375 opt_in.
method.compare(0, 3,
"bgt") != 0 && opt_in.
method.compare(0, 3,
"snc") != 0 &&
389 std::string blocking_why;
397 blocking_why = routed.second;
398 if (!routed.first.empty())
opt.method = routed.first;
403 "SolverBA: method '" + opt_in.
method +
404 "' does not support finite-buffer blocking: every SolverBA bound family but the"
405 " QRF blocking ones is parameterized by demands and a population alone, so it"
406 " bounds the model as if its buffers were unbounded. Use SolverMVA with method"
407 " 'sqd', an exact solver (CTMC, SSA, JMT, LDES), or the QRF blocking bounds"
408 " 'qrf.bas'/'qrf.rsrd', which model the finite buffer" +
409 (blocking_why.empty()
411 :
". The QRF blocking bounds do not apply here either: " + blocking_why));
420 const bool qrf = detail::is_qrf_noblo_method(
opt.method) ||
421 detail::is_qrf_lp_method(
opt.method) ||
422 detail::is_qrf_bas_nlp_method(
opt.method);
429 std::vector<std::vector<bool>> mask(M, std::vector<bool>(K,
false));
430 for (std::size_t i = 0; i < M; ++i)
431 for (std::size_t k = 0; k < K; ++k)
445 std::vector<std::vector<bool>> srcmask(M, std::vector<bool>(K,
false));
446 for (std::size_t i = 0; i < M; ++i)
447 if (L.
stations[i].nodetype == qn::NodeType::Source)
448 for (std::size_t k = 0; k < K; ++k) srcmask[i][k] =
true;
477 std::vector<std::vector<bool>>
keep;
492 const std::string fam = m.substr(0, m.find(
'.'));
499 if (!routed.first.empty())
501 "SolverBA: '" +
opt.method +
"' resolves to '" + routed.first +
502 "' on this model, which has a binding finite buffer, and that bound is"
503 " UPPER-only: there is no bracket to return. Call getAvg for the upper"
504 " bound, or SolverMVA with method 'sqd' for a point estimate");
507 auto listed = [&](
const std::string& x) {
508 return std::find(valid.begin(), valid.end(), x) != valid.end();
510 const double nan = std::numeric_limits<double>::quiet_NaN();
518 if (listed(fam +
".lower")) {
520 o.
method = fam +
".lower";
526 if (listed(fam +
".upper")) {
528 o.
method = fam +
".upper";
534 b.
keep.assign(M, std::vector<bool>(K,
true));
535 for (std::size_t i = 0; i < M; ++i)
536 for (std::size_t k = 0; k < K; ++k) {
538 bool any_present =
false, any_nonzero =
false;
539 for (
const T& x : v) {
544 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...
Conservation laws of a layered queueing network, enumerated from its structure.
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.