5#ifndef LINE_SOLVERS_MAM_MAM_DISPATCH_H
6#define LINE_SOLVERS_MAM_MAM_DISPATCH_H
87namespace dispatch_detail {
91bool has_fork(
const qn::NetworkStruct<T>& L) {
101bool has_fork_join(
const qn::NetworkStruct<T>& L) {
102 return !L.fj.empty();
113bool is_closed_model(
const qn::NetworkStruct<T>& L) {
114 for (
const qn::JobClass& c : L.classes)
115 if (!std::isfinite(c.population))
return false;
116 return !L.classes.empty();
121bool is_closed_delay_queue(
const qn::NetworkStruct<T>& L) {
122 if (L.nclasses != 1 || L.nstations != 2)
return false;
123 if (!std::isfinite(L.classes[0].population))
return false;
124 std::size_t ndelay = 0, nqueue = 0;
125 for (
const qn::Station<T>& st : L.stations) {
129 return ndelay == 1 && nqueue == 1;
138bool ldqbd_setup_ok(
const qn::NetworkStruct<T>& L) {
139 if (L.setupparam.empty())
return true;
140 for (
typename std::map<std::size_t, qn::SetupDelayOffParam<T>>::const_iterator it =
141 L.setupparam.begin();
142 it != L.setupparam.end(); ++it) {
143 const std::size_t ist = it->first;
144 if (ist == 0 || ist > L.stations.size())
return false;
145 lang::Distrib<T> su, doff;
146 if (!it->second.last(su, doff) || doff.disabled)
continue;
147 const qn::Station<T>& st = L.stations[ist - 1];
148 if (st.nservers > 1)
return false;
155 if (L.service.size() < ist || L.service[ist - 1].empty())
return false;
169bool mna_applies(
const qn::NetworkStruct<T>& L) {
176 if (L.nchains != L.nclasses)
return false;
178 for (
const qn::NodeDef& nd : L.nodes)
179 for (std::size_t r = 0; r < nd.routing.size(); ++r)
182 for (
const qn::Station<T>& st : L.stations) {
199 const Matrix<T> V = basic_detail::station_visits(L);
200 for (std::size_t k = 0; k < L.nclasses; ++k) {
201 if (!std::isfinite(L.classes[k].population))
continue;
202 std::size_t seen = 0, at = 0;
203 for (std::size_t i = 0; i < L.nstations; ++i)
204 if (num_traits<T>::to_double(V(i, k)) > 1e-8) {
223template <
class T,
class Sol>
224bool mna_conserves(
const qn::NetworkStruct<T>& L,
const Sol& sol) {
225 for (std::size_t i = 0; i < L.nstations; ++i)
226 for (std::size_t k = 0; k < L.nclasses; ++k)
227 if (!std::isfinite(num_traits<T>::to_double(sol.Q(i, k))) ||
228 !std::isfinite(num_traits<T>::to_double(sol.R(i, k))) ||
229 !std::isfinite(num_traits<T>::to_double(sol.Tp(i, k))))
232 for (std::size_t k = 0; k < L.nclasses; ++k) {
233 const double nk = L.classes[k].population;
234 if (!std::isfinite(nk) || nk <= 0)
continue;
236 for (std::size_t i = 0; i < L.nstations; ++i)
237 npred += num_traits<T>::to_double(sol.Tp(i, k)) * num_traits<T>::to_double(sol.R(i, k));
238 if (std::fabs(npred - nk) > 0.01 * nk)
return false;
249bool bgchain_applies(
const qn::NetworkStruct<T>& L,
const MamOptions& opt) {
250 bool prio_sched =
false;
251 for (
const qn::Station<T>& st : L.stations)
255 for (std::size_t k = 1; k < L.nclasses; ++k)
256 if (L.classes[k].prio != L.classes[0].prio)
return false;
258 if (has_fork_join(L))
return false;
261 bool visited =
false;
262 for (std::size_t c = 0; c < L.nchains && !visited; ++c) {
263 if (!std::isfinite(num_traits<T>::to_double(dem.Nchain[c])) ||
264 num_traits<T>::to_double(dem.Nchain[c]) <= 0.0)
266 for (std::size_t i = 0; i < L.nstations; ++i)
267 if (num_traits<T>::to_double(dem.Vchain(i, c)) > 1e-14) {
272 if (!visited)
return false;
280 const double states_max = (opt.bgstates_max > 0) ?
static_cast<double>(opt.bgstates_max) : 20000.0;
308bool bgchain_closed_exact(
const qn::NetworkStruct<T>& L) {
309 for (std::size_t i = 0; i < L.nstations; ++i) {
314 double rate_here = std::numeric_limits<double>::quiet_NaN();
315 for (std::size_t r = 0; r < L.nclasses; ++r) {
316 const double rate = num_traits<T>::to_double(L.rates(i, r));
317 if (!std::isfinite(rate) || rate <= 0.0)
continue;
319 if (std::isnan(rate_here)) {
321 }
else if (std::fabs(rate - rate_here) > 1e-3 * rate_here) {
331void reject_unsupported_nodes(
const qn::NetworkStruct<T>& L) {
332 for (
const qn::NodeDef& nd : L.nodes) {
333 switch (nd.nodetype) {
334 case qn::NodeType::Cache:
336 "SolverMAM: Cache nodes are outside the MAM feature set; use SolverMVA, "
337 "SolverNC, SolverCTMC or SolverLDES");
338 case qn::NodeType::Place:
339 case qn::NodeType::Transition:
341 "SolverMAM: stochastic Petri net models are outside the MAM feature set; "
342 "use SolverCTMC, SolverSSA or SolverLDES");
350 for (std::size_t i = 0; i < L.nstations; ++i) {
351 if (L.stations[i].nodetype == qn::NodeType::Source)
continue;
352 for (std::size_t r = 0; r < L.nclasses; ++r)
353 if (!L.service[i][r].disabled &&
357 "SolverMAM: station '" + L.stations[i].name +
"' serves class '" +
358 L.classes[r].name +
"' with a batch (BMAP) process, i.e. a bulk server "
359 "whose firing completes several jobs; MAM has no bulk-server analysis. "
388 for (std::size_t i = 0; i < L.
nstations; ++i)
389 if (L.
stations[i].sched == SchedStrategy::EXT)
390 for (std::size_t r = 0; r < L.
nclasses; ++r)
398 for (std::size_t i = 0; i < m->rows(); ++i)
399 for (std::size_t j = 0; j < m->cols(); ++j)
401 for (T& v : out.
sol.C)
403 for (T& v : out.
sol.X)
415 dispatch_detail::reject_unsupported_nodes(L);
429 out.
sol.method =
opt.method;
441 out.
sol.method =
opt.method;
447 const std::string& method =
opt.method;
449 if (method ==
"dec.mmap") {
455 if (method ==
"default" || method ==
"dec.source" || method ==
"dec.poisson") {
456 if (method ==
"dec.poisson")
opt.space_max = 1;
457 if (method !=
"dec.poisson") {
459 if (dispatch_detail::has_fork(L) || dispatch_detail::has_fork_join(L))
461 "SolverMAM: model '" + L.
name +
462 "' forks, and every fork-join route here first classifies the branches by "
463 "fitting a phase-type to each, which is transcendental and has no exact "
464 "counterpart; use --arith double or real");
487 if (dispatch_detail::has_fork_join(L) && L.
is_open_model()) {
504 if (dispatch_detail::has_fork(L))
506 "SolverMAM: model '" + L.
name +
507 "' carries a Fork but is not an open fork-join network, and "
508 "solver_mam_analyzer.m has no branch for it: it falls through to "
509 "solver_mam_basic, whose dec.source decomposition never charges the "
510 "synchronization delay at the Join, so the response times would be those of "
511 "the branches alone. Call method 'dec.source.mmap', which routes the model to "
512 "the MMAP decomposition's closed wrapper and does charge the join, or use "
513 "SolverMVA, SolverNC, SolverJMT or SolverLDES");
532 "SolverMAM: model '" + L.
name +
533 "' declares a retrial orbit, whose analyzer fits a phase-type to the "
534 "arrival and service processes and has no exact counterpart; use --arith "
546 for (std::size_t i = 1; i <= L.
nstations; ++i)
547 if (retrial_detail::has_retrial_proc(L, i))
549 "SolverMAM: model '" + L.
name +
"' declares a retrial orbit outside the "
550 "BMAP/PH/N/N bufferless shape the retrial analyzer solves (" + retinfo.
why +
551 "). Use SolverLDES for any retrial law, or SolverCTMC/SolverSSA for an "
552 "exponential retrial delay with unlimited attempts and no orbit abandonment.");
581 if (method ==
"default" && dispatch_detail::ldqbd_setup_ok(L) &&
582 dispatch_detail::is_closed_delay_queue(L)) {
597 if (method ==
"default" && dispatch_detail::is_closed_model(L) &&
599 dispatch_detail::bgchain_applies(L,
opt) &&
600 dispatch_detail::bgchain_closed_exact(L)) {
613 if (method ==
"default" && dispatch_detail::is_closed_model(L) &&
615 dispatch_detail::mna_applies(L)) {
618 if (!dispatch_detail::mna_conserves(L, out.
sol)) {
635 !dispatch_detail::is_closed_model(L) && dispatch_detail::bgchain_applies(L,
opt)) {
642 out.
actualmethod = (method ==
"default") ?
"dec.source" : method;
643 }
else if (method ==
"mna") {
649 }
else if (dispatch_detail::is_closed_model(L)) {
658 "SolverMAM: the mna method in SolverMAM does not support mixed models");
661 }
else if (method ==
"bgchain") {
668 }
else if (method ==
"ldqbd") {
671 }
else if (method ==
"inap" || method ==
"inaprc" || method ==
"inapinf"
672 || method ==
"exact") {
677 "SolverMAM: the '" + method +
"' method moved to SolverAG: RCAT decomposes the "
678 "model into cooperating agents rather than decomposing traffic, and no MAM "
679 "algorithm shares its machinery. Solve it with -s ag, or call "
680 "line::ag::solver_ag directly");
681 }
else if (method ==
"retrial") {
695 "SolverMAM: the retrial analyzer fits a phase-type to the arrival and "
696 "service processes and has no exact counterpart; use --arith double or real");
702 }
else if (method ==
"sync.faster" || method ==
"sync.slower") {
709 "SolverMAM: the sync methods uniformize the service law, whose coefficients need "
710 "exp; use --arith double or real");
715 }
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)
The (D0,D1) pair that reaches sn.proc.
SchedStrategy
Scheduling disciplines, with the values of MATLAB SchedStrategy.
@ BMMAPT
BMMAPT crosses the BATCH axis with the two above: a block is indexed by segment, mark and batch size,...
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)
Number of states of the background-chain CTMC solver_mam_bgchain would build on this model,...
mva::MvaSolution< T > solver_mam_bgchain(const qn::NetworkStruct< T > &L, const MamOptions &opt)
Port of solver_mam_bgchain.m.
MamSolution< T > mam_dispatch(const qn::NetworkStruct< T > &L, const MamOptions &opt_in)
The ladder.
bool mam_declares_reneging(const qn::NetworkStruct< T > &L)
True when some station-class pair declares RENEGING, with or without a patience law this analyzer can...
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...
MamRenegingInfo mam_reneging_detect(const qn::NetworkStruct< T > &L)
Port of mam_reneging_applicable.m: is this the MAP/M/s+G shape?
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.
mva::MvaSolution< T > solver_mam_sync(const qn::NetworkStruct< T > &L, const MamOptions &opt)
Port of solver_mam_sync.m: fills Q, U, R, T, C, X from qsys_mgck_sync.
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)
std::string mam_sync_refusal(const qn::NetworkStruct< T > &L, std::size_t nph, MamSyncParams< T > *par=nullptr)
mam_sync_applicable.m: why the sync methods cannot answer this model, or "" when they can,...
mva::MvaSolution< T > solver_mam_decmmap(const qn::NetworkStruct< T > &L, const MamOptions &opt)
Port of solver_mam.m.
std::string mam_reneging_default_refusal(const qn::NetworkStruct< T > &L)
The sentence supportsModelMethod('default') refuses a declared reneging with when the model is not th...
mva::MvaSolution< T > solver_mam_retrial(const qn::NetworkStruct< T > &L, const MamOptions &opt, const MamRetrialConfig &cfg=MamRetrialConfig())
Port of solver_mam_retrial.m.
mva::MvaSolution< T > solver_mam_reneging(const qn::NetworkStruct< T > &L, const MamOptions &opt, const MamRenegingInfo &info)
The reneging route 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.
Conservation laws of a layered queueing network, enumerated from its structure.
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.
The 'sync.faster' and 'sync.slower' methods of SolverMAM: the synchronous approximation of an M/G/c/K...
Port of solver_mna_open.m and solver_mna_closed.m, the two analyzers behind SolverMAM's mna method.
The <kind> part of a LINE:<category>:<kind> identifier (see _kb/17-error-vocabulary....
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.
std::string why
the reference's errorMsg, empty when ok
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