5#ifndef LINE_SOLVERS_AUTO_AUTO_METHODS_H
6#define LINE_SOLVERS_AUTO_AUTO_METHODS_H
77#include <initializer_list>
120 return {
"mva",
"nc",
"ctmc",
"fluid",
"mam",
"ag",
"ba",
"ssa",
"ldes",
"jmt",
"qns"};
136 if (family ==
"mva")
return {
"amva."};
148 const std::vector<std::string>& declared) {
150 if (name.size() > prefix.size() && name.compare(0, prefix.size(), prefix) == 0 &&
151 std::find(declared.begin(), declared.end(), name.substr(prefix.size())) !=
161inline bool auto_slot_of_family(
const std::string& family,
AutoSolver& out) {
189bool capacity_admits(
const std::string& family,
const qn::NetworkStruct<T>& sn,
190 const std::string& method) {
191 if (family !=
"mva" && family !=
"nc" && family !=
"fluid" && family !=
"qns")
return true;
193 if (family ==
"nc" && method ==
"mem") {
195 return ms.supported && ms.blocking;
208inline bool declared_feature_set(
const std::string& family,
const std::string& method,
209 qn::FeatureSet& declared, std::string& label) {
210 if (family ==
"mva") { declared =
qn::mva_feature_set(method); label =
"SolverMVA";
return true; }
211 if (family ==
"nc") { declared =
qn::nc_feature_set(method); label =
"SolverNC";
return true; }
212 if (family ==
"mam") { declared =
qn::mam_feature_set(method); label =
"SolverMAM";
return true; }
213 if (family ==
"fluid") { declared =
qn::fluid_feature_set(method); label =
"SolverFLD";
return true; }
214 if (family ==
"ssa") { declared =
qn::ssa_feature_set(method); label =
"SolverSSA";
return true; }
215 if (family ==
"ctmc") { declared =
qn::ctmc_feature_set(method); label =
"SolverCTMC";
return true; }
216 if (family ==
"ldes") { declared =
qn::ldes_feature_set(method); label =
"SolverLDES";
return true; }
217 if (family ==
"ag") { declared =
qn::ag_feature_set(method); label =
"SolverAG";
return true; }
218 if (family ==
"ba") { declared =
qn::ba_feature_set(method); label =
"SolverBA";
return true; }
219 if (family ==
"jmt") { declared =
qn::jmt_feature_set(method); label =
"SolverJMT";
return true; }
220 if (family ==
"qns") { declared =
qn::qns_feature_set(method); label =
"SolverQNS";
return true; }
247 return std::vector<std::string>();
262 const std::string& method) {
263 if (!detail::capacity_admits(family,
sn, method))
264 return "SolverAUTO: this method ignores the finite station capacity this model sets "
265 "(setCapacity / classCap), so it would report a capped station as unbounded.";
269 if (!detail::declared_feature_set(family, method, declared, label))
270 return "SolverAUTO: the '" + family +
"' method family does not solve a flat Network.";
281 if (family ==
"nc") {
283 if (!ncr.empty())
return ncr;
295 if (family ==
"jmt") {
297 if (!why.empty())
return why;
299 if (family ==
"mam") {
301 if (!why.empty())
return why;
307 if (family ==
"ag") {
308 const std::string why = ag::runner_detail::method_refusal(
sn, method);
309 if (!why.empty())
return why;
314 if (family ==
"qns") {
316 if (!why.empty())
return why;
326 if (family ==
"ba") {
328 if (!why.empty())
return "SolverBA: " + why;
330 if (family ==
"ctmc") {
333 const std::string why =
334 (method ==
"cftp" || method ==
"cftp.approx")
337 if (!why.empty())
return why;
346 if (family ==
"ctmc" || family ==
"ssa") {
348 if (!why.empty())
return why;
356 if (family ==
"ssa" && method ==
"nrm") {
357 const std::string why = ssa::detail::ssa_nrm_supports(
sn);
358 if (!why.empty())
return why;
360 if (family ==
"fluid") {
365 if (!why.empty())
return why;
369 const std::string
fj = fluid::detail::fluid_forkjoin_supports(
sn, method);
370 if (!
fj.empty())
return fj;
378 if (detail::auto_slot_of_family(family, slot)) {
381 ": no engine for this solver is available in this build.";
385 ": 'exact' needs a product-form model, and this one has no product-form "
388 return "SolverCTMC: the state space of this model is too large to enumerate.";
389 return label +
": this solver refuses the model through its own structural check.";
397 const std::string& method) {
433 return {
"avg",
"tran",
"cdf",
"prob",
"tranprob",
"sample",
434 "cache",
"loss",
"orbit",
"moment",
"sens"};
439inline bool name_in(
const std::string& n, std::initializer_list<const char*> names) {
440 for (
const char* c : names)
441 if (n == c)
return true;
445inline bool starts_with(
const std::string& s,
const std::string& p) {
446 return s.size() >= p.size() && s.compare(0, p.size(), p) == 0;
464 if (name.empty())
return "";
466 if (g == name)
return g;
467 if (name ==
"any" || name ==
"all")
return "";
468 if (detail::name_in(name, {
"getTranAvg",
"getTranAvgVar",
"tranAvg"}))
return "tran";
469 if (detail::name_in(name, {
"getCdfRespT",
"getCdfPassT",
"getPerctRespT",
"getTranCdfPassT",
470 "getTranCdfRespT",
"getCdfSysRespT"}))
472 if (detail::name_in(name, {
"getTranProb",
"getTranProbSys",
"getTranProbAggr",
473 "getTranProbSysAggr"}))
475 if (detail::name_in(name, {
"getProb",
"getProbAggr",
"getProbSys",
"getProbSysAggr",
476 "getProbMarg",
"getProbNormConstAggr"}))
478 if (detail::name_in(name, {
"sample",
"sampleSys",
"sampleAggr",
"sampleSysAggr"}))
480 if (detail::name_in(name, {
"getAvgCacheTable",
"getAvgCacheT",
"getAvgItemTable",
481 "getAvgItemT",
"cacheAvgT",
"itemAvgT",
"aCaT",
"aIT"}))
483 if (detail::name_in(name, {
"getAvgLossTable",
"getAvgLossT",
"getAvgRegionLossTable",
484 "getAvgRegionLossT",
"lossAvgT",
"regionLossAvgT",
"aLT",
"aRLT"}))
486 if (detail::name_in(name, {
"getAvgOrbitTable",
"getAvgOrbitT",
"getAvgOrbit",
"orbitAvgT",
489 if (detail::name_in(name, {
"getMomentTable",
"getMomentChainTable",
"getMomentStationTable",
490 "getMomentT",
"getMomentChainT",
"getMomentStationT",
"momentT",
491 "momentChainT",
"momentStationT",
"mT",
"mCT",
"mST"}))
493 if (detail::name_in(name, {
"getSensitivityTable",
"getSensitivityT",
"sensitivityT",
"sT",
494 "getSensitivity",
"getSensitivityRanking"}))
498 if (detail::starts_with(name,
"getAvg") || detail::starts_with(name,
"avg") ||
499 detail::name_in(name, {
"getAvgSysRespT",
"getAvgSysTput",
"aT",
"aNT",
"aCT",
"aST",
526 if (family ==
"mva")
return {
"avg",
"prob",
"cache",
"orbit",
"moment",
"sens"};
527 if (family ==
"nc")
return {
"avg",
"cdf",
"prob",
"cache",
"moment",
"sens"};
528 if (family ==
"ctmc")
529 return {
"avg",
"tran",
"cdf",
"prob",
"tranprob",
"sample",
"cache",
"loss",
"orbit",
531 if (family ==
"fluid")
return {
"avg",
"tran",
"cdf",
"prob",
"cache",
"sens"};
532 if (family ==
"mam")
return {
"avg",
"cdf"};
535 if (family ==
"ag")
return {
"avg"};
537 if (family ==
"ba")
return {
"avg"};
538 if (family ==
"ssa")
return {
"avg",
"cdf",
"prob",
"sample",
"loss"};
539 if (family ==
"ldes")
540 return {
"avg",
"tran",
"cdf",
"prob",
"sample",
"cache",
"loss",
"orbit"};
541 if (family ==
"jmt")
return {
"avg",
"tran",
"cdf",
"prob",
"tranprob",
"sample"};
556 if (family ==
"ssa" || family ==
"ldes")
return true;
558 if (family ==
"jmt") {
560 std::vector<std::string> toks;
561 for (
char ch : method) {
562 if (ch ==
'.' || ch ==
'/') {
566 tok +=
static_cast<char>(std::tolower(
static_cast<unsigned char>(ch)));
571 for (
const std::string& t : toks)
572 if (t ==
"jmva") jmva =
true;
573 if (!jmva)
return true;
574 for (
const std::string& t : toks)
575 if (t ==
"ls" || t ==
"mci" || t ==
"imci" || t ==
"sampling")
return true;
582inline std::string exact_if(
bool cond) {
return cond ?
"exact" :
"approx"; }
613 bool is_stochastic,
bool is_product_form,
614 bool is_qbd_shape,
bool has_cache =
false) {
617 if (family ==
"ba")
return "bound";
618 if (is_stochastic)
return "simulation";
619 if (family ==
"ctmc") {
623 if (method ==
"cftp.approx")
return "approx";
624 if (method ==
"mdd")
return detail::exact_if(is_product_form);
627 if (family ==
"nc") {
632 if (detail::name_in(method, {
"exact",
"divdiff",
"ca",
"comom",
"comomld",
"rec",
"ms",
634 return detail::exact_if(is_product_form && !has_cache);
637 if (family ==
"mva") {
640 if (detail::name_in(method, {
"exact",
"mva"}))
641 return detail::exact_if(is_product_form && !has_cache);
644 if (family ==
"jmt") {
647 if (detail::name_in(method, {
"jmva.mva",
"jmva.recal",
"jmva.comom",
"jmva.treeconv"}))
648 return detail::exact_if(is_product_form && !has_cache);
651 if (family ==
"mam") {
655 if (detail::name_in(method, {
"default",
"mna",
"ldqbd",
"bgchain",
"retrial"}))
656 return detail::exact_if(is_qbd_shape);
659 if (family ==
"ag") {
690 const std::string& metric = std::string(),
691 bool show_all =
false) {
693 if (group.empty() && !metric.empty() && metric !=
"any" && metric !=
"all") {
696 if (!groups.empty()) groups +=
", ";
703 throw InputError(
"'" + metric +
"' names no measure. Pass a group (" + groups +
704 ") or the accessor that returns it, e.g. 'getCdfRespT'.");
709 const bool is_product_form =
sn.has_product_form();
710 std::size_t nsources = 0;
713 const std::vector<double> njobs =
sn.njobs();
714 bool all_open = !njobs.empty();
715 for (
double n : njobs)
716 if (!std::isinf(n)) all_open =
false;
717 const bool is_qbd_shape = all_open && (
sn.nstations - nsources) == 1;
718 bool has_cache =
false;
720 if (nd.
nodetype == qn::NodeType::Cache) has_cache =
true;
722 std::vector<SolverCandidate> rows;
725 if (!group.empty() &&
726 std::find(groups.begin(), groups.end(), group) == groups.end())
728 std::string metric_list;
729 for (
const std::string& g : groups) {
730 if (!metric_list.empty()) metric_list +=
",";
734 for (
const std::string& name : declared) {
735 if (detail::starts_with(name, family +
".")) {
752 const bool ok = reason.empty();
753 if (!ok && !show_all)
continue;
756 row.method = family +
"." + name;
760 is_qbd_shape, has_cache);
761 row.metrics = metric_list;
762 row.reason = ok ? std::string() : reason;
772 const std::string& metric = std::string(),
773 bool show_all =
false) {
780 const std::string& metric = std::string(),
781 bool show_all =
false) {
791 if (rows.empty())
return "No solver method can analyze this model.\n";
792 std::vector<std::array<std::string, 6>> cells;
793 cells.push_back({
"Solver",
"Method",
"Runnable",
"Class",
"Metrics",
"Reason"});
795 cells.push_back({r.solver, r.method, r.runnable ?
"true" :
"false", r.method_class,
796 r.metrics, r.reason});
797 std::array<std::size_t, 6> width{};
798 for (
const auto& row : cells)
799 for (std::size_t c = 0; c < 5; ++c) width[c] = std::max(width[c], row[c].size());
801 for (
const auto& row : cells) {
803 for (std::size_t c = 0; c < 5; ++c)
line += row[c] + std::string(width[c] - row[c].size() + 2,
' ');
805 while (!
line.empty() &&
line.back() ==
' ')
line.pop_back();
835 std::vector<std::string> out{
"accurate",
"auto",
"bound",
"default",
836 "exact",
"fast",
"heur",
"sim"};
838 out.push_back(row.method);
839 out.push_back(row.solver);
841 std::sort(out.begin(), out.end());
842 out.erase(std::unique(out.begin(), out.end()), out.end());
A subset of the registry: MATLAB's SolverFeatureSet, whose list is a flag per field.
A network plus its refreshed NetworkStruct.
The exception types the port throws.
The language-feature gate: what a MODEL uses against what a SOLVER declares.
The fluid solver's outermost entry point: @@SolverFLD/runAnalyzer.m's method resolution over solver_f...
The option and result records of SolverLDES, the discrete-event simulator.
std::vector< std::string > list_valid_methods()
Every method SolverAG serves, as the other families expose theirs.
bool auto_supports(AutoSolver s, const qn::NetworkStruct< T > &sn, const std::string &token)
Solver.supports(model) for a candidate slot, tightened by the two method-level rules a flat feature s...
AutoSolver
The Network candidate slots, in the reference's slot order (SolverAUTO.m:41-50), which is also the or...
std::string auto_method_class(const std::string &family, const std::string &method, bool is_stochastic, bool is_product_form, bool is_qbd_shape, bool has_cache=false)
What KIND of answer a method returns: "exact", "approx", "bound" or "simulation".
std::vector< std::string > auto_network_family_names()
The method families that solve a flat Network, in SolverAUTO.familyNames' own order – which is also t...
std::string auto_metric_group_of(const std::string &name)
The measure group an accessor belongs to, "" when the name belongs to none.
bool auto_solver_is_available(AutoSolver s)
True when this port has an engine behind the slot at all.
std::string auto_family_refusal(const std::string &family, const qn::NetworkStruct< T > &sn, const std::string &method)
Solver.supportsModelMethod for a family method name: may THIS model run THIS method of THIS family?
std::string auto_find_solver_table(const std::vector< SolverCandidate > &rows)
The rows as an aligned text table, the form the CLI and a console caller want.
bool auto_is_stochastic_method(const std::string &family, const std::string &method)
Solver.isStochasticMethod for a family method name: does this method return seed-dependent estimates?
std::vector< std::string > auto_family_metrics(const std::string &family)
The measure groups a method family can answer.
std::vector< SolverCandidate > auto_find_method(const qn::NetworkStruct< T > &sn, const std::string &metric=std::string(), bool show_all=false)
Alias of auto_find_solver: the same table, asked for by method.
std::vector< std::string > auto_metric_groups()
The measure groups auto_find_solver reports on, in report order.
bool auto_family_supports(const std::string &family, const qn::NetworkStruct< T > &sn, const std::string &method)
const char * auto_solver_name(AutoSolver s)
std::vector< SolverCandidate > auto_find_solver(const qn::NetworkStruct< T > &sn, const std::string &metric=std::string(), bool show_all=false)
SolverAUTO.findSolver: which solvers and solver methods can analyze this model, and for the ones that...
std::vector< SolverCandidate > auto_help(const qn::NetworkStruct< T > &sn, const std::string &metric=std::string(), bool show_all=false)
Alias of auto_find_solver: what can this model be solved with?
std::vector< std::string > auto_list_valid_methods(const qn::NetworkStruct< T > &sn)
SolverAUTO.listValidMethods: every method name this model can be asked for.
std::vector< std::string > auto_family_methods(const std::string &family, const qn::NetworkStruct< T > &sn)
The methods a family declares ON THIS MODEL, empty when it declares none.
std::vector< std::string > auto_method_alias_prefixes(const std::string &family)
The prefixes under which a family advertises a SECOND SPELLING of a method it already declares plainl...
bool auto_is_method_alias(const std::string &family, const std::string &name, const std::vector< std::string > &declared)
Is name a second spelling of another method this family declares?
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 ...
std::vector< std::string > list_valid_methods()
Port of SolverBA.listValidMethods.
std::vector< std::string > list_valid_methods()
Port of SolverCTMC.listValidMethods.
std::string solver_ctmc_mdd_supports(const NetworkStruct< T > &sn)
Can the mdd decision-diagram method be asked for this model?
std::string solver_ctmc_cftp_supports(const NetworkStruct< T > &sn)
The cftp model-class gate as a public predicate.
std::string fluid_qsys_horizon_supports(const std::string &method, const FluidOptions &opt)
The horizon rule the time-varying limits impose, as a public predicate a REPORT can ask: empty when m...
std::vector< std::string > fluid_list_valid_methods()
Port of SolverFLD.listValidMethods.
std::vector< std::string > jmt_list_valid_methods()
Port of SolverJMT.listValidMethods.
std::string jmt_method_refusal(const qn::NetworkStruct< T > &sn, const std::string &method, const JmtOptions &opt)
The structural half of SolverJMT's method gate; empty when admissible.
std::vector< std::string > list_valid_methods()
Port of SolverLDES.listValidMethods.
std::vector< std::string > list_valid_methods()
Port of SolverMAM.listValidMethods.
std::string mam_model_method_refusal(const qn::NetworkStruct< T > &L, const std::string &method)
check_model_method asked WITHOUT raising: the same verdict as a sentence.
std::vector< std::string > list_valid_methods(const qn::NetworkStruct< T > &L)
Port of SolverMVA.listValidMethods.
MemSupport solver_nc_mem_supports(const qn::NetworkStruct< T > &sn)
Port of solver_nc_mem_supports.m.
std::vector< std::string > list_valid_methods()
Port of SolverNC.listValidMethods.
bool is_stochastic_method(const std::string &method)
Port of SolverNC.isStochasticMethod.
std::string nc_method_refusal(const qn::NetworkStruct< T > &sn, const std::string &method, bool slotted=false, bool for_report=true)
May method run on this model?
FeatureSet ssa_feature_set(const std::string &)
SolverSSA.getFeatureSet, 98 MATLAB names.
FeatureSet ldes_feature_set(const std::string &)
SolverLDES.getFeatureSet, transcribed WHOLE.
FeatureSet fluid_feature_set(const std::string &method)
SolverFLD.getFeatureSet, transcribed, MINUS what the requested method cannot evaluate – the port of @...
FeatureSet jmt_feature_set(const std::string &method)
FeatureSet used_lang_features(const NetworkStruct< T > &sn)
FeatureSet nc_feature_set(const std::string &method)
SolverNC.getFeatureSet, 48 names, transcribed unchanged.
FeatureSet ba_feature_set(const std::string &method)
std::string sn_fj_supports(const NetworkStruct< T > &sn)
Can the exact fork-join construction be asked for this model?
FeatureSet qns_feature_set(const std::string &)
SolverQNS.getFeatureSet, transcribed WHOLE.
bool has_binding_capacity(const NetworkStruct< T > &sn)
getUsedLangFeatures: the features the MODEL uses.
FeatureSet ctmc_feature_set(const std::string &method)
SolverCTMC.getFeatureSet, the reference's 104 MATLAB names in full.
SupportResult feature_set_supports(const std::string &solver, const FeatureSet &declared, const FeatureSet &used)
SolverFeatureSet.supports: is every feature the model uses declared?
FeatureSet mam_feature_set(const std::string &method)
SolverMAM.getFeatureSet, the union of its four setTrue calls: 55 MATLAB names, WIDENED for 'default'/...
FeatureSet mva_feature_set(const std::string &raw_method)
FeatureSet ag_feature_set(const std::string &)
SolverAG.getFeatureSet: what the RCAT decomposition can represent.
std::string method_refusal(const qn::NetworkStruct< T > &L, const std::string &method)
SolverQNS.supportsModelMethod's structural rules, as the REASON they refuse, empty when the pair is s...
std::vector< std::string > list_valid_methods()
Port of SolverQNS.listValidMethods.
std::vector< std::string > list_valid_methods()
Port of SolverSSA.listValidMethods.
A queueing network and its refreshed NetworkStruct.
The gates and the dispatch of the agent-based (RCAT) solver.
The SolverAUTO chooser: which solver a model is handed to.
The SolverBA class surface: @@SolverBA/runAnalyzer.m, listValidMethods, getBounds and getBoundsTable.
Port of solver_ctmc_analyzer.m and the parts of @@SolverCTMC/runAnalyzer.m that surround one solve: t...
The cftp and cftp.approx methods of SolverCTMC: stationary analysis of a closed single-class product-...
The mdd method of SolverCTMC: stationary analysis of a closed single-class network whose state space ...
The DECLARED side of the gate: one feature set per solver.
Port of SolverJMT, the Java Modelling Tools client.
The SolverMAM class surface: @@SolverMAM/runAnalyzer.m and the gates around it.
The SolverMVA class surface: @@SolverMVA/runAnalyzer.m and the gates around it.
The SolverNC class surface: @@SolverNC/runAnalyzer.m and the gates around it.
Port of @@SolverQNS, the wrapper around qnsolver of the RADS/LQNS distribution.
The SolverSSA entry surface: a port of @@SolverSSA/runAnalyzer.m's method whitelist,...
One row of auto_find_solver: a (family, method) pair this model can be asked for, whether it runs,...
std::string method
the method name to pass, "mva.exact"
bool runnable
the model passes this method's own support gate
std::string metrics
the measure groups the family answers, comma-joined
std::string reason
why a refused pair was refused, "" when runnable
std::string solver
the method family: "mva", "ctmc", "ldes", ...
std::string method_class
"exact", "approx", "bound" or "simulation"
Controls, defaulting to SolverOptions('Fluid') in the reference.
The options of one JMT solve, SolverOptions('JMT') restricted to what is read.
One station of the network.
The outcome of the gate: the verdict, the offending features, the message.
std::string reason
empty when ok