5#ifndef LINE_SOLVERS_MAM_MAM_DISPATCH_H
6#define LINE_SOLVERS_MAM_MAM_DISPATCH_H
85namespace dispatch_detail {
89bool has_fork(
const qn::NetworkStruct<T>& L) {
99bool has_fork_join(
const qn::NetworkStruct<T>& L) {
100 return !L.fj.empty();
111bool is_closed_model(
const qn::NetworkStruct<T>& L) {
112 for (
const qn::JobClass& c : L.classes)
113 if (!std::isfinite(c.population))
return false;
114 return !L.classes.empty();
119bool is_closed_delay_queue(
const qn::NetworkStruct<T>& L) {
120 if (L.nclasses != 1 || L.nstations != 2)
return false;
121 if (!std::isfinite(L.classes[0].population))
return false;
122 std::size_t ndelay = 0, nqueue = 0;
123 for (
const qn::Station<T>& st : L.stations) {
127 return ndelay == 1 && nqueue == 1;
136bool ldqbd_setup_ok(
const qn::NetworkStruct<T>& L) {
137 if (L.setupparam.empty())
return true;
138 for (
typename std::map<std::size_t, qn::SetupDelayOffParam<T>>::const_iterator it =
139 L.setupparam.begin();
140 it != L.setupparam.end(); ++it) {
141 const std::size_t ist = it->first;
142 if (ist == 0 || ist > L.stations.size())
return false;
143 lang::Distrib<T> su, doff;
144 if (!it->second.last(su, doff) || doff.disabled)
continue;
145 const qn::Station<T>& st = L.stations[ist - 1];
146 if (st.nservers > 1)
return false;
153 if (L.service.size() < ist || L.service[ist - 1].empty())
return false;
167bool mna_applies(
const qn::NetworkStruct<T>& L) {
174 if (L.nchains != L.nclasses)
return false;
176 for (
const qn::NodeDef& nd : L.nodes)
177 for (std::size_t r = 0; r < nd.routing.size(); ++r)
180 for (
const qn::Station<T>& st : L.stations) {
197 const Matrix<T> V = basic_detail::station_visits(L);
198 for (std::size_t k = 0; k < L.nclasses; ++k) {
199 if (!std::isfinite(L.classes[k].population))
continue;
200 std::size_t seen = 0, at = 0;
201 for (std::size_t i = 0; i < L.nstations; ++i)
202 if (num_traits<T>::to_double(V(i, k)) > 1e-8) {
221template <
class T,
class Sol>
222bool mna_conserves(
const qn::NetworkStruct<T>& L,
const Sol& sol) {
223 for (std::size_t i = 0; i < L.nstations; ++i)
224 for (std::size_t k = 0; k < L.nclasses; ++k)
225 if (!std::isfinite(num_traits<T>::to_double(sol.Q(i, k))) ||
226 !std::isfinite(num_traits<T>::to_double(sol.R(i, k))) ||
227 !std::isfinite(num_traits<T>::to_double(sol.Tp(i, k))))
230 for (std::size_t k = 0; k < L.nclasses; ++k) {
231 const double nk = L.classes[k].population;
232 if (!std::isfinite(nk) || nk <= 0)
continue;
234 for (std::size_t i = 0; i < L.nstations; ++i)
235 npred += num_traits<T>::to_double(sol.Tp(i, k)) * num_traits<T>::to_double(sol.R(i, k));
236 if (std::fabs(npred - nk) > 0.01 * nk)
return false;
247bool bgchain_applies(
const qn::NetworkStruct<T>& L,
const MamOptions& opt) {
248 bool prio_sched =
false;
249 for (
const qn::Station<T>& st : L.stations)
253 for (std::size_t k = 1; k < L.nclasses; ++k)
254 if (L.classes[k].prio != L.classes[0].prio)
return false;
256 if (has_fork_join(L))
return false;
259 bool visited =
false;
260 for (std::size_t c = 0; c < L.nchains && !visited; ++c) {
261 if (!std::isfinite(num_traits<T>::to_double(dem.Nchain[c])) ||
262 num_traits<T>::to_double(dem.Nchain[c]) <= 0.0)
264 for (std::size_t i = 0; i < L.nstations; ++i)
265 if (num_traits<T>::to_double(dem.Vchain(i, c)) > 1e-14) {
270 if (!visited)
return false;
278 const double states_max = (opt.bgstates_max > 0) ?
static_cast<double>(opt.bgstates_max) : 20000.0;
306bool bgchain_closed_exact(
const qn::NetworkStruct<T>& L) {
307 for (std::size_t i = 0; i < L.nstations; ++i) {
312 double rate_here = std::numeric_limits<double>::quiet_NaN();
313 for (std::size_t r = 0; r < L.nclasses; ++r) {
314 const double rate = num_traits<T>::to_double(L.rates(i, r));
315 if (!std::isfinite(rate) || rate <= 0.0)
continue;
317 if (std::isnan(rate_here)) {
319 }
else if (std::fabs(rate - rate_here) > 1e-3 * rate_here) {
329void reject_unsupported_nodes(
const qn::NetworkStruct<T>& L) {
330 for (
const qn::NodeDef& nd : L.nodes) {
331 switch (nd.nodetype) {
332 case qn::NodeType::Cache:
334 "SolverMAM: Cache nodes are outside the MAM feature set; use SolverMVA, "
335 "SolverNC, SolverCTMC or SolverLDES");
336 case qn::NodeType::Place:
337 case qn::NodeType::Transition:
339 "SolverMAM: stochastic Petri net models are outside the MAM feature set; "
340 "use SolverCTMC, SolverSSA or SolverLDES");
371 for (std::size_t i = 0; i < L.
nstations; ++i)
372 if (L.
stations[i].sched == SchedStrategy::EXT)
373 for (std::size_t r = 0; r < L.
nclasses; ++r)
381 for (std::size_t i = 0; i < m->rows(); ++i)
382 for (std::size_t j = 0; j < m->cols(); ++j)
384 for (T& v : out.
sol.C)
386 for (T& v : out.
sol.X)
398 dispatch_detail::reject_unsupported_nodes(L);
412 out.
sol.method =
opt.method;
424 out.
sol.method =
opt.method;
430 const std::string& method =
opt.method;
432 if (method ==
"dec.mmap") {
438 if (method ==
"default" || method ==
"dec.source" || method ==
"dec.poisson") {
439 if (method ==
"dec.poisson")
opt.space_max = 1;
440 if (method !=
"dec.poisson") {
442 if (dispatch_detail::has_fork(L) || dispatch_detail::has_fork_join(L))
444 "SolverMAM: model '" + L.
name +
445 "' forks, and every fork-join route here first classifies the branches by "
446 "fitting a phase-type to each, which is transcendental and has no exact "
447 "counterpart; use --arith double or real");
470 if (dispatch_detail::has_fork_join(L) && L.
is_open_model()) {
487 if (dispatch_detail::has_fork(L))
489 "SolverMAM: model '" + L.
name +
490 "' carries a Fork but is not an open fork-join network, and "
491 "solver_mam_analyzer.m has no branch for it: it falls through to "
492 "solver_mam_basic, whose dec.source decomposition never charges the "
493 "synchronization delay at the Join, so the response times would be those of "
494 "the branches alone. Call method 'dec.source.mmap', which routes the model to "
495 "the MMAP decomposition's closed wrapper and does charge the join, or use "
496 "SolverMVA, SolverNC, SolverJMT or SolverLDES");
515 "SolverMAM: model '" + L.
name +
516 "' declares a retrial orbit, whose analyzer fits a phase-type to the "
517 "arrival and service processes and has no exact counterpart; use --arith "
545 if (method ==
"default" && dispatch_detail::ldqbd_setup_ok(L) &&
546 dispatch_detail::is_closed_delay_queue(L)) {
561 if (method ==
"default" && dispatch_detail::is_closed_model(L) &&
563 dispatch_detail::bgchain_applies(L,
opt) &&
564 dispatch_detail::bgchain_closed_exact(L)) {
577 if (method ==
"default" && dispatch_detail::is_closed_model(L) &&
579 dispatch_detail::mna_applies(L)) {
582 if (!dispatch_detail::mna_conserves(L, out.
sol)) {
599 !dispatch_detail::is_closed_model(L) && dispatch_detail::bgchain_applies(L,
opt)) {
606 out.
actualmethod = (method ==
"default") ?
"dec.source" : method;
607 }
else if (method ==
"mna") {
613 }
else if (dispatch_detail::is_closed_model(L)) {
622 "SolverMAM: the mna method in SolverMAM does not support mixed models");
625 }
else if (method ==
"bgchain") {
632 }
else if (method ==
"ldqbd") {
635 }
else if (method ==
"inap" || method ==
"inapplus" || method ==
"inapinf"
636 || method ==
"exact") {
641 "SolverMAM: the '" + method +
"' method moved to SolverAG: RCAT decomposes the "
642 "model into cooperating agents rather than decomposing traffic, and no MAM "
643 "algorithm shares its machinery. Solve it with -s ag, or call "
644 "line::ag::solver_ag directly");
645 }
else if (method ==
"retrial") {
659 "SolverMAM: the retrial analyzer fits a phase-type to the arrival and "
660 "service processes and has no exact counterpart; use --arith double or real");
666 }
else if (method ==
"dec.source.mmap") {
UnsupportedError(const std::string &what)
A network plus its refreshed NetworkStruct.
std::map< std::size_t, SetupDelayOffParam< T > > setupparam
Setup / delay-off, keyed by 1-based STATION index.
std::vector< std::vector< bool > > disabled
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 ...
bool is_open_model() const
sn_is_open_model: EVERY class is open, which is not has_open_classes.
The exception types the port throws.
The option and result types SolverMAM shares with its analyzers.
bool sn_has_load_dependence(const qn::NetworkStruct< T > &sn)
size(sn.lldscaling,2)>0: some station carries a limited-load scaling row.
bool sn_is_discrete_time(const qn::NetworkStruct< T > &sn, const DiscreteTimeOptions &options, double *slot_length, DiscreteTimeInfo *info)
True when every law of sn is lattice-valued on slot_length.
mam::Map< T > dist_to_map(const Distrib< T > &d)
SchedStrategy
Scheduling disciplines, with the values of MATLAB SchedStrategy.
mva::MvaSolution< T > solver_mna_open(const qn::NetworkStruct< T > &L, const MamOptions &opt, const MnaConfig &cfg=MnaConfig())
Port of solver_mna_open.m.
MamSolution< T > & finish_dispatch(const qn::NetworkStruct< T > &L, MamSolution< T > &out)
What solver_mam_analyzer.m does AFTER whichever analyzer ran: pin the throughput of every EXT (Source...
double bgchain_states(const qn::NetworkStruct< T > &L, const MamOptions &opt)
Port of solver_mam_bgchain.m.
mva::MvaSolution< T > solver_mam_bgchain(const qn::NetworkStruct< T > &L, const MamOptions &opt)
MamSolution< T > mam_dispatch(const qn::NetworkStruct< T > &L, const MamOptions &opt_in)
The ladder.
mva::MvaSolution< T > solver_mam_basic_mmap(const qn::NetworkStruct< T > &L, const MamOptions &opt)
Port of solver_mam_basic_mmap.m, the top-level dispatcher of the MMAP fork-join decomposition: an ope...
mva::MvaSolution< T > solver_mam_fj(const qn::NetworkStruct< T > &L, const MamOptions &opt, std::vector< std::vector< T > > *percentiles_out)
Port of solver_mam_fj.m.
LdqbdSolution< T > solver_mam_ldqbd(const qn::NetworkStruct< T > &L, const MamOptions &opt)
Port of solver_mam_ldqbd.m.
mva::MvaSolution< T > solver_mam_basic(const qn::NetworkStruct< T > &L, const MamOptions &opt)
Port of solver_mam_basic.m.
MamFjInfo mam_fj_is_homogeneous(const qn::NetworkStruct< T > &L)
Port of fj_is_homogeneous.m.
MamSolution< T > solver_mam_dt(const qn::NetworkStruct< T > &sn, const MamOptions &opt, double slot_length)
Discrete-time analysis of sn, returning the same metric tuple as every other MAM analyzer.
mva::MvaSolution< T > solver_mna_closed(const qn::NetworkStruct< T > &L, const MamOptions &opt)
Port of solver_mna_closed.m.
MamRetrialInfo mam_retrial_detect(const qn::NetworkStruct< T > &L)
Port of qsys_is_retrial.m, plus the reneging gate of solver_mam_retrial.m.
MapMap1Exact< T > solver_mam_mapmap1_exact(const qn::NetworkStruct< T > &L)
mva::MvaSolution< T > solver_mam_decmmap(const qn::NetworkStruct< T > &L, const MamOptions &opt)
Port of solver_mam.m.
mva::MvaSolution< T > solver_mam_retrial(const qn::NetworkStruct< T > &L, const MamOptions &opt, const MamRetrialConfig &cfg=MamRetrialConfig())
Port of solver_mam_retrial.m.
std::string mam_retrial_refusal(const qn::NetworkStruct< T > &L)
The 'retrial' method's applicability as one sentence; empty when applicable.
ChainDemands< T > sn_get_demands_chain(const qn::NetworkStruct< T > &L)
Port of sn_get_demands_chain.
A queueing network and its refreshed NetworkStruct.
Port of matlab/src/api/sn/sn_is_discrete_time.m.
Ports of the sn_has_* / sn_is_* predicate family of matlab/src/api/sn.
Port of solver_mam_basic.m, the dec.source analyzer and the default algorithm of SolverMAM.
Port of solver_mam_basic_mmap.m, solver_mam_basic_mmap_inner.m and solver_mam_basic_mmap_closed....
Port of solver_mam_bgchain.m and its three helpers: the analyzer that treats the CLOSED classes as a ...
Port of solver_mam.m, the dec.mmap method: the per-class departure-process decomposition.
Port of solver_mam_dt.m: discrete-time (slotted) analysis of an open network whose interarrival and s...
Port of solver_mam_fj.m, the fork-join route of SolverMAM.
Port of solver_mam_ldqbd.m: the level-dependent QBD analyzer for a single-class network of one infini...
Port of solver_mam_mapmap1_exact.m: the exact fast path SolverMAM tries BEFORE anything else,...
Port of solver_mam_retrial.m, the customer-impatience analyzer of SolverMAM.
Port of solver_mna_open.m and solver_mna_closed.m, the two analyzers behind SolverMAM's mna method.
Which laws the model carries, and why it was refused when it was.
How the caller may override the automatic decision.
std::string timescale
"auto", "discrete" or "continuous".
double slotlength
Slot length in model time units.
What fj_is_homogeneous.m returns: the fork-join pair, or why there is none.
The options SolverMAM reads.
What qsys_is_retrial.m returns: the station it found, or why it found none.
What the MAM dispatch returns: the metrics plus the algorithm that ran.
std::string actualmethod
The concrete algorithm, as the reference's actualmethod.
mva::MvaSolution< T > sol
Result of the fast path; ok false means the model is not in its regime.
mva::MvaSolution< T > sol