5#ifndef LINE_SOLVERS_AUTO_SOLVER_AUTO_H
6#define LINE_SOLVERS_AUTO_SOLVER_AUTO_H
213 return token.empty() || token ==
"default" || token ==
"auto" || token ==
"heur" ||
214 token ==
"sim" || token ==
"exact" || token ==
"fast" ||
219 if (token.empty() || token ==
"default" || token ==
"auto" || token ==
"heur")
225 throw InputError(
"SolverAUTO: '" + token +
"' is not a selection intent");
234 if (name ==
"mam")
return "mam";
235 if (name ==
"ag")
return "ag";
236 if (name ==
"mva")
return "mva";
237 if (name ==
"nc")
return "nc";
238 if (name ==
"fluid" || name ==
"fld")
return "fld";
239 if (name ==
"jmt")
return "jmt";
240 if (name ==
"ssa")
return "ssa";
241 if (name ==
"ctmc")
return "ctmc";
242 if (name ==
"ldes" || name ==
"des")
return "ldes";
243 if (name ==
"ba")
return "ba";
244 if (name ==
"env")
return "env";
245 if (name ==
"ln")
return "ln";
246 if (name ==
"lqns" || name ==
"lqsim")
return "lqns";
247 if (name ==
"qns")
return "lqns";
248 if (name ==
"uq")
return "uq";
269 const std::string token = raw.empty() ? std::string(
"default") : raw;
275 if (token ==
"bound") {
286 const std::size_t dot = token.find(
'.');
287 const std::string head = dot == std::string::npos ? token : token.substr(0, dot);
288 const std::string rest = dot == std::string::npos ? std::string() : token.substr(dot + 1);
290 const std::string fam =
294 "SolverAUTO: '" + token +
295 "' is neither a selection intent (default, heur, exact, sim, fast, accurate, "
297 "nor a method family (mva, nc, ctmc, fld, mam, ag, ba, ssa, ldes, jmt, ln, "
298 "env, lqns, uq). A bare algorithm name must be qualified by its family here, as in "
299 "'nc.comom': resolving it needs the per-family method registry "
300 "(listValidMethods) that this port does not carry");
303 t.
submethod = rest.empty() ? std::string(
"default") : rest;
347 bool has_open =
false, has_closed =
false;
349 if (std::isinf(c.
population)) has_open =
true;
350 else has_closed =
true;
352 t.
is_open = has_open && !has_closed;
354 t.
is_mixed = has_open && has_closed;
361 for (std::size_t i = 1; i <=
sn.nstations && !t.
has_map; ++i)
362 for (std::size_t r = 1; r <=
sn.nclasses; ++r) {
363 const ProcessType p =
sn.procid(i, r);
364 if (p == ProcessType::MAP || p == ProcessType::MMPP2) {
372 bool preempt =
false, psprio =
false, hol =
false;
374 if (s.
sched == SchedStrategy::FCFSPRPRIO || s.
sched == SchedStrategy::FCFSPIPRIO ||
375 s.
sched == SchedStrategy::LCFSPRPRIO || s.
sched == SchedStrategy::LCFSPIPRIO)
377 if (s.
sched == SchedStrategy::PSPRIO || s.
sched == SchedStrategy::DPSPRIO ||
378 s.
sched == SchedStrategy::GPSPRIO)
380 if (s.
sched == SchedStrategy::HOL || s.
sched == SchedStrategy::LCFSPRIO ||
381 s.
sched == SchedStrategy::SRPTPRIO)
384 if (preempt) t.
prio =
"preempt";
385 else if (psprio) t.
prio =
"ps";
386 else if (hol) t.
prio =
"hol";
441 const std::string method = token.empty() ? std::string(
"default") : token;
466 if (!
sn.has_product_form()) {
473 if (st.
sched == SchedStrategy::OI || st.
sched == SchedStrategy::PAS) oi =
true;
474 if (!oi)
return false;
499 std::vector<AutoSolver> skipped;
500 for (std::size_t k = 0; k < order.size(); ++k) {
502 skipped.push_back(order[k]);
508 out.
method = (token ==
"default") ? std::string() : token;
523 std::vector<AutoSolver> out;
524 for (std::size_t i = 0; i <
sizeof(kSlots) /
sizeof(*kSlots); ++i)
526 out.push_back(kSlots[i]);
543inline std::vector<AutoSolver> avg_order(
const AutoTraits& t,
bool homogeneous_inf) {
551 if (t.prio ==
"preempt")
568inline UnsupportedError no_solver(
const std::string& what,
569 const std::vector<AutoSolver>& skipped) {
570 std::string msg =
"SolverAUTO: no solver supports this model" + what;
571 if (!skipped.empty()) {
572 msg +=
" (the ranking preferred ";
573 for (std::size_t i = 0; i < skipped.size(); ++i) {
577 msg +=
", which this port does not build)";
602 const std::vector<AutoSolver> exact_order =
609 const std::vector<AutoSolver> order =
610 detail::avg_order(t,
sn.has_homogeneous_scheduling(SchedStrategy::INF));
619 throw detail::no_solver(
"", pool);
633inline bool in_list(
const std::string& m,
const char*
const* tab, std::size_t n) {
634 for (std::size_t i = 0; i < n; ++i)
635 if (m == tab[i])
return true;
640inline bool is_avg_method(
const std::string& m) {
641 static const char* kAvg[] = {
642 "getAvgChainTable",
"getAvgTputTable",
"getAvgRespTTable",
"getAvgUtilTable",
643 "getAvgSysTable",
"getAvgNodeTable",
"getAvgTable",
"getAvgTableLayered",
"getAvg",
644 "getAvgChain",
"getAvgSys",
"getAvgNode",
"getAvgNodeChain",
"getAvgArvRChain",
645 "getAvgQLenChain",
"getAvgUtilChain",
"getAvgRespTChain",
"getAvgTputChain",
646 "getAvgSysRespT",
"getAvgSysTput",
"getAvgQLen",
"getAvgUtil",
"getAvgRespT",
647 "getAvgResidT",
"getAvgWaitT",
"getAvgTput",
"getAvgArvR",
"getAvgQLenTable",
648 "getAvgResidTChain",
"getAvgNodeQLenChain",
"getAvgNodeUtilChain",
649 "getAvgNodeRespTChain",
"getAvgNodeResidTChain",
"getAvgNodeTputChain",
650 "getAvgNodeArvRChain",
"getAvgNodeChainTable",
"getResults",
"hasResults",
651 "getAvgHandles",
"getTranHandles",
"getAvgQLenHandles",
"getAvgUtilHandles",
652 "getAvgRespTHandles",
"getAvgTputHandles",
"getAvgArvRHandles",
"getAvgResidTHandles",
653 "getMethodFeatureSet",
"supportsModelMethod",
"isStochasticMethod",
"libraries",
654 "showLibraryAttribution",
"citations"};
655 return in_list(m, kAvg,
sizeof(kAvg) /
sizeof(*kAvg));
659inline bool is_ensemble_method(
const std::string& m) {
660 static const char* kEns[] = {
"getEnsembleAvg",
"getEnsembleAvgTables",
"getSolver",
661 "setSolver",
"getNumberOfModels",
"getIteration",
662 "get_state",
"set_state",
"update_solver"};
663 return in_list(m, kEns,
sizeof(kEns) /
sizeof(*kEns));
666inline bool is_cdf_method(
const std::string& m) {
667 return m ==
"getCdfRespT" || m ==
"getCdfPassT" || m ==
"getPerctRespT";
670inline bool is_tran_prob_method(
const std::string& m) {
671 return m ==
"getTranProb" || m ==
"getTranProbSys" || m ==
"getTranProbAggr" ||
672 m ==
"getTranProbSysAggr";
675inline bool is_prob_method(
const std::string& m) {
676 return m ==
"getProb" || m ==
"getProbAggr" || m ==
"getProbSys" || m ==
"getProbSysAggr" ||
677 m ==
"getProbMarg" || m ==
"getProbNormConstAggr";
680inline bool is_sample_method(
const std::string& m) {
return m ==
"sample" || m ==
"sampleSys"; }
682inline bool is_sample_aggr_method(
const std::string& m) {
683 return m ==
"sampleAggr" || m ==
"sampleSysAggr";
686inline bool is_cache_metric_method(
const std::string& m) {
687 static const char* kTab[] = {
"getAvgCacheTable",
"getAvgCacheT",
"getAvgItemTable",
688 "getAvgItemT",
"cacheAvgT",
"itemAvgT",
"aCaT",
"aIT"};
689 return in_list(m, kTab,
sizeof(kTab) /
sizeof(*kTab));
692inline bool is_loss_metric_method(
const std::string& m) {
693 static const char* kTab[] = {
"getAvgLossTable",
"getAvgLossT",
"getAvgRegionLossTable",
694 "getAvgRegionLossT",
"lossAvgT",
"regionLossAvgT",
696 return in_list(m, kTab,
sizeof(kTab) /
sizeof(*kTab));
699inline bool is_orbit_metric_method(
const std::string& m) {
700 static const char* kTab[] = {
"getAvgOrbitTable",
"getAvgOrbitT",
"getAvgOrbit",
"orbitAvgT",
702 return in_list(m, kTab,
sizeof(kTab) /
sizeof(*kTab));
705inline bool is_moment_method(
const std::string& m) {
706 static const char* kTab[] = {
"getMomentTable",
"getMomentChainTable",
"getMomentStationTable",
707 "getMomentT",
"getMomentChainT",
"getMomentStationT",
708 "momentT",
"momentChainT",
"momentStationT",
710 return in_list(m, kTab,
sizeof(kTab) /
sizeof(*kTab));
713inline bool is_sens_method(
const std::string& m) {
714 static const char* kTab[] = {
"getSensitivityTable",
"getSensitivityT",
"sensitivityT",
"sT",
715 "supportsExactSensitivity"};
716 return in_list(m, kTab,
sizeof(kTab) /
sizeof(*kTab));
731 if (detail::is_ensemble_method(method))
733 "' is only available for LayeredNetwork models");
735 std::vector<AutoSolver> order;
736 if (method ==
"getTranAvg") {
738 }
else if (detail::is_cdf_method(method)) {
741 if (
sn.has_homogeneous_scheduling(SchedStrategy::FCFS) &&
sn.has_product_form())
745 }
else if (method ==
"getTranCdfPassT" || method ==
"getTranCdfRespT") {
747 }
else if (detail::is_tran_prob_method(method)) {
749 }
else if (detail::is_sample_method(method)) {
751 }
else if (detail::is_sample_aggr_method(method)) {
753 }
else if (detail::is_prob_method(method)) {
754 if (
sn.has_product_form())
758 }
else if (detail::is_cache_metric_method(method)) {
761 }
else if (detail::is_loss_metric_method(method)) {
763 }
else if (detail::is_orbit_metric_method(method)) {
765 }
else if (detail::is_moment_method(method)) {
767 }
else if (detail::is_sens_method(method)) {
784 std::vector<AutoSolver> order;
785 if (detail::is_tran_prob_method(method) || detail::is_cdf_method(method) ||
786 method ==
"getTranCdfPassT" || method ==
"getTranCdfRespT" || method ==
"getTranAvg") {
788 }
else if (detail::is_sample_method(method) || detail::is_sample_aggr_method(method)) {
791 }
else if (detail::is_prob_method(method)) {
801 bool any_open =
false;
803 if (std::isinf(c.
population)) any_open =
true;
805 order.erase(std::remove(order.begin(), order.end(),
AutoSolver::CTMC), order.end());
811 throw detail::no_solver(
" exactly for method '" + method +
812 "'; use the default heuristic for the approximation",
819 const std::vector<AutoSolver> order =
820 detail::is_sample_method(method)
825 throw detail::no_solver(
" by simulation", order);
843 sn, std::string(), out))
850 sn, std::string(), out))
871 const std::string& method,
AutoMode mode) {
873 std::vector<AutoSolver> out(1, chosen.
solver);
875 for (std::size_t i = 0; i < cand.size(); ++i)
876 if (std::find(out.begin(), out.end(), cand[i]) == out.end()) out.push_back(cand[i]);
891inline bool layered_ranked(
const std::vector<AutoLayered>& order,
AutoLayeredChoice& out) {
892 std::vector<AutoLayered> skipped;
893 for (std::size_t k = 0; k < order.size(); ++k) {
895 skipped.push_back(order[k]);
919 std::vector<AutoLayered> order;
926 }
else if (method ==
"getTranAvg" || detail::is_cdf_method(method) ||
927 method ==
"getTranCdfPassT" || method ==
"getTranCdfRespT") {
929 }
else if (detail::is_sample_method(method) || detail::is_sample_aggr_method(method)) {
931 }
else if (detail::is_prob_method(method) || detail::is_tran_prob_method(method)) {
933 }
else if (detail::is_ensemble_method(method)) {
936 }
else if (has_cache_task) {
949 for (std::size_t i = 0; i < order.size(); ++i)
953 if (detail::layered_ranked(order, out))
return out;
955 "SolverAUTO: no LayeredNetwork solver in this port serves method '" + method +
956 "'; the reference's ranking is served by SolverLQNS, which shells out to an external "
957 "binary this port does not wrap");
982 std::vector<AutoEnv> order;
988 std::vector<AutoEnv> skipped;
989 for (std::size_t k = 0; k < order.size(); ++k) {
991 skipped.push_back(order[k]);
996 out.
retry.push_back(order[k]);
998 for (std::size_t j = 0; j < 3; ++j)
1000 out.
retry.push_back(slots[j]);
1004 "SolverAUTO: no Environment stage solver in this port serves method '" + method +
"'");
UnsupportedError(const std::string &what)
A subset of the registry: MATLAB's SolverFeatureSet, whose list is a flag per field.
A network plus its refreshed NetworkStruct.
Worst-case log-size of the CTMC state space induced by a NetworkStruct.
The exception types the port throws.
Where the LDES engine is, and whether this machine can run it.
Is a usable lqns installed on this machine?
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...
bool auto_layered_is_available(AutoLayered s)
Every LN layer engine the ranking names is one --layer-solver takes (mva, nc, fluid,...
AutoEnvChoice auto_choose_env_solver(const std::string &method, AutoMode mode=AutoMode::HEUR)
The Environment arm of chooseSolverHeur / chooseSolverExact / chooseSolverSim.
AutoSolver
The Network candidate slots, in the reference's slot order (SolverAUTO.m:41-50), which is also the or...
bool auto_ctmc_is_tractable(const qn::NetworkStruct< T > &sn, double cutoff=-1.0)
double auto_ctmc_state_space_logsize(const qn::NetworkStruct< T > &sn, double cutoff=-1.0)
Worst-case log-size of the CTMC state space induced by sn: the SAME estimator SolverCTMC's memory gat...
AutoChoice auto_choose_solver_exact(const qn::NetworkStruct< T > &sn, const std::string &method)
chooseSolverExact: the ranking restricted to solvers that answer exactly.
AutoSolver auto_choose_solver(const qn::NetworkStruct< T > &sn, const std::string &method)
The heuristic Network arm, by getter name.
std::string auto_family_alias(const std::string &name)
SolverAUTO.familyAlias: the canonical family of a method name, or "" when the method name names none.
AutoTraits auto_traits(const qn::NetworkStruct< T > &sn)
bool auto_solver_is_available(AutoSolver s)
True when this port has an engine behind the slot at all.
bool auto_is_selection_intent(const std::string &token)
SolverAUTO.selectionIntents, less 'bound'.
const double kAutoExactPopulationMax
Population at or below which an exact solver is preferred (EXACT_POPULATION_MAX).
AutoSolver auto_choose_avg_solver(const qn::NetworkStruct< T > &sn)
AutoChoice auto_choose_avg_solver_ex(const qn::NetworkStruct< T > &sn)
chooseAvgSolverHeur, the Network arm: exact first at small populations, then the trait-keyed ranking,...
AutoLayeredChoice auto_choose_layered_solver(const std::string &method, bool has_cache_task, AutoMode mode=AutoMode::HEUR)
chooseLayeredSolver plus the LayeredNetwork arm of chooseAvgSolverHeur.
AutoMode auto_mode_of_token(const std::string &token)
const char * auto_env_stage_solver(AutoEnv s)
The EnvOptions::stage_solver (and --stage-solver) name of a candidate ensemble.
const double kAutoCtmcStateCap
The cap reachable_space_generator enforces (solver_ctmc.h, maxst).
AutoChoice auto_choose_solver_sim(const qn::NetworkStruct< T > &sn, const std::string &method)
chooseSolverSim: the ranking restricted to simulators.
bool auto_env_is_available(AutoEnv)
All three candidate ensembles are built here (EnvOptions::stage_solver = fluid, mva,...
const char * auto_solver_name(AutoSolver s)
AutoToken auto_resolve_token(const std::string &raw, const qn::NetworkStruct< T > &sn)
resolveMethodToken with its third arm: an intent, a family[.submethod], or else a BARE method name,...
std::vector< AutoSolver > auto_candidates(const qn::NetworkStruct< T > &sn)
The candidate pool in slot order, filtered by supports: what the delegate retries through after the c...
std::vector< AutoSolver > auto_proposed_solvers(const qn::NetworkStruct< T > &sn, const std::string &method, AutoMode mode)
delegate's proposed order: the chosen solver, then every feasible candidate in slot order.
AutoChoice auto_choose_solver_mode(const qn::NetworkStruct< T > &sn, const std::string &method, AutoMode mode)
chooseSolver: the selection mode picks the ranking, and every mode but the two learned ones keeps the...
const char * auto_env_name(AutoEnv s)
AutoChoice auto_choose_solver_heur(const qn::NetworkStruct< T > &sn, const std::string &method)
chooseNetworkSolver in chooseSolverHeur.m: the getter names the metric family, the family names a ran...
bool auto_ranked(const std::vector< AutoSolver > &order, const qn::NetworkStruct< T > &sn, const std::string &token, AutoChoice &out)
chooseSolverRanked: the first slot in ORDER that exists and accepts the model.
AutoEnv
The Environment candidate slots (SolverAUTO.m:58-60).
AutoLayered
The LayeredNetwork candidate slots (SolverAUTO.m:52-56).
AutoMode
The selection intents of SolverAUTO.selectionIntents, less 'bound'.
const char * auto_layered_name(AutoLayered s)
The layered names are the CLI's own tokens, because that is what the choice is spent on: ln....
SchedStrategy
Scheduling disciplines, with the values of MATLAB SchedStrategy.
ProcessType
Distribution kinds, with the values of MATLAB ProcessType.
NodeType
Node kinds, with the values of MATLAB NodeType.
bool ldes_is_available()
True when this machine can run the engine at all, by either image.
bool lqns_is_available()
True when lqns is installed AND is a release this port speaks.
double ctmc_state_space_logsize(const qn::NetworkStruct< T > &sn, const CtmcSizeOptions &opt=CtmcSizeOptions())
Worst-case log state-space size of sn.
FeatureSet ldes_feature_set(const std::string &)
SolverLDES.getFeatureSet, transcribed WHOLE.
FeatureSet fluid_feature_set(const std::string &method)
FeatureSet ssa_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 fluid_class_feature_set()
SolverFLD.getFeatureSet, transcribed, MINUS what the requested method cannot evaluate – the port of @...
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)
SolverMVA.getFeatureSet, plus getMethodFeatureSet's per-method deltas.
Conservation laws of a layered queueing network, enumerated from its structure.
A queueing network and its refreshed NetworkStruct.
The DECLARED side of the gate: one feature set per solver.
The <kind> part of a LINE:<category>:<kind> identifier (see _kb/17-error-vocabulary....
What a ranking resolved to, and what it had to skip to get there.
std::string method
The method the choice was gated on: "" for the default, "exact".
std::vector< AutoSolver > skipped
Slots that outranked solver and have no engine in this port.
std::vector< AutoEnv > retry
delegate.m's proposed list: the chosen ensemble, then every candidate in slot order (MVA,...
std::vector< AutoEnv > skipped
std::vector< AutoLayered > skipped
resolveMethodToken, minus the unqualified-algorithm-name arm.
std::string submethod
the method handed to the family, "default" when bare
std::string family
empty when is_intent
The structural traits the rankings are keyed on.
std::string prio
"none", "preempt", "ps" or "hol", in the reference's own precedence.
Options the estimator reads; only the cutoff matters.
double cutoff
< 0 or non-finite = not given, take the solver default
One job class of the network.
double population
infinite for an open class
One station of the network.