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) {
332 throw UnsupportedError(ErrorKind{
"Model"},
"SolverCTMC: node '" + sn.nodes[ind - 1].name +
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(ErrorKind{
"Model"},
"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_rtfun_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;
777 if (!
sn.transparam.empty())
779 "SolverCTMC: a WAITQ finite capacity region is not supported together with stochastic "
780 "Petri net transitions; a firing is atomic across its arcs and has no single "
781 "destination to park a refused token against");
782 if (
sn.has_fork() || !
sn.fj.empty())
784 "SolverCTMC: a WAITQ finite capacity region is not supported together with fork-join; "
785 "a forked task refused entry would park without its siblings, and the join has no rule "
786 "for a sibling that never arrived");
787 for (std::size_t i = 0; i <
sn.nodes.size(); ++i) {
789 for (std::size_t r = 0; r < nd.
routing.size(); ++r) {
800 "SolverCTMC: a WAITQ finite capacity region is not supported together with the "
801 "routing strategy at node '" +
803 "'; the generator evaluates PROB, RAND, RROBIN, WRROBIN, SDR, JSQ and SQ routing only");
806 for (std::size_t f = 0; f <
sn.regions.size(); ++f)
807 for (std::size_t r = 0; r <
sn.regions[f].rule.size(); ++r) {
809 if (d == DropStrategy::DROP || d == DropStrategy::WAITQ)
continue;
811 "SolverCTMC: finite capacity region " + std::to_string(f + 1) +
812 " applies a blocking rule other than DROP or WAITQ to class " +
813 std::to_string(r + 1) +
814 "; BAS, BBS and RSRD hold the job AT ITS SERVER, which needs a blocked-server "
815 "marker column in the node state that this port does not allocate");
825 for (std::size_t i = 0; i <
sn.droprule.size() && i <
sn.stations.size(); ++i)
826 for (std::size_t r = 0; r <
sn.droprule[i].size(); ++r) {
828 if (d != DropStrategy::BAS && d != DropStrategy::BBS && d != DropStrategy::RSRD)
831 "SolverCTMC: station '" +
sn.stations[i].name +
832 "' applies true blocking (BAS/BBS/RSRD) to class " + std::to_string(r + 1) +
833 ", which holds the completing job at its server; this port allocates no "
834 "blocked-server marker in the node state, and voiding the departure instead would "
835 "let the server re-serve the same job and understate throughput");
851 const std::size_t K =
sn.nclasses;
852 const std::size_t NF =
sn.stateful_nodes.size();
855 const std::vector<Sync<T>> sync = refresh_sync(
sn);
856 std::vector<waitq_detail::RegionCaps<T>> caps = waitq_detail::extract_caps(
sn);
857 const std::size_t F = caps.size();
858 const std::vector<std::size_t> cutoff = analyzer_detail::resolve_cutoff(
sn,
opt);
859 waitq_detail::resolve_lmax(
sn, cutoff, caps);
866 init.
net = waitq_detail::wide_init_state(
sn, cutoff);
867 init.
buf.assign(F, std::vector<std::size_t>());
868 for (std::size_t f = 0; f < F; ++f)
869 if (waitq_detail::violates(caps[f], waitq_detail::region_aggr(
sn, caps[f], init.
net)))
871 "SolverCTMC: the initial state violates the constraints of finite capacity region " +
872 std::to_string(f + 1) +
"; the region cannot hold the model's initial population");
874 std::vector<WaitqState<T>> states;
875 states.push_back(init);
876 std::map<std::vector<double>, std::size_t> index;
877 index[waitq_detail::waitq_key(init)] = 0;
878 std::deque<std::size_t> frontier;
879 frontier.push_back(0);
884 std::vector<std::size_t> ta, ti, tj;
886 std::vector<std::vector<std::vector<T>>> arv, dep;
887 arv.push_back(std::vector<std::vector<T>>(NF, std::vector<T>(K, zero)));
888 dep.push_back(std::vector<std::vector<T>>(NF, std::vector<T>(K, zero)));
892 const std::vector<double> key = waitq_detail::waitq_key(
ws);
893 const typename std::map<std::vector<double>, std::size_t>::const_iterator it =
895 if (it != index.end())
return it->second;
896 if (states.size() >=
opt.state_max)
898 "SolverCTMC: the WAITQ state space exceeds the cap of " +
899 std::to_string(
opt.state_max) +
900 " states; the region FIFO multiplies the space by its own occupancy");
901 states.push_back(
ws);
902 arv.push_back(std::vector<std::vector<T>>(NF, std::vector<T>(K, zero)));
903 dep.push_back(std::vector<std::vector<T>>(NF, std::vector<T>(K, zero)));
904 index[key] = states.size() - 1;
905 frontier.push_back(states.size() - 1);
906 return states.size() - 1;
914 std::vector<waitq_detail::Successor<T>> succ;
915 while (!frontier.empty()) {
916 const std::size_t s = frontier.front();
917 frontier.pop_front();
919 waitq_detail::waitq_successors(
sn, sync, caps,
ws, succ);
920 for (std::size_t e = 0; e < succ.size(); ++e) {
921 const waitq_detail::Successor<T>& su = succ[e];
922 const std::size_t dst = state_index(su.next);
923 ta.push_back(su.sync);
927 if (su.dep_isf != 0) dep[s][su.dep_isf - 1][su.dep_cls - 1] += su.w;
928 for (std::size_t q = 0; q < su.arv.size(); ++q)
929 arv[s][su.arv[q].first - 1][su.arv[q].second - 1] += su.w;
933 const std::size_t n = states.size();
936 res.
chain.space.reserve(n);
938 for (std::size_t s = 0; s < n; ++s) {
939 res.
chain.space.push_back(states[s].net);
940 res.
buf.push_back(states[s].buf);
942 res.
chain.arv_rates = arv;
943 res.
chain.dep_rates = dep;
944 if (
opt.keep_filtration) res.
chain.filt.assign(sync.size(),
Matrix<T>(n, n, zero));
945 for (std::size_t e = 0; e < tv.size(); ++e) {
946 res.
chain.Q(ti[e], tj[e]) += tv[e];
947 if (
opt.keep_filtration) res.
chain.filt[ta[e]](ti[e], tj[e]) += tv[e];
965 const std::vector<T>& pi) {
966 const std::size_t K =
sn.nclasses;
968 for (std::size_t s = 0; s < r.
buf.size() && s < pi.size(); ++s)
969 for (std::size_t f = 0; f < r.
buf[s].size(); ++f)
970 for (std::size_t j = 0; j < r.
buf[s][f].size(); ++j) {
971 const std::size_t cls = (r.
buf[s][f][j] - 1) % K + 1;
972 out[cls - 1] += pi[s];
981 std::vector<std::vector<std::vector<std::size_t>>>
buf;
1000 out.
sol.cutoff = analyzer_detail::resolve_cutoff(
sn,
opt);
1006 out.
sol.actualmethod =
opt.method;
1016 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)
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...
The <kind> part of a LINE:<category>:<kind> identifier (see _kb/17-error-vocabulary....
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.