5#ifndef LINE_SOLVERS_MVA_SOLVER_MVA_RUNNER_H
6#define LINE_SOLVERS_MVA_SOLVER_MVA_RUNNER_H
142 std::vector<std::string> m{
"default",
"mva",
"exact",
"amva",
143 "sum",
"esum",
"qdlin",
"amva.qdlin",
144 "bs",
"amva.bs",
"sqni",
"qd",
145 "amva.qd",
"qli",
"amva.qli",
"fli",
146 "amva.fli",
"ab",
"amva.ab",
"schmidt",
147 "amva.schmidt",
"schmidt-ext",
"amva.schmidt-ext",
149 "lcp",
"amva.lcp",
"chow",
"amva.chow",
150 "pamb",
"amva.pamb",
"pami",
"amva.pami",
151 "pamt",
"amva.pamt",
"clust",
"amva.clust",
152 "dmlin",
"amva.dmlin"};
159 m.push_back(
"amva.aql");
161 m.push_back(
"amva.qsa");
163 m.push_back(
"amva.tay");
166 bool open =
true, closed =
false;
179 bool has_bas =
false;
183 if (L.
nclasses == 1 && !open && has_bas) m.push_back(
"sqd");
189 bool any_open_class =
false;
191 if (std::isinf(c.
population)) any_open_class =
true;
192 if (!any_open_class) m.push_back(
"mvac");
201 m.push_back(
"sjn.mva");
202 m.push_back(
"sjn.amva");
209 m.insert(m.begin() + 4,
"rqt");
210 m.insert(m.begin() + 4,
"rqna");
211 m.insert(m.begin() + 4,
"qna");
218 bool classdep_routing =
false;
219 for (std::size_t r = 0; r + 1 < L.
nclasses; ++r)
220 for (std::size_t i = 1; i <= L.
nof_nodes() && !classdep_routing; ++i)
221 for (std::size_t j = 1; j <= L.
nof_nodes(); ++j)
223 classdep_routing =
true;
226 if (!classdep_routing) {
227 m.push_back(
"marie");
228 m.push_back(
"amva.marie");
235 if (st.
sched == qn::SchedStrategy::FCFSPRPRIO) {
236 m.push_back(
"priomva");
237 m.push_back(
"amva.priomva");
244 m.push_back(
"amva.mapqn");
248 m.push_back(
"egflin");
249 m.push_back(
"gflin");
250 m.push_back(
"amva.lin");
255 const char*
qsys[] = {
"mm1",
"mmk",
"mg1",
256 "mgi1",
"gm1",
"gig1",
257 "gim1",
"gig1.kingman",
"gigk",
258 "gigk.kingman_approx",
"gig1.gelenbe",
259 "gig1.heyman",
"gig1.kimura",
"gig1.allen",
260 "gig1.kobayashi",
"gig1.klb",
"gig1.marchal",
261 "gigk.whitt",
"qed",
"gig1.extremal",
"gigk.diffusion"};
262 for (
const char* q :
qsys) m.push_back(q);
266 const std::size_t qi = detail::station_of_type(L, qn::NodeType::Queue);
268 m.push_back(
"erlanga");
269 m.push_back(
"mgisrgi");
280 std::size_t n_inf = 0;
282 if (st.
sched == qn::SchedStrategy::INF) ++n_inf;
287 std::vector<double> sx_n;
288 std::vector<bool> sx_fcfs;
289 bool sx_ready =
false;
290 std::vector<std::string> keep;
291 keep.reserve(m.size());
292 for (
const std::string& name : m) {
300 for (std::size_t c = 0; c < L.
nchains; ++c) sx_n.push_back(cd.
Nchain[c]);
302 if (st.
sched != qn::SchedStrategy::INF && st.
sched != qn::SchedStrategy::EXT)
303 sx_fcfs.push_back(st.
sched == qn::SchedStrategy::FCFS);
311 keep.push_back(name);
324 if (std::find(valid.begin(), valid.end(), method) != valid.end())
return;
325 throw UnsupportedError(
"SolverMVA: the '" + method +
"' method is unsupported by this solver");
340 if (method !=
"default" || L.
nclasses != 1)
return method;
343 for (std::size_t r = 0; r < L.
nclasses; ++r)
344 if (std::isfinite(L.
classes[r].population))
return method;
351 const std::size_t qi = detail::station_of_type(L, qn::NodeType::Queue);
373 for (std::size_t c = 0; c < L.
nchains; ++c)
374 for (std::size_t i = 0; i < L.
nstations; ++i) {
376 for (std::size_t k = 0; k < L.
nclasses; ++k)
377 V(i, k) = T(V(i, k) + L.
visits[c](sf - 1, k));
397 const Matrix<T> V = detail::station_visits(L);
399 for (std::size_t i = 0; i < M; ++i)
400 for (std::size_t k = 0; k < K; ++k) {
409 const bool is_place = L.
stations[i].nodetype == qn::NodeType::Place;
410 if ((L.
disabled[i][k] && !is_place) || !(RN(i, k) > zero))
continue;
416 for (std::size_t cc = 0; cc < L.
nchains; ++cc)
417 if (L.
chains[cc][k]) c = cc;
419 const std::size_t rs = L.
classes[k].refstat;
424 for (std::size_t r : L.
inchain[c]) den += V(rs - 1, r - 1);
426 if (den > zero) WN(i, k) = T(RN(i, k) * V(i, k) / den);
428 for (std::size_t i = 0; i < M; ++i)
429 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);
499 std::vector<std::size_t> ucols;
500 std::vector<bool> is_u(S * R,
false);
501 for (std::size_t sf = 0; sf < S; ++sf) {
503 for (std::size_t k = 0; k < R; ++k) {
504 ucols.push_back(sf * R + k);
505 is_u[sf * R + k] =
true;
508 if (!ucols.empty()) {
509 const std::size_t nU = ucols.size();
511 std::vector<T> b(nU, zero);
512 for (std::size_t j = 0; j < nU; ++j) {
514 for (std::size_t col = 0; col < S * R; ++col)
515 if (!is_u[col]) acc += T(TS(col / R, col % R) * L.
rt(col, ucols[j]));
517 for (std::size_t i = 0; i < nU; ++i)
521 for (std::size_t j = 0; j < nU; ++j) TS(ucols[j] / R, ucols[j] % R) = xu[j];
523 for (std::size_t ist = 0; ist < M; ++ist) {
525 for (std::size_t k = 0; k < R; ++k) {
527 for (std::size_t sf2 = 0; sf2 < S; ++sf2)
528 for (std::size_t r = 0; r < R; ++r)
529 acc += T(TS(sf2, r) * L.
rt(sf2 * R + r, sf_i * R + k));
549enum class MetricKind { QLen, Util, RespT, ResidT, Tput, ArvR };
560 const std::vector<std::vector<bool>>* zero_mask) {
575 bool has_fj =
false, has_cache =
false, has_spn =
false;
577 if (nd.
nodetype == qn::NodeType::Fork || nd.
nodetype == qn::NodeType::Join) has_fj =
true;
578 if (nd.
nodetype == qn::NodeType::Cache) has_cache =
true;
579 if (nd.
nodetype == qn::NodeType::Transition) has_spn =
true;
585 std::vector<bool> is_cache_class(K,
false);
588 for (std::size_t r = 0; r < cp.
hitclass.size(); ++r) {
591 is_cache_class[cp.
missclass[r] - 1] =
true;
596 for (std::size_t i = 0; i < M; ++i) {
598 const bool src_or_sink = (nt == qn::NodeType::Source || nt == qn::NodeType::Sink);
599 bool kind_disabled =
false;
604 kind_disabled = src_or_sink;
607 kind_disabled = src_or_sink || nt == qn::NodeType::Fork || nt == qn::NodeType::Join;
611 kind_disabled =
false;
614 if (kind_disabled)
continue;
637 bool service_tunnel = (nt == qn::NodeType::Source || nt == qn::NodeType::Fork ||
638 nt == qn::NodeType::Join);
639 if (nt == qn::NodeType::Place) {
640 service_tunnel =
true;
641 for (std::size_t k = 0; k < K && k < L.
service[i].size(); ++k)
642 if (!L.
service[i][k].disabled) { service_tunnel =
false;
break; }
644 for (std::size_t k = 0; k < K; ++k) {
645 if (!L.
disabled[i][k] || service_tunnel) {
646 out(i, k) = metric(i, k);
647 }
else if (is_cache_class[k] &&
652 out(i, k) = metric(i, k);
657 for (std::size_t i = 0; i < M; ++i)
658 for (std::size_t k = 0; k < K; ++k)
659 if ((*zero_mask)[i][k]) out(i, k) = zero;
660 for (std::size_t i = 0; i < M; ++i)
661 for (std::size_t k = 0; k < K; ++k)
664 for (std::size_t k = 0; k < K; ++k) {
666 for (std::size_t cc = 0; cc < L.
nchains; ++cc)
667 if (L.
chains[cc][k]) c = cc;
669 for (std::size_t i = 0; i < M; ++i) {
671 if ((has_fj || has_cache || has_spn) &&
716 (void)L; (void)QN; (void)UN; (void)RN; (void)TN;
720 bool any_open =
false;
721 for (std::size_t r = 0; r < K; ++r)
722 if (std::isinf(L.
classes[r].population)) any_open =
true;
723 if (!any_open)
return false;
725 const double dinf = std::numeric_limits<double>::infinity();
726 bool any_unstable =
false;
727 for (std::size_t i = 0; i < M; ++i) {
731 if (!std::isfinite(c) || c <= 0.0)
continue;
732 if (nt == qn::NodeType::Source || nt == qn::NodeType::Sink)
continue;
733 if (sch == qn::SchedStrategy::INF || sch == qn::SchedStrategy::EXT)
continue;
735 bool bounded = (i < L.
cap.size() && std::isfinite(L.
cap[i]));
736 if (!bounded && i < L.
classcap.size())
738 if (std::isfinite(cc)) { bounded =
true;
break; }
740 for (
const auto& rg : L.
regions)
741 if (i < rg.members.size() && rg.members[i]) { bounded =
true;
break; }
742 if (bounded)
continue;
744 bool reneges =
false;
747 if (reneges)
continue;
749 double lldpeak = 1.0;
750 for (
const T& v : L.
stations[i].lldscaling) {
752 if (std::isfinite(d) && d > 0.0 && d > lldpeak) lldpeak = d;
755 std::vector<double> rho(K, 0.0);
756 double rho_open = 0.0, rho_tot = 0.0;
757 for (std::size_t r = 0; r < K; ++r) {
760 if (rate > 0.0 && tp > 0.0) rho[r] = tp / (c * lldpeak * rate);
762 if (std::isinf(L.
classes[r].population)) rho_open += rho[r];
764 if (rho_open < 1.0 || rho_tot <= 0.0)
continue;
766 for (std::size_t r = 0; r < K; ++r) {
768 if (std::isinf(L.
classes[r].population) && rho[r] > 0.0) {
780 return "The model has unstable queues (utilization >= 1); station utilization is "
781 "reported capped at 1.0, queue length and response time as Inf.";
837 "SolverMVA: this model uses a Finite Capacity Region (addRegion), which is not "
838 "supported by SolverMVA. Use SolverJMT, or setCapacity for a single-station limit.");
863 std::string actualmethod, warning;
867 std::shared_ptr<qn::NetworkStruct<T>> refreshed;
878 std::vector<T> lam(
tr.V.classes.size() + 1,
906 std::vector<std::vector<bool>> mask(M, std::vector<bool>(K,
false));
907 for (std::size_t i = 0; i < M; ++i)
908 for (std::size_t k = 0; k < K; ++k)
914 std::vector<std::vector<bool>> srcmask(M, std::vector<bool>(K,
false));
915 for (std::size_t i = 0; i < M; ++i)
916 if (L.
stations[i].nodetype == qn::NodeType::Source)
917 for (std::size_t k = 0; k < K; ++k) srcmask[i][k] =
true;
940 if (!warning.empty()) warning +=
" ";
955 if (!warning.empty()) warning +=
" ";
957 "SolverMVA does not handle immediate feedback (immfeed); the solver will treat "
958 "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
Whether immediate feedback is EFFECTIVE anywhere in the model.
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< double > cap
sn.cap and sn.classcap: the total and per-class buffers.
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< std::vector< double > > classcap
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.
LU factorization with partial pivoting, templated on the number type.
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.
ImpatienceType
Impatience kinds, with the values of MATLAB ImpatienceType.
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 ...
bool cap_unstable_open_stations(const qn::NetworkStruct< T > &L, Matrix< T > &QN, Matrix< T > &UN, Matrix< T > &RN, const Matrix< T > &TN)
The saturated open-station rule of @@NetworkSolver/getAvg.m:221-308.
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...
const char * unstable_open_warning()
The warning getAvg.m raises when cap_unstable_open_stations() fires.
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.
Conservation laws of a layered queueing network, enumerated from its structure.
std::vector< T > solve(const Matrix< T > &A, const std::vector< T > &b)
Convenience: solve Ax = b, leaving A and b untouched.
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.