5#ifndef LINE_SOLVERS_AUTO_AUTO_METHODS_H
6#define LINE_SOLVERS_AUTO_AUTO_METHODS_H
79#include <initializer_list>
122 return {
"mva",
"nc",
"ctmc",
"fld",
"mam",
"ag",
"ba",
"ssa",
"ldes",
"jmt",
"lqns"};
138 if (family ==
"mva")
return {
"amva."};
150 const std::vector<std::string>& declared) {
152 if (name.size() > prefix.size() && name.compare(0, prefix.size(), prefix) == 0 &&
153 std::find(declared.begin(), declared.end(), name.substr(prefix.size())) !=
163inline bool auto_slot_of_family(
const std::string& family,
AutoSolver& out) {
191bool capacity_admits(
const std::string& family,
const qn::NetworkStruct<T>& sn,
192 const std::string& method) {
193 if (family !=
"mva" && family !=
"nc" && family !=
"fld" && family !=
"lqns")
return true;
195 if (family ==
"nc" && method ==
"mem") {
197 return ms.supported && ms.blocking;
210inline bool declared_feature_set(
const std::string& family,
const std::string& method,
211 qn::FeatureSet& declared, std::string& label) {
212 if (family ==
"mva") { declared =
qn::mva_feature_set(method); label =
"SolverMVA";
return true; }
213 if (family ==
"nc") { declared =
qn::nc_feature_set(method); label =
"SolverNC";
return true; }
214 if (family ==
"mam") { declared =
qn::mam_feature_set(method); label =
"SolverMAM";
return true; }
216 if (family ==
"ssa") { declared =
qn::ssa_feature_set(method); label =
"SolverSSA";
return true; }
217 if (family ==
"ctmc") { declared =
qn::ctmc_feature_set(method); label =
"SolverCTMC";
return true; }
218 if (family ==
"ldes") { declared =
qn::ldes_feature_set(method); label =
"SolverLDES";
return true; }
219 if (family ==
"ag") { declared =
qn::ag_feature_set(method); label =
"SolverAG";
return true; }
220 if (family ==
"ba") { declared =
qn::ba_feature_set(method); label =
"SolverBA";
return true; }
221 if (family ==
"jmt") { declared =
qn::jmt_feature_set(method); label =
"SolverJMT";
return true; }
222 if (family ==
"lqns") {
224 label =
"SolverLQNS";
253 return std::vector<std::string>();
268 const std::string& method) {
273 if (family ==
"nc" && (method ==
"cftp" || method ==
"cftp.approx")) {
275 if (!why.empty())
return why;
277 if (!detail::capacity_admits(family,
sn, method))
278 return "SolverAUTO: this method ignores the finite station capacity this model sets "
279 "(setCapacity / classCap), so it would report a capped station as unbounded.";
283 if (!detail::declared_feature_set(family, method, declared, label))
284 return "SolverAUTO: the '" + family +
"' method family does not solve a flat Network.";
295 if (family ==
"nc") {
297 if (!ncr.empty())
return ncr;
309 if (family ==
"jmt") {
311 if (!why.empty())
return why;
313 if (family ==
"mam") {
315 if (!why.empty())
return why;
321 if (family ==
"ag") {
322 const std::string why = ag::runner_detail::method_refusal(
sn, method);
323 if (!why.empty())
return why;
328 if (family ==
"lqns") {
330 if (!why.empty())
return why;
340 if (family ==
"ba") {
342 if (!why.empty())
return "SolverBA: " + why;
344 if (family ==
"ctmc") {
347 const std::string why =
349 if (!why.empty())
return why;
358 if (family ==
"ctmc" || family ==
"ssa") {
360 if (!why.empty())
return why;
368 if (family ==
"ssa" && method ==
"nrm") {
369 const std::string why = ssa::detail::ssa_nrm_supports(
sn);
370 if (!why.empty())
return why;
372 if (family ==
"fld") {
377 if (!why.empty())
return why;
381 const std::string
fj = fluid::detail::fluid_forkjoin_supports(
sn, method);
382 if (!
fj.empty())
return fj;
390 if (detail::auto_slot_of_family(family, slot)) {
393 ": no engine for this solver is available in this build.";
397 ": 'exact' needs a product-form model, and this one has no product-form "
400 return "SolverCTMC: the state space of this model is too large to enumerate.";
401 return label +
": this solver refuses the model through its own structural check.";
409 const std::string& method) {
445 return {
"avg",
"tran",
"cdf",
"prob",
"tranprob",
"sample",
446 "cache",
"loss",
"orbit",
"moment",
"sens"};
451inline bool name_in(
const std::string& n, std::initializer_list<const char*> names) {
452 for (
const char* c : names)
453 if (n == c)
return true;
457inline bool starts_with(
const std::string& s,
const std::string& p) {
458 return s.size() >= p.size() && s.compare(0, p.size(), p) == 0;
476 if (name.empty())
return "";
478 if (g == name)
return g;
479 if (name ==
"any" || name ==
"all")
return "";
480 if (detail::name_in(name, {
"getTranAvg",
"getTranAvgVar",
"tranAvg"}))
return "tran";
481 if (detail::name_in(name, {
"getCdfRespT",
"getCdfPassT",
"getPerctRespT",
"getTranCdfPassT",
482 "getTranCdfRespT",
"getCdfSysRespT"}))
484 if (detail::name_in(name, {
"getTranProb",
"getTranProbSys",
"getTranProbAggr",
485 "getTranProbSysAggr"}))
487 if (detail::name_in(name, {
"getProb",
"getProbAggr",
"getProbSys",
"getProbSysAggr",
488 "getProbMarg",
"getProbNormConstAggr"}))
490 if (detail::name_in(name, {
"sample",
"sampleSys",
"sampleAggr",
"sampleSysAggr"}))
492 if (detail::name_in(name, {
"getAvgCacheTable",
"getAvgCacheT",
"getAvgItemTable",
493 "getAvgItemT",
"cacheAvgT",
"itemAvgT",
"aCaT",
"aIT"}))
495 if (detail::name_in(name, {
"getAvgLossTable",
"getAvgLossT",
"getAvgRegionLossTable",
496 "getAvgRegionLossT",
"lossAvgT",
"regionLossAvgT",
"aLT",
"aRLT"}))
498 if (detail::name_in(name, {
"getAvgOrbitTable",
"getAvgOrbitT",
"getAvgOrbit",
"orbitAvgT",
501 if (detail::name_in(name, {
"getMomentTable",
"getMomentChainTable",
"getMomentStationTable",
502 "getMomentT",
"getMomentChainT",
"getMomentStationT",
"momentT",
503 "momentChainT",
"momentStationT",
"mT",
"mCT",
"mST"}))
505 if (detail::name_in(name, {
"getSensitivityTable",
"getSensitivityT",
"sensitivityT",
"sT",
506 "getSensitivity",
"getSensitivityRanking"}))
510 if (detail::starts_with(name,
"getAvg") || detail::starts_with(name,
"avg") ||
511 detail::name_in(name, {
"getAvgSysRespT",
"getAvgSysTput",
"aT",
"aNT",
"aCT",
"aST",
538 if (family ==
"mva")
return {
"avg",
"prob",
"cache",
"orbit",
"moment",
"sens"};
539 if (family ==
"nc")
return {
"avg",
"cdf",
"prob",
"cache",
"moment",
"sens"};
540 if (family ==
"ctmc")
541 return {
"avg",
"tran",
"cdf",
"prob",
"tranprob",
"sample",
"cache",
"loss",
"orbit",
543 if (family ==
"fld")
return {
"avg",
"tran",
"cdf",
"prob",
"cache",
"sens"};
544 if (family ==
"mam")
return {
"avg",
"cdf"};
547 if (family ==
"ag")
return {
"avg"};
549 if (family ==
"ba")
return {
"avg"};
550 if (family ==
"ssa")
return {
"avg",
"cdf",
"prob",
"sample",
"loss"};
551 if (family ==
"ldes")
552 return {
"avg",
"tran",
"cdf",
"prob",
"sample",
"cache",
"loss",
"orbit"};
553 if (family ==
"jmt")
return {
"avg",
"tran",
"cdf",
"prob",
"tranprob",
"sample"};
568 if (family ==
"ssa" || family ==
"ldes")
return true;
570 if (family ==
"jmt") {
572 std::vector<std::string> toks;
573 for (
char ch : method) {
574 if (ch ==
'.' || ch ==
'/') {
578 tok +=
static_cast<char>(std::tolower(
static_cast<unsigned char>(ch)));
583 for (
const std::string& t : toks)
584 if (t ==
"jmva") jmva =
true;
585 if (!jmva)
return true;
586 for (
const std::string& t : toks)
587 if (t ==
"ls" || t ==
"mci" || t ==
"imci" || t ==
"sampling")
return true;
594inline std::string exact_if(
bool cond) {
return cond ?
"exact" :
"approx"; }
625 bool is_stochastic,
bool is_product_form,
626 bool is_qbd_shape,
bool has_cache =
false) {
629 if (family ==
"ba")
return "bound";
630 if (is_stochastic)
return "simulation";
631 if (family ==
"ctmc") {
635 if (method ==
"mdd")
return detail::exact_if(is_product_form);
638 if (family ==
"nc") {
645 if (detail::name_in(method, {
"exact",
"divdiff",
"ca",
"comom",
"comomld",
"rec",
"ms",
646 "cub",
"rgf",
"cftp"}))
647 return detail::exact_if(is_product_form && !has_cache);
650 if (family ==
"mva") {
653 if (detail::name_in(method, {
"exact",
"mva"}))
654 return detail::exact_if(is_product_form && !has_cache);
657 if (family ==
"jmt") {
660 if (detail::name_in(method, {
"jmva.mva",
"jmva.recal",
"jmva.comom",
"jmva.treeconv"}))
661 return detail::exact_if(is_product_form && !has_cache);
664 if (family ==
"mam") {
668 if (detail::name_in(method, {
"default",
"mna",
"ldqbd",
"bgchain",
"retrial"}))
669 return detail::exact_if(is_qbd_shape);
672 if (family ==
"ag") {
703 const std::string& metric = std::string(),
704 bool show_all =
false) {
706 if (group.empty() && !metric.empty() && metric !=
"any" && metric !=
"all") {
709 if (!groups.empty()) groups +=
", ";
716 throw InputError(
"'" + metric +
"' names no measure. Pass a group (" + groups +
717 ") or the accessor that returns it, e.g. 'getCdfRespT'.");
722 const bool is_product_form =
sn.has_product_form();
723 std::size_t nsources = 0;
726 const std::vector<double> njobs =
sn.njobs();
727 bool all_open = !njobs.empty();
728 for (
double n : njobs)
729 if (!std::isinf(n)) all_open =
false;
730 const bool is_qbd_shape = all_open && (
sn.nstations - nsources) == 1;
731 bool has_cache =
false;
733 if (nd.
nodetype == qn::NodeType::Cache) has_cache =
true;
735 std::vector<SolverCandidate> rows;
738 if (!group.empty() &&
739 std::find(groups.begin(), groups.end(), group) == groups.end())
741 std::string metric_list;
742 for (
const std::string& g : groups) {
743 if (!metric_list.empty()) metric_list +=
",";
747 for (
const std::string& name : declared) {
748 if (detail::starts_with(name, family +
".")) {
765 const bool ok = reason.empty();
766 if (!ok && !show_all)
continue;
769 row.method = family +
"." + name;
773 is_qbd_shape, has_cache);
774 row.metrics = metric_list;
775 row.reason = ok ? std::string() : reason;
785 const std::string& metric = std::string(),
786 bool show_all =
false) {
793 const std::string& metric = std::string(),
794 bool show_all =
false) {
804 if (rows.empty())
return "No solver method can analyze this model.\n";
805 std::vector<std::array<std::string, 6>> cells;
806 cells.push_back({
"Solver",
"Method",
"Runnable",
"Class",
"Metrics",
"Reason"});
808 cells.push_back({r.solver, r.method, r.runnable ?
"true" :
"false", r.method_class,
809 r.metrics, r.reason});
810 std::array<std::size_t, 6> width{};
811 for (
const auto& row : cells)
812 for (std::size_t c = 0; c < 5; ++c) width[c] = std::max(width[c], row[c].size());
814 for (
const auto& row : cells) {
816 for (std::size_t c = 0; c < 5; ++c)
line += row[c] + std::string(width[c] - row[c].size() + 2,
' ');
818 while (!
line.empty() &&
line.back() ==
' ')
line.pop_back();
848 std::vector<std::string> out{
"accurate",
"auto",
"bound",
"default",
849 "exact",
"fast",
"heur",
"sim"};
851 out.push_back(row.method);
852 out.push_back(row.solver);
854 std::sort(out.begin(), out.end());
855 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.
The flat-Network path of @@SolverLQNS: its qns methods, served by qnsolver of the RADS/LQNS distribut...
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 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 > qns_methods()
Port of SolverLQNS.listValidMethods on a flat Network.
std::string qns_method_refusal(const qn::NetworkStruct< T > &L, const std::string &method)
SolverLQNS.supportsModelMethod's structural rules on a Network, as the REASON they refuse,...
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?
std::string solver_nc_cftp_supports(const qn::NetworkStruct< T > &sn)
The cftp model-class gate as a public predicate.
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 lqns_network_feature_set(const std::string &)
SolverLQNS.getFeatureSet on a flat Network (its qns methods), transcribed WHOLE.
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?
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::vector< std::string > list_valid_methods()
Port of SolverSSA.listValidMethods.
Conservation laws of a layered queueing network, enumerated from its structure.
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 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.
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