5#ifndef LINE_SOLVERS_MVA_SOLVER_MVA_RUNNER_H
6#define LINE_SOLVERS_MVA_SOLVER_MVA_RUNNER_H
141 std::vector<std::string> m{
"default",
"mva",
"exact",
"amva",
142 "sum",
"esum",
"qdlin",
"amva.qdlin",
143 "bs",
"amva.bs",
"sqni",
"qd",
144 "amva.qd",
"qli",
"amva.qli",
"fli",
145 "amva.fli",
"ab",
"amva.ab",
"schmidt",
146 "amva.schmidt",
"schmidt-ext",
"amva.schmidt-ext",
148 "lcp",
"amva.lcp",
"chow",
"amva.chow",
149 "pamb",
"amva.pamb",
"pami",
"amva.pami",
150 "pamt",
"amva.pamt",
"clust",
"amva.clust",
151 "dmlin",
"amva.dmlin"};
158 m.push_back(
"amva.aql");
160 m.push_back(
"amva.qsa");
162 m.push_back(
"amva.tay");
165 bool open =
true, closed =
false;
178 bool has_bas =
false;
182 if (L.
nclasses == 1 && !open && has_bas) m.push_back(
"sqd");
188 bool any_open_class =
false;
190 if (std::isinf(c.
population)) any_open_class =
true;
191 if (!any_open_class) m.push_back(
"mvac");
200 m.push_back(
"sjn.mva");
201 m.push_back(
"sjn.amva");
208 m.insert(m.begin() + 4,
"rqt");
209 m.insert(m.begin() + 4,
"rqna");
210 m.insert(m.begin() + 4,
"qna");
217 bool classdep_routing =
false;
218 for (std::size_t r = 0; r + 1 < L.
nclasses; ++r)
219 for (std::size_t i = 1; i <= L.
nof_nodes() && !classdep_routing; ++i)
220 for (std::size_t j = 1; j <= L.
nof_nodes(); ++j)
222 classdep_routing =
true;
225 if (!classdep_routing) {
226 m.push_back(
"marie");
227 m.push_back(
"amva.marie");
234 if (st.
sched == qn::SchedStrategy::FCFSPRPRIO) {
235 m.push_back(
"priomva");
236 m.push_back(
"amva.priomva");
243 m.push_back(
"amva.mapqn");
247 m.push_back(
"egflin");
248 m.push_back(
"gflin");
249 m.push_back(
"amva.lin");
254 const char*
qsys[] = {
"mm1",
"mmk",
"mg1",
255 "mgi1",
"gm1",
"gig1",
256 "gim1",
"gig1.kingman",
"gigk",
257 "gigk.kingman_approx",
"gig1.gelenbe",
258 "gig1.heyman",
"gig1.kimura",
"gig1.allen",
259 "gig1.kobayashi",
"gig1.klb",
"gig1.marchal",
260 "gigk.whitt",
"qed",
"gig1.extremal",
"gigk.diffusion"};
261 for (
const char* q :
qsys) m.push_back(q);
265 const std::size_t qi = detail::station_of_type(L, qn::NodeType::Queue);
267 m.push_back(
"erlanga");
268 m.push_back(
"mgisrgi");
279 std::size_t n_inf = 0;
281 if (st.
sched == qn::SchedStrategy::INF) ++n_inf;
286 std::vector<double> sx_n;
287 std::vector<bool> sx_fcfs;
288 bool sx_ready =
false;
289 std::vector<std::string> keep;
290 keep.reserve(m.size());
291 for (
const std::string& name : m) {
299 for (std::size_t c = 0; c < L.
nchains; ++c) sx_n.push_back(cd.
Nchain[c]);
301 if (st.
sched != qn::SchedStrategy::INF && st.
sched != qn::SchedStrategy::EXT)
302 sx_fcfs.push_back(st.
sched == qn::SchedStrategy::FCFS);
310 keep.push_back(name);
323 if (std::find(valid.begin(), valid.end(), method) != valid.end())
return;
324 throw UnsupportedError(
"SolverMVA: the '" + method +
"' method is unsupported by this solver");
339 if (method !=
"default" || L.
nclasses != 1)
return method;
342 for (std::size_t r = 0; r < L.
nclasses; ++r)
343 if (std::isfinite(L.
classes[r].population))
return method;
350 const std::size_t qi = detail::station_of_type(L, qn::NodeType::Queue);
372 for (std::size_t c = 0; c < L.
nchains; ++c)
373 for (std::size_t i = 0; i < L.
nstations; ++i) {
375 for (std::size_t k = 0; k < L.
nclasses; ++k)
376 V(i, k) = T(V(i, k) + L.
visits[c](sf - 1, k));
396 const Matrix<T> V = detail::station_visits(L);
398 for (std::size_t i = 0; i < M; ++i)
399 for (std::size_t k = 0; k < K; ++k) {
408 const bool is_place = L.
stations[i].nodetype == qn::NodeType::Place;
409 if ((L.
disabled[i][k] && !is_place) || !(RN(i, k) > zero))
continue;
415 for (std::size_t cc = 0; cc < L.
nchains; ++cc)
416 if (L.
chains[cc][k]) c = cc;
418 const std::size_t rs = L.
classes[k].refstat;
423 for (std::size_t r : L.
inchain[c]) den += V(rs - 1, r - 1);
425 if (den > zero) WN(i, k) = T(RN(i, k) * V(i, k) / den);
427 for (std::size_t i = 0; i < M; ++i)
428 for (std::size_t k = 0; k < K; ++k)
470 for (std::size_t nd = 0; nd < L.
nodes.size(); ++nd) {
471 if (L.
nodes[nd].nodetype != qn::NodeType::Join)
continue;
472 const std::size_t ist = L.
nodes[nd].station;
473 if (ist == 0 || ist > RN.
rows())
continue;
474 for (std::size_t r = 0; r < RN.
cols(); ++r)
475 if (AN(ist - 1, r) > zero) RN(ist - 1, r) = QN(ist - 1, r) / AN(ist - 1, r);
485 if (L.
rt.rows() != S * R)
return AN;
490 for (std::size_t sf = 0; sf < S; ++sf) {
492 const std::size_t ist = L.
nodes[nd - 1].station;
493 if (ist == 0)
continue;
494 for (std::size_t r = 0; r < R; ++r) TS(sf, r) = TN(ist - 1, r);
496 for (
int pass = 0; pass < 2; ++pass) {
497 for (std::size_t sf = 0; sf < S; ++sf) {
499 if (L.
nodes[nd - 1].station != 0)
continue;
500 const bool is_cache = L.
nodes[nd - 1].nodetype == qn::NodeType::Cache;
501 if (is_cache != (pass == 0))
continue;
502 for (std::size_t k = 0; k < R; ++k) {
504 for (std::size_t sf2 = 0; sf2 < S; ++sf2)
505 for (std::size_t r = 0; r < R; ++r)
506 acc += T(TS(sf2, r) * L.
rt(sf2 * R + r, sf * R + k));
511 for (std::size_t ist = 0; ist < M; ++ist) {
513 for (std::size_t k = 0; k < R; ++k) {
515 for (std::size_t sf2 = 0; sf2 < S; ++sf2)
516 for (std::size_t r = 0; r < R; ++r)
517 acc += T(TS(sf2, r) * L.
rt(sf2 * R + r, sf_i * R + k));
537enum class MetricKind { QLen, Util, RespT, ResidT, Tput, ArvR };
548 const std::vector<std::vector<bool>>* zero_mask) {
563 bool has_fj =
false, has_cache =
false, has_spn =
false;
565 if (nd.
nodetype == qn::NodeType::Fork || nd.
nodetype == qn::NodeType::Join) has_fj =
true;
566 if (nd.
nodetype == qn::NodeType::Cache) has_cache =
true;
567 if (nd.
nodetype == qn::NodeType::Transition) has_spn =
true;
573 std::vector<bool> is_cache_class(K,
false);
576 for (std::size_t r = 0; r < cp.
hitclass.size(); ++r) {
579 is_cache_class[cp.
missclass[r] - 1] =
true;
584 for (std::size_t i = 0; i < M; ++i) {
586 const bool src_or_sink = (nt == qn::NodeType::Source || nt == qn::NodeType::Sink);
587 bool kind_disabled =
false;
592 kind_disabled = src_or_sink;
595 kind_disabled = src_or_sink || nt == qn::NodeType::Fork || nt == qn::NodeType::Join;
599 kind_disabled =
false;
602 if (kind_disabled)
continue;
625 bool service_tunnel = (nt == qn::NodeType::Source || nt == qn::NodeType::Fork ||
626 nt == qn::NodeType::Join);
627 if (nt == qn::NodeType::Place) {
628 service_tunnel =
true;
629 for (std::size_t k = 0; k < K && k < L.
service[i].size(); ++k)
630 if (!L.
service[i][k].disabled) { service_tunnel =
false;
break; }
632 for (std::size_t k = 0; k < K; ++k) {
633 if (!L.
disabled[i][k] || service_tunnel) {
634 out(i, k) = metric(i, k);
635 }
else if (is_cache_class[k] &&
640 out(i, k) = metric(i, k);
645 for (std::size_t i = 0; i < M; ++i)
646 for (std::size_t k = 0; k < K; ++k)
647 if ((*zero_mask)[i][k]) out(i, k) = zero;
648 for (std::size_t i = 0; i < M; ++i)
649 for (std::size_t k = 0; k < K; ++k)
652 for (std::size_t k = 0; k < K; ++k) {
654 for (std::size_t cc = 0; cc < L.
nchains; ++cc)
655 if (L.
chains[cc][k]) c = cc;
657 for (std::size_t i = 0; i < M; ++i) {
659 if ((has_fj || has_cache || has_spn) &&
721 "SolverMVA: this model uses a Finite Capacity Region (addRegion), which is not "
722 "supported by SolverMVA. Use SolverJMT, or setCapacity for a single-station limit.");
747 std::string actualmethod, warning;
751 std::shared_ptr<qn::NetworkStruct<T>> refreshed;
762 std::vector<T> lam(
tr.V.classes.size() + 1,
790 std::vector<std::vector<bool>> mask(M, std::vector<bool>(K,
false));
791 for (std::size_t i = 0; i < M; ++i)
792 for (std::size_t k = 0; k < K; ++k)
798 std::vector<std::vector<bool>> srcmask(M, std::vector<bool>(K,
false));
799 for (std::size_t i = 0; i < M; ++i)
800 if (L.
stations[i].nodetype == qn::NodeType::Source)
801 for (std::size_t k = 0; k < K; ++k) srcmask[i][k] =
true;
828 const double dinf = std::numeric_limits<double>::infinity();
829 for (std::size_t i = 0; i < M; ++i) {
833 if (!std::isfinite(c) || c <= 0.0)
continue;
834 if (nt == qn::NodeType::Source || nt == qn::NodeType::Sink)
continue;
835 if (sch == qn::SchedStrategy::INF || sch == qn::SchedStrategy::EXT)
continue;
836 std::vector<double> rho(K, 0.0);
837 double rho_open = 0.0, rho_tot = 0.0;
838 for (std::size_t r = 0; r < K; ++r) {
841 if (rate > 0.0 && tp > 0.0) rho[r] = tp / (c * rate);
843 if (std::isinf(L.
classes[r].population)) rho_open += rho[r];
845 if (rho_open < 1.0 || rho_tot <= 0.0)
continue;
846 for (std::size_t r = 0; r < K; ++r) {
848 if (std::isinf(L.
classes[r].population) && rho[r] > 0.0) {
866 if (!warning.empty()) warning +=
" ";
868 "SolverMVA does not handle immediate feedback (immfeed); the solver will treat "
869 "self-loops as class-switching with re-queueing.";
What a solver observed about the Cache nodes of a model.
UnsupportedError(const std::string &what)
A network plus its refreshed NetworkStruct.
bool has_immediate_feedback() const
any(sn.immfeed(:)): whether any (station, class) pair feeds back.
std::size_t stateful_of_station(std::size_t st) const
std::size_t nof_nodes() const
std::size_t nof_stateful() const
std::vector< std::size_t > stateful_nodes
1-based node indices, ascending
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< std::vector< bool > > chains
(nchains x nclasses)
std::vector< std::size_t > refclass
(nchains) 1-based class, 0 = none
std::vector< std::vector< bool > > disabled
Matrix< T > rt
sn.rt and sn.rtnodes: the class-expanded routing.
std::map< std::size_t, CacheParam< T > > nodeparam
Cache parameters by 1-based NODE index; only Cache nodes have an entry.
std::vector< JobClass > classes
std::vector< Station< T > > stations
stations[k-1] is the k-th station
Matrix< T > rates
(nstations x nclasses) service rates and SCVs, with a PARALLEL disabled flag instead of MATLAB's NaN ...
T route_eff(std::size_t r, std::size_t s, std::size_t i, std::size_t j) const
The routing actually in force: the expansion when there is one, else P.
std::vector< std::vector< std::size_t > > inchain
1-based class indices per chain
bool has_multi_server() const
std::vector< NodeDef > nodes
every node, in creation order
std::vector< Matrix< T > > visits
(nchains) each (nstateful x nclasses)
std::vector< Region > regions
The fork-join fixed point that drives one inner MVA solve.
The Heidelberger-Trivedi fork-join transform, options.config.fork_join='ht'.
The fork-join transform SolverMVA applies before solving a layer that contains a Fork.
Port of @@SolverMVA/mvaDispatch.m: one inner solve, choosing the analyzer that fits the model.
The option and result types every MVA analyzer shares.
PatienceHandles< T > sn_patience_handles(const qn::NetworkStruct< T > &sn, std::size_t ist, std::size_t r)
Build the patience handles of station ist (0-based), class r.
bool sn_has_bursty_arrival(const qn::NetworkStruct< T > &sn)
SchedStrategy
Scheduling disciplines, with the values of MATLAB SchedStrategy.
NodeType
Node kinds, with the values of MATLAB NodeType.
Matrix< T > sn_get_residt_from_respt(const qn::NetworkStruct< T > &L, const Matrix< T > &RN)
Port of sn_get_residt_from_respt: the per-JOB residence time.
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.
bool sn_has_sjn(const qn::NetworkStruct< T > &L)
True when the layer has an SJF station, the reference's any(sn.sched == SchedStrategy....
std::string mva_single_class_open_reason(const qn::NetworkStruct< T > &L, const std::string &method)
RQNA and RQT decompose an open network into GI/G/1 queues and build one uncertainty set per flow out ...
std::string mva_schmidt_ext_reason(const std::vector< double > &njobs, const std::vector< bool > &fcfs, const std::string &method)
The extended Schmidt method needs a customer of every class to tag.
bool sn_is_mm1k_loss(const qn::NetworkStruct< T > &L)
Port of matlab/src/api/sn/sn_is_mm1k_loss.m: a single-class open Source-Queue-Sink system whose queue...
MetricKind
Which metric is being filtered.
std::string resolve_method(const qn::NetworkStruct< T > &L, const std::string &method)
Port of SolverMVA.resolveMethod: the feature-driven default -> rqna upgrade for a bursty single-class...
std::vector< std::string > list_valid_methods(const qn::NetworkStruct< T > &L)
Port of SolverMVA.listValidMethods.
bool sn_is_bas_model(const qn::NetworkStruct< T > &L)
Port of matlab/src/api/sn/sn_is_bas_model.m: a closed single-CLASS model with a BAS drop rule.
std::string mva_mvac_reason(const qn::NetworkStruct< T > &L, const std::string &method)
MVAC is the exact chain recursion over single-server fixed-rate (SSFR) queues and infinite-server cen...
void mva_check_finite_capacity(const qn::NetworkStruct< T > &L, const std::string &method)
Port of SolverMVA.supportsFiniteCapacity (SolverMVA.m:158-186), the structural capacity gate SolverMV...
std::string mva_mapqn_reason(const qn::NetworkStruct< T > &L, const std::string &method)
The reason 'amva.mapqn' cannot solve L, or "" when it can (and "" for any other method).
FjMmt< T > fj_fork_join_transform(const qn::NetworkStruct< T > &L, const std::string &method)
options.config.fork_join -> the transform it names.
void sn_apply_join_respt(const qn::NetworkStruct< T > &L, const Matrix< T > &QN, const Matrix< T > &AN, Matrix< T > &RN)
Port of sn_get_arvr_from_tput: the arrival rate each station sees, from the throughputs and the class...
std::string mva_closed_population_reason(const qn::NetworkStruct< T > &L, const std::string &method)
Per-method structural gates, shared by list_valid_methods and by the analyzers themselves.
Matrix< T > sn_get_arvr_from_tput(const qn::NetworkStruct< T > &L, const Matrix< T > &TN)
void check_method(const qn::NetworkStruct< T > &L, const std::string &method)
Port of runAnalyzerChecks' method gate: a method the solver does not list is refused before any analy...
MvaSolution< T > fj_fixed_point(const qn::NetworkStruct< T > &L, FjMmt< T > &tr, std::vector< T > &lam, const MvaOptions &opt, InnerSolve inner)
Drive the fork-join fixed point of a transformed model to convergence.
AvgResult< T > solver_mva_run_analyzer(const qn::NetworkStruct< T > &L, const MvaOptions &opt_in, const Matrix< T > &init_sol)
Port of @@SolverMVA/runAnalyzer.m for the lang='matlab' path: gate, solve, convert,...
DispatchResult< T > mva_dispatch(const qn::NetworkStruct< T > &L, const MvaOptions &opt, const Matrix< T > &init_sol)
The ladder itself.
ChainDemands< T > sn_get_demands_chain(const qn::NetworkStruct< T > &L)
Port of sn_get_demands_chain.
void check_binding_capacity(const std::string &solver, const NetworkStruct< T > &sn)
FeatureSet mva_feature_set(const std::string &raw_method)
void feature_gate(const std::string &solver, const FeatureSet &declared, const NetworkStruct< T > &sn, const std::string &requested_method="", const std::string &resolved_method="")
runAnalyzerChecks: refuse a model the solver does not declare, by name.
std::string mva_base_method(const std::string &method)
SolverMVA.getFeatureSet, plus getMethodFeatureSet's per-method deltas.
A queueing network and its refreshed NetworkStruct.
Chain aggregation and de-aggregation.
Port of matlab/src/api/sn/sn_has_bursty_arrival.m.
Port of matlab/src/api/sn/sn_patience_handles.m.
The DECLARED side of the gate: one feature set per solver.
Closed networks with shortest-job-next (SJF) stations, ladder branch 0.
The patience law of one station-class pair, in the forms the solvers consume.
bool present
Whether the pair declares reneging at all; everything below is unset when false.
bool isExponential
Whether the law is exponential, in which case the analysis is exact.
The metrics getAvg returns, after filtering.
std::shared_ptr< qn::NetworkStruct< T > > refreshed_struct
The struct whose cache self-switch carries the CONVERGED hit/miss split, filled by the cacheqn branch...
Matrix< T > RN
response time, per visit
std::string warning
The reference's own warning text, verbatim, empty when it did not warn.
Matrix< T > UN
utilization
std::vector< T > listcost
(h) mean storage cost held by each cache list, K_j = sum_i sigma_i pi_ij, filled only by the NC cache...
std::optional< double > lognormconst
@@SolverNC/getProbNormConstAggr, i.e.
std::optional< bool > converged
Whether the fixed point met its tolerance, empty when the handler reports none.
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
solvers::CacheMetrics< T > cache
What the cache branches observed, EMPTY on a model with no Cache node and on every solver that does n...
Matrix< T > AN
arrival rate
The chain-level view of a layer, as sn_get_demands_chain returns it.
std::vector< double > Nchain
(C) population, infinite for an open chain
What the dispatch returns: the metrics plus the algorithm that produced them.
std::string actualmethod
The concrete algorithm, as the reference's actualmethod.
std::string warning
Non-empty when the analyzer that ran would have raised a reference line_warning and still returned a ...
solvers::CacheMetrics< T > cache
What a cache branch measured, EMPTY on every model without a Cache.
std::shared_ptr< qn::NetworkStruct< T > > refreshed_struct
Set only by the integrated cacheqn branch: the converged struct whose routing carries the actual hit/...
The transformed layer and the bookkeeping the fixed point needs to drive it and to merge its results ...
static constexpr double FineTol
The options SolverMVA reads.
bool base_has_fork
Whether the model this solve came from has a Fork, which the model handed to the analyzer no longer d...
Class-level results, the [Q,U,R,T,C,X] of the MATLAB analyzers.
std::optional< bool > converged
Whether the fixed point met its tolerance, or empty when the handler reports none.
std::vector< std::size_t > missclass
std::vector< std::size_t > hitclass
One job class of the network.
double population
infinite for an open class
One station of the network.
std::vector< int > droprule
Per-class blocking rule as an INT, with 0 meaning "not set".
Every Cache node of the model, in node order; empty on a model with none.