5#ifndef LINE_SOLVERS_CTMC_SOLVER_CTMC_WAITQ_H
6#define LINE_SOLVERS_CTMC_SOLVER_CTMC_WAITQ_H
88 std::vector<std::vector<std::size_t>>
buf;
95 std::vector<std::vector<std::vector<std::size_t>>>
buf;
98namespace waitq_detail {
110 std::vector<bool> member;
111 std::vector<double> ccap;
113 double memcap = -1.0;
114 std::vector<double> size;
117 std::vector<bool> iswaitq;
118 std::size_t lmax = 0;
119 std::vector<std::size_t> tokbound;
127bool tok_full(
const RegionCaps<T>& cap,
const std::vector<std::size_t>& buf, std::size_t cls, std::size_t K) {
129 for (std::size_t t : buf)
130 if ((t - 1) % K + 1 == cls) ++n;
131 return n >= (cls - 1 < cap.tokbound.size() ? cap.tokbound[cls - 1] : 0);
135std::vector<RegionCaps<T>> extract_caps(
const NetworkStruct<T>& sn) {
136 const std::size_t M = sn.stations.size(), K = sn.nclasses;
137 std::vector<RegionCaps<T>> out;
138 out.reserve(sn.regions.size());
139 for (std::size_t f = 0; f < sn.regions.size(); ++f) {
140 const typename NetworkStruct<T>::Region& rg = sn.regions[f];
142 c.member.assign(M,
false);
143 c.ccap.assign(K, -1.0);
144 c.size.assign(K, 1.0);
145 c.iswaitq.assign(K,
false);
146 for (std::size_t i = 0; i < M; ++i) {
147 if (i >= rg.members.size() || !rg.members[i])
continue;
151 for (std::size_t k = 0; k < K && k < rg.cap[i].size(); ++k)
152 if (rg.cap[i][k] != -1.0)
153 c.ccap[k] = c.ccap[k] == -1.0 ? rg.cap[i][k] : std::min(c.ccap[k], rg.cap[i][k]);
154 if (K < rg.cap[i].size() && rg.cap[i][K] != -1.0)
155 c.gcap = c.gcap == -1.0 ? rg.cap[i][K] : std::min(c.gcap, rg.cap[i][K]);
156 if (i < rg.maxmem.size() && rg.maxmem[i] != -1.0)
157 c.memcap = c.memcap == -1.0 ? rg.maxmem[i] : std::min(c.memcap, rg.maxmem[i]);
159 for (std::size_t k = 0; k < K; ++k) {
160 if (k < rg.size.size()) c.size[k] = num_traits<T>::to_double(rg.size[k]);
161 c.iswaitq[k] = k < rg.rule.size() && rg.rule[k] != DropStrategy::DROP;
172bool violates(
const RegionCaps<T>& c,
const std::vector<double>& x) {
173 double total = 0, memory = 0;
174 for (std::size_t k = 0; k < x.size(); ++k) {
176 memory += x[k] * c.size[k];
177 if (c.ccap[k] != -1.0 && x[k] > c.ccap[k] + 1e-9)
return true;
179 if (c.gcap != -1.0 && total > c.gcap + 1e-9)
return true;
180 if (c.memcap != -1.0 && memory > c.memcap + 1e-9)
return true;
181 for (std::size_t row = 0; row < c.A.rows() && row < c.b.size(); ++row) {
183 for (std::size_t k = 0; k < x.size() && k < c.A.cols(); ++k)
184 lhs += num_traits<T>::to_double(c.A(row, k)) * x[k];
185 if (lhs > num_traits<T>::to_double(c.b[row]) + 1e-9)
return true;
199std::vector<double> region_aggr(
const NetworkStruct<T>& sn,
const RegionCaps<T>& c,
200 const NetState<T>& net) {
201 const std::size_t K = sn.nclasses;
202 std::vector<double> x(K, 0.0);
203 for (std::size_t i = 0; i < c.member.size(); ++i) {
204 if (!c.member[i])
continue;
205 const std::size_t ind = sn.node_of_station(i + 1);
206 const std::size_t isf = sn.stateful_index(ind);
207 if (isf == 0)
continue;
209 for (std::size_t k = 0; k < K && k < mg.second.size(); ++k) {
210 const double v = num_traits<T>::to_double(mg.second[k]);
214 if (std::isfinite(v)) x[k] += v;
222std::vector<double> waitq_key(
const WaitqState<T>& ws) {
223 std::vector<double> key = ctmc_detail::state_key(ws.net);
224 for (std::size_t f = 0; f < ws.buf.size(); ++f) {
226 for (std::size_t j = 0; j < ws.buf[f].size(); ++j)
227 key.push_back(
static_cast<double>(ws.buf[f][j]));
243void resolve_lmax(
const NetworkStruct<T>& sn,
const std::vector<std::size_t>& cutoff,
244 std::vector<RegionCaps<T>>& caps) {
245 const std::size_t K = sn.nclasses;
246 const std::vector<double> N = sn.njobs();
247 std::vector<std::size_t> tokbound(K, 0);
248 for (std::size_t r = 0; r < K; ++r) {
250 for (std::size_t f = 0; f < caps.size(); ++f)
251 if (caps[f].iswaitq[r]) any =
true;
255 for (std::size_t c = 0; c < sn.nchains; ++c) {
257 for (std::size_t a = 0; a < sn.inchain[c].size(); ++a)
258 if (sn.inchain[c][a] == r + 1) holds =
true;
259 if (!holds)
continue;
260 for (std::size_t a = 0; a < sn.inchain[c].size(); ++a) {
261 const double nj = N[sn.inchain[c][a] - 1];
262 if (std::isfinite(nj))
268 tokbound[r] = finite ?
static_cast<std::size_t
>(chainpop)
269 : (r < cutoff.size() ? cutoff[r] : 0);
271 for (std::size_t f = 0; f < caps.size(); ++f) {
273 for (std::size_t r = 0; r < K; ++r)
274 if (caps[f].iswaitq[r]) s += tokbound[r];
276 caps[f].tokbound = tokbound;
318std::size_t max_row_width(
const NetworkStruct<T>& sn, std::size_t ind,
319 const std::vector<std::size_t>& cutoff) {
320 const std::size_t R = sn.nclasses;
321 const std::size_t ist = sn.nodes[ind - 1].station;
322 std::vector<std::size_t> ph(R, 1);
324 for (std::size_t r = 0; r < R; ++r) ph[r] = sn.phasessz_of(ist, r + 1);
329 if (ist == 0 || sn.stations[ist - 1].nodetype == NodeType::Source) {
333 "' admits no state at all, so the WAITQ walk cannot start");
339 const std::vector<double> N = sn.njobs();
340 std::vector<std::size_t> bound(R, 0);
341 for (std::size_t r = 0; r < R; ++r) {
342 double b = std::isfinite(N[r]) ? N[r]
343 :
static_cast<double>(r < cutoff.size() ? cutoff[r] : 0);
344 const double cc = sn.classcap[ist - 1][r];
346 bound[r] = b > 0 ?
static_cast<std::size_t
>(b) : 0;
348 std::size_t total = 0;
349 for (std::size_t r = 0; r < R; ++r) total += bound[r];
350 const double tcap = sn.cap[ist - 1];
351 if (std::isfinite(tcap) && tcap <
static_cast<double>(total))
352 total = tcap > 0 ?
static_cast<std::size_t
>(tcap) : 0;
354 for (std::size_t t = total + 1; t-- > 0;) {
355 std::vector<std::size_t> n(R, 0);
356 std::size_t left = t;
357 for (std::size_t r = 0; r < R && left > 0; ++r) {
358 n[r] = std::min(left, bound[r]);
361 if (left > 0)
continue;
363 if (!rows.empty())
return rows[0].size();
365 throw UnsupportedError(
"SolverCTMC: station '" + sn.stations[ist - 1].name +
366 "' admits no state at all, so the WAITQ walk cannot start");
378NetState<T> wide_init_state(
const NetworkStruct<T>& sn,
379 const std::vector<std::size_t>& cutoff) {
381 if (!analyzer_detail::default_init_state(sn, init))
383 "SolverCTMC: the model's initial state admits no state at some node; check the class "
384 "populations against their reference stations");
385 for (std::size_t f = 0; f < sn.stateful_nodes.size() && f < init.local.size(); ++f) {
386 const std::size_t w = max_row_width(sn, sn.stateful_nodes[f], cutoff);
387 if (init.local[f].size() >= w)
continue;
388 init.local[f].insert(init.local[f].begin(), w - init.local[f].size(),
389 num_traits<T>::from_int(0));
405 std::vector<std::vector<std::size_t>> buf;
406 T w = num_traits<T>::from_int(0);
407 std::vector<std::pair<std::size_t, std::size_t>> arv;
408 std::size_t dep_isf = 0, dep_cls = 0;
410 std::size_t pcls = 0, pdest = 0;
418 std::vector<std::vector<std::size_t>> buf;
420 std::vector<std::pair<std::size_t, std::size_t>> arv;
421 std::size_t dep_isf = 0, dep_cls = 0;
422 std::size_t pf = 0, pcls = 0, pdest = 0;
437 std::size_t sync = 0;
439 T w = num_traits<T>::from_int(0);
440 std::vector<std::pair<std::size_t, std::size_t>> arv;
441 std::size_t dep_isf = 0, dep_cls = 0;
459void waitq_successors(
const NetworkStruct<T>& sn,
const std::vector<Sync<T>>& sync,
460 const std::vector<RegionCaps<T>>& caps,
const WaitqState<T>& ws,
461 std::vector<Successor<T>>& out) {
462 const std::size_t K = sn.nclasses;
463 const std::size_t F = caps.size();
464 const std::size_t local = sn.nodes.size() + 1;
465 const NetState<T>& net = ws.net;
466 const std::vector<std::vector<std::size_t>>& buf = ws.buf;
469 std::vector<std::vector<double>> xf(F);
470 for (std::size_t f = 0; f < F; ++f) xf[f] = region_aggr(sn, caps[f], net);
478 const bool sdr = sn.has_sdr_routing();
490 const auto emit = [&](std::size_t a,
const Emission<T>& e) {
491 if (num_traits<T>::to_double(e.w) <= 0)
return;
492 std::deque<Work<T>> work;
496 w0.prob = num_traits<T>::from_int(1);
498 w0.dep_isf = e.dep_isf;
499 w0.dep_cls = e.dep_cls;
503 w0.pwaitq = e.pwaitq;
506 while (!work.empty()) {
507 const Work<T> it = work.front();
510 bool progressed =
false;
511 for (std::size_t f = 0; f < F && !progressed; ++f) {
512 if (it.buf[f].empty())
continue;
513 const std::size_t tok = it.buf[f][0];
514 const std::size_t dest = (tok - 1) / K + 1;
515 const std::size_t r = (tok - 1) % K + 1;
516 std::vector<double> x = region_aggr(sn, caps[f], it.net);
520 if (violates(caps[f], x))
continue;
521 const std::size_t isf_d = sn.stateful_index(dest);
522 if (isf_d == 0)
continue;
523 const qn::EventOutcome<T> od =
525 if (od.space.empty())
continue;
526 for (std::size_t
id = 0;
id < od.space.size(); ++id) {
527 if (num_traits<T>::to_double(od.prob[
id]) <= 0)
continue;
529 nx.net.local[isf_d - 1] = od.space[id];
530 nx.buf[f].erase(nx.buf[f].begin());
531 nx.prob = T(it.prob * od.prob[
id]);
532 nx.arv.push_back(std::make_pair(isf_d, r));
537 if (progressed)
continue;
543 const std::size_t f = it.pf - 1;
544 std::vector<double> x = region_aggr(sn, caps[f], it.net);
545 x[it.pcls - 1] += 1.0;
548 if (violates(caps[f], x)) {
552 if (tok_full(caps[f], nx.buf[f], it.pcls, K))
continue;
553 nx.buf[f].push_back((it.pdest - 1) * K + it.pcls);
558 const std::size_t isf_d = sn.stateful_index(it.pdest);
559 bool admitted =
false;
562 sn, it.pdest, it.net.local[isf_d - 1], EventType::ARV, it.pcls);
563 for (std::size_t
id = 0;
id < od.space.size(); ++id) {
564 if (num_traits<T>::to_double(od.prob[
id]) <= 0)
continue;
566 ny.net.local[isf_d - 1] = od.space[id];
567 ny.prob = T(it.prob * od.prob[
id]);
568 ny.arv.push_back(std::make_pair(isf_d, it.pcls));
577 if (tok_full(caps[f], nx.buf[f], it.pcls, K))
continue;
578 nx.buf[f].push_back((it.pdest - 1) * K + it.pcls);
584 const T contrib = T(e.w * it.prob);
585 if (num_traits<T>::to_double(contrib) <= 0)
continue;
592 s.dep_isf = it.dep_isf;
593 s.dep_cls = it.dep_cls;
598 for (std::size_t a = 0; a < sync.size(); ++a) {
599 const Sync<T>& sy = sync[a];
600 const std::size_t node_a = sy.active.node;
601 const std::size_t isf_a = sn.stateful_index(node_a);
602 if (isf_a == 0)
continue;
603 const std::size_t cls_a = sy.active.cls;
604 const std::size_t stat_a = sn.nodes[node_a - 1].station;
609 const qn::EventOutcome<T> oa =
610 qn::after_event(sn, node_a, net.local[isf_a - 1], sy.active.event, cls_a,
613 const bool rr_here = sy.active.event == EventType::DEP && sn.rr_var_slot(node_a, cls_a) != 0;
614 for (std::size_t ia = 0; ia < oa.space.size(); ++ia) {
616 const T rate = T(oa.rate[ia] * oa.prob[ia]);
617 if (num_traits<T>::to_double(rate) <= 0)
continue;
619 const std::size_t node_p = sy.passive.node;
620 if (node_p == local) {
623 e.net.local[isf_a - 1] = oa.space[ia];
629 const std::size_t isf_p = sn.stateful_index(node_p);
630 if (isf_p == 0)
continue;
631 const std::size_t cls_p = sy.passive.cls;
632 const std::size_t stat_p = sn.nodes[node_p - 1].station;
636 T proute = sy.passive.statedep ? rt_now(sy.passive.rt_row, sy.passive.rt_col) : sy.passive.prob;
638 const std::size_t nv = sn.nvars_of(node_a);
639 const std::vector<T>& arow = oa.space[ia];
640 std::size_t dest = 0;
641 if (arow.size() >= nv) {
642 const std::vector<T> var(arow.end() - nv, arow.end());
643 dest = sn.rr_dest(node_a, cls_a, var);
645 proute = (dest == node_p && sy.passive.cls == cls_a) ? num_traits<T>::from_int(1)
646 : num_traits<T>::from_int(0);
648 const T w = T(rate * proute);
649 if (num_traits<T>::to_double(w) <= 0)
continue;
650 const std::size_t dep_isf = sy.active.event == EventType::DEP ? isf_a : 0;
655 std::size_t blockedf = 0, droppedf = 0;
656 if (sy.passive.event == EventType::ARV && stat_p != 0) {
657 for (std::size_t f = 0; f < F; ++f) {
658 if (!caps[f].member[stat_p - 1])
continue;
659 if (stat_a != 0 && caps[f].member[stat_a - 1])
continue;
660 std::vector<double> xn = xf[f];
661 xn[cls_p - 1] += 1.0;
662 if (!violates(caps[f], xn))
continue;
663 if (caps[f].iswaitq[cls_p - 1])
677 e.net.local[isf_a - 1] = oa.space[ia];
688 std::size_t switchf = 0;
689 if (blockedf == 0 && sy.passive.event == EventType::ARV && cls_p != cls_a &&
690 stat_a != 0 && stat_p != 0)
691 for (std::size_t f = 0; f < F; ++f)
692 if (caps[f].member[stat_a - 1] && caps[f].member[stat_p - 1]) {
700 e.net.local[isf_a - 1] = oa.space[ia];
708 e.pwaitq = caps[switchf - 1].iswaitq[cls_p - 1];
718 if (tok_full(caps[blockedf - 1], buf[blockedf - 1], cls_p, K))
continue;
721 e.net.local[isf_a - 1] = oa.space[ia];
723 e.buf[blockedf - 1].push_back((node_p - 1) * K + cls_p);
731 const std::vector<T>& psrc = node_p == node_a ? oa.space[ia] : net.local[isf_p - 1];
732 const qn::EventOutcome<T> op =
qn::after_event(sn, node_p, psrc, sy.passive.event, cls_p);
738 for (std::size_t ip = 0; ip < op.space.size(); ++ip) {
739 if (num_traits<T>::to_double(op.prob[ip]) <= 0)
continue;
742 e.net.local[isf_a - 1] = oa.space[ia];
743 e.net.local[isf_p - 1] = op.space[ip];
745 e.w = T(w * op.prob[ip]);
748 if (sy.passive.event == EventType::ARV)
749 e.arv.push_back(std::make_pair(isf_p, cls_p));
761 for (std::size_t f = 0; f <
sn.regions.size(); ++f)
762 for (std::size_t r = 0; r <
sn.regions[f].rule.size(); ++r)
763 if (
sn.regions[f].rule[r] != DropStrategy::DROP)
return true;
778 if (!
sn.transparam.empty())
780 "SolverCTMC: a WAITQ finite capacity region is not supported together with stochastic "
781 "Petri net transitions; a firing is atomic across its arcs and has no single "
782 "destination to park a refused token against");
783 if (
sn.has_fork() || !
sn.fj.empty())
785 "SolverCTMC: a WAITQ finite capacity region is not supported together with fork-join; "
786 "a forked task refused entry would park without its siblings, and the join has no rule "
787 "for a sibling that never arrived");
788 for (std::size_t i = 0; i <
sn.nodes.size(); ++i) {
790 for (std::size_t r = 0; r < nd.
routing.size(); ++r) {
799 "SolverCTMC: a WAITQ finite capacity region is not supported together with the "
800 "routing strategy at node '" +
802 "'; the generator evaluates PROB, RAND, RROBIN, WRROBIN and SDR routing only");
805 for (std::size_t f = 0; f <
sn.regions.size(); ++f)
806 for (std::size_t r = 0; r <
sn.regions[f].rule.size(); ++r) {
808 if (d == DropStrategy::DROP || d == DropStrategy::WAITQ)
continue;
810 "SolverCTMC: finite capacity region " + std::to_string(f + 1) +
811 " applies a blocking rule other than DROP or WAITQ to class " +
812 std::to_string(r + 1) +
813 "; BAS, BBS and RSRD hold the job AT ITS SERVER, which needs a blocked-server "
814 "marker column in the node state that this port does not allocate");
824 for (std::size_t i = 0; i <
sn.droprule.size() && i <
sn.stations.size(); ++i)
825 for (std::size_t r = 0; r <
sn.droprule[i].size(); ++r) {
827 if (d != DropStrategy::BAS && d != DropStrategy::BBS && d != DropStrategy::RSRD)
830 "SolverCTMC: station '" +
sn.stations[i].name +
831 "' applies true blocking (BAS/BBS/RSRD) to class " + std::to_string(r + 1) +
832 ", which holds the completing job at its server; this port allocates no "
833 "blocked-server marker in the node state, and voiding the departure instead would "
834 "let the server re-serve the same job and understate throughput");
850 const std::size_t K =
sn.nclasses;
851 const std::size_t NF =
sn.stateful_nodes.size();
854 const std::vector<Sync<T>> sync = refresh_sync(
sn);
855 std::vector<waitq_detail::RegionCaps<T>> caps = waitq_detail::extract_caps(
sn);
856 const std::size_t F = caps.size();
857 const std::vector<std::size_t> cutoff = analyzer_detail::resolve_cutoff(
sn,
opt);
858 waitq_detail::resolve_lmax(
sn, cutoff, caps);
865 init.
net = waitq_detail::wide_init_state(
sn, cutoff);
866 init.
buf.assign(F, std::vector<std::size_t>());
867 for (std::size_t f = 0; f < F; ++f)
868 if (waitq_detail::violates(caps[f], waitq_detail::region_aggr(
sn, caps[f], init.
net)))
870 "SolverCTMC: the initial state violates the constraints of finite capacity region " +
871 std::to_string(f + 1) +
"; the region cannot hold the model's initial population");
873 std::vector<WaitqState<T>> states;
874 states.push_back(init);
875 std::map<std::vector<double>, std::size_t> index;
876 index[waitq_detail::waitq_key(init)] = 0;
877 std::deque<std::size_t> frontier;
878 frontier.push_back(0);
883 std::vector<std::size_t> ta, ti, tj;
885 std::vector<std::vector<std::vector<T>>> arv, dep;
886 arv.push_back(std::vector<std::vector<T>>(NF, std::vector<T>(K, zero)));
887 dep.push_back(std::vector<std::vector<T>>(NF, std::vector<T>(K, zero)));
891 const std::vector<double> key = waitq_detail::waitq_key(
ws);
892 const typename std::map<std::vector<double>, std::size_t>::const_iterator it =
894 if (it != index.end())
return it->second;
895 if (states.size() >=
opt.state_max)
897 "SolverCTMC: the WAITQ state space exceeds the cap of " +
898 std::to_string(
opt.state_max) +
899 " states; the region FIFO multiplies the space by its own occupancy");
900 states.push_back(
ws);
901 arv.push_back(std::vector<std::vector<T>>(NF, std::vector<T>(K, zero)));
902 dep.push_back(std::vector<std::vector<T>>(NF, std::vector<T>(K, zero)));
903 index[key] = states.size() - 1;
904 frontier.push_back(states.size() - 1);
905 return states.size() - 1;
913 std::vector<waitq_detail::Successor<T>> succ;
914 while (!frontier.empty()) {
915 const std::size_t s = frontier.front();
916 frontier.pop_front();
918 waitq_detail::waitq_successors(
sn, sync, caps,
ws, succ);
919 for (std::size_t e = 0; e < succ.size(); ++e) {
920 const waitq_detail::Successor<T>& su = succ[e];
921 const std::size_t dst = state_index(su.next);
922 ta.push_back(su.sync);
926 if (su.dep_isf != 0) dep[s][su.dep_isf - 1][su.dep_cls - 1] += su.w;
927 for (std::size_t q = 0; q < su.arv.size(); ++q)
928 arv[s][su.arv[q].first - 1][su.arv[q].second - 1] += su.w;
932 const std::size_t n = states.size();
935 res.
chain.space.reserve(n);
937 for (std::size_t s = 0; s < n; ++s) {
938 res.
chain.space.push_back(states[s].net);
939 res.
buf.push_back(states[s].buf);
941 res.
chain.arv_rates = arv;
942 res.
chain.dep_rates = dep;
943 if (
opt.keep_filtration) res.
chain.filt.assign(sync.size(),
Matrix<T>(n, n, zero));
944 for (std::size_t e = 0; e < tv.size(); ++e) {
945 res.
chain.Q(ti[e], tj[e]) += tv[e];
946 if (
opt.keep_filtration) res.
chain.filt[ta[e]](ti[e], tj[e]) += tv[e];
964 const std::vector<T>& pi) {
965 const std::size_t K =
sn.nclasses;
967 for (std::size_t s = 0; s < r.
buf.size() && s < pi.size(); ++s)
968 for (std::size_t f = 0; f < r.
buf[s].size(); ++f)
969 for (std::size_t j = 0; j < r.
buf[s][f].size(); ++j) {
970 const std::size_t cls = (r.
buf[s][f][j] - 1) % K + 1;
971 out[cls - 1] += pi[s];
980 std::vector<std::vector<std::vector<std::size_t>>>
buf;
999 out.
sol.cutoff = analyzer_detail::resolve_cutoff(
sn,
opt);
1005 out.
sol.actualmethod =
opt.method;
1015 std::vector<std::vector<std::vector<std::size_t>>>
buf;
UnsupportedError(const std::string &what)
A network plus its refreshed NetworkStruct.
Steady-state distribution of a continuous-time Markov chain.
Port of matlab/src/solvers/CTMC/ctmc_stationary.m: the single entry point for the stationary distribu...
The exception types the port throws.
Dense matrix and non-owning view.
void check_method(const std::string &method)
Port of runAnalyzerChecks' method gate.
void ctmc_check_support(const NetworkStruct< T > &sn)
Refuse the constructs this port generates a chain for but does not MODEL.
void make_infgen(Matrix< T > &Q)
Port of ctmc_makeinfgen: turn an off-diagonal rate matrix into a generator.
mva::AvgResult< T > solver_ctmc_avg_table(const NetworkStruct< T > &sn, const CtmcSolution< T > &d, const std::string &method)
Port of @@SolverCTMC/runAnalyzer.m's result assembly: solve, then apply the metric filter @@NetworkSo...
CtmcAvg< T > solver_ctmc_avg_from_pi(const NetworkStruct< T > &sn, const CtmcResult< T > &r, const std::vector< T > &pivec, bool stationary=true)
Port of solver_ctmc_avg_from_pi: map a state distribution to mean metrics.
CtmcSolution< T > solver_ctmc_analyzer(const NetworkStruct< T > &sn_in, const CtmcOptions &opt)
Port of solver_ctmc_analyzer.m plus the fork-join wrapper of @@SolverCTMC/runAnalyzer....
CtmcAnySolution< T > solver_ctmc_analyzer_any(const NetworkStruct< T > &sn, const CtmcOptions &opt)
The entry point a caller who does not know which path a model needs should use: pick the WAITQ walk w...
CtmcStationaryResult< T > ctmc_stationary(const Matrix< T > &Q, std::size_t init_index=static_cast< std::size_t >(-1))
std::vector< T > ctmc_waitq_parked(const NetworkStruct< T > &sn, const WaitqResult< T > &r, const std::vector< T > &pi)
The mean number of parked jobs per class, over a stationary law.
WaitqSolution< T > solver_ctmc_waitq_analyzer(const NetworkStruct< T > &sn, const CtmcOptions &opt)
Build the WAITQ chain, solve it, and map it onto the same means every other CTMC path reports.
WaitqResult< T > solver_ctmc_waitq(const NetworkStruct< T > &sn, const CtmcOptions &opt)
Port of the reachability walk of solver_ctmc_fcr_waitq.m.
bool ctmc_has_waitq_region(const NetworkStruct< T > &sn)
True when the model declares a region that applies anything other than DROP.
mva::AvgResult< T > solver_ctmc_run_analyzer_any(const NetworkStruct< T > &sn, const CtmcOptions &opt)
Solve on whichever path applies and format, mirroring solver_ctmc_run_analyzer.
void ctmc_check_waitq_support(const NetworkStruct< T > &sn)
The combinations the reference gates, plus the two this port cannot represent.
DropStrategy
Blocking and loss rules, with the values of MATLAB DropStrategy.
RoutingStrategy
Routing strategies, with the values of MATLAB RoutingStrategy.
@ SDR
Krzesinski (1987) product-form state-dependent routing.
std::vector< std::vector< T > > from_marginal_node(const NetworkStruct< T > &sn, std::size_t ind, const std::vector< std::size_t > &n, const std::vector< std::size_t > &phases)
Port of State.fromMarginal at its OWN signature: the reference indexes by NODE, not by station,...
bool from_marginal_node_first(const NetworkStruct< T > &sn, std::size_t ind, const std::vector< std::size_t > &n, const std::vector< std::size_t > &phases, std::vector< T > &out)
The FIRST row from_marginal_node emits, BUILT rather than enumerated.
bool immfeed_self_loop(const NetworkStruct< T > &sn, const Sync< T > &sy)
True when a synchronization is an IMMEDIATE-FEEDBACK SELF-LOOP: a departure whose passive half is an ...
FeatureSet ctmc_feature_set(const std::string &method)
SolverCTMC.getFeatureSet, the reference's 104 MATLAB names in full.
EventOutcome< T > after_event(const NetworkStruct< T > &sn, std::size_t ind, const std::vector< T > &inspace, EventType event, std::size_t cls, bool no_promote=false, const T &aux_rate=num_traits< T >::from_int(0))
Port of State.afterEvent: the successors of one event at one NODE.
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::pair< T, std::vector< T > > to_marginal_aggr(const NetworkStruct< T > &sn, std::size_t ind, const std::vector< T > &state_i)
Port of State.toMarginalAggr: the job counts of one node's state row, without the per-phase detail to...
Matrix< T > rt_state(const NetworkStruct< T > &sn, const std::vector< std::vector< T > > &local)
Port of sn.rtfun: the routing over the stateful nodes AT ONE STATE.
Conservation laws of a layered queueing network, enumerated from its structure.
A queueing network and its refreshed NetworkStruct.
Port of solver_ctmc.m: the infinitesimal generator of a queueing network, assembled from the enumerat...
Port of solver_ctmc_analyzer.m and the parts of @@SolverCTMC/runAnalyzer.m that surround one solve: t...
Finite Capacity Regions in SolverCTMC: the DROP rule, as a filter on the enumerated state space,...
The DECLARED side of the gate: one feature set per solver.
Port of the MATLAB +State package: the encoding that turns a station's state row into marginal job co...
Port of the event half of MATLAB's +State package: the successor states an event produces at one node...
A CTMC solve routed to whichever path the model's region rules require.
std::vector< T > parked
mean parked jobs per class; empty off the WAITQ path
std::vector< std::vector< std::vector< std::size_t > > > buf
empty off the WAITQ path
The SolverCTMC knobs this port honours.
The generator, the state space it is indexed by, and the event rates.
Everything one CTMC solve produces.
The chain the WAITQ walk produces, alongside the FIFOs its states carry.
std::vector< std::vector< std::vector< std::size_t > > > buf
per state, per region
CtmcResult< T > chain
Q, the net halves, rates, filt.
A solved WAITQ model: the usual CTMC solution, plus what the FIFOs hold.
std::vector< std::vector< std::vector< std::size_t > > > buf
std::vector< T > parked
mean parked jobs per class
One augmented state: the network state, plus the token FIFO of every region.
std::vector< std::vector< std::size_t > > buf
The metrics getAvg returns, after filtering.
One network state: the per-stateful-node local rows it is composed of.
std::vector< RoutingStrategy > routing
sn.routing, per class.