5#ifndef LINE_SOLVERS_CTMC_SOLVER_CTMC_WAITQ_H
6#define LINE_SOLVERS_CTMC_SOLVER_CTMC_WAITQ_H
87 std::vector<std::vector<std::size_t>>
buf;
94 std::vector<std::vector<std::vector<std::size_t>>>
buf;
97namespace waitq_detail {
109 std::vector<bool> member;
110 std::vector<double> ccap;
112 double memcap = -1.0;
113 std::vector<double> size;
116 std::vector<bool> iswaitq;
117 std::size_t lmax = 0;
121std::vector<RegionCaps<T>> extract_caps(
const NetworkStruct<T>&
sn) {
122 const std::size_t M =
sn.stations.size(), K =
sn.nclasses;
123 std::vector<RegionCaps<T>> out;
124 out.reserve(
sn.regions.size());
125 for (std::size_t f = 0; f <
sn.regions.size(); ++f) {
126 const typename NetworkStruct<T>::Region& rg =
sn.regions[f];
128 c.member.assign(M,
false);
129 c.ccap.assign(K, -1.0);
130 c.size.assign(K, 1.0);
131 c.iswaitq.assign(K,
false);
132 for (std::size_t i = 0; i < M; ++i) {
133 if (i >= rg.members.size() || !rg.members[i])
continue;
137 for (std::size_t k = 0; k < K && k < rg.cap[i].size(); ++k)
138 if (rg.cap[i][k] != -1.0)
139 c.ccap[k] = c.ccap[k] == -1.0 ? rg.cap[i][k] : std::min(c.ccap[k], rg.cap[i][k]);
140 if (K < rg.cap[i].size() && rg.cap[i][K] != -1.0)
141 c.gcap = c.gcap == -1.0 ? rg.cap[i][K] : std::min(c.gcap, rg.cap[i][K]);
142 if (i < rg.maxmem.size() && rg.maxmem[i] != -1.0)
143 c.memcap = c.memcap == -1.0 ? rg.maxmem[i] : std::min(c.memcap, rg.maxmem[i]);
145 for (std::size_t k = 0; k < K; ++k) {
146 if (k < rg.size.size()) c.size[k] = num_traits<T>::to_double(rg.size[k]);
147 c.iswaitq[k] = k < rg.rule.size() && rg.rule[k] != DropStrategy::DROP;
158bool violates(
const RegionCaps<T>& c,
const std::vector<double>& x) {
159 double total = 0, memory = 0;
160 for (std::size_t k = 0; k < x.size(); ++k) {
162 memory += x[k] * c.size[k];
163 if (c.ccap[k] != -1.0 && x[k] > c.ccap[k] + 1e-9)
return true;
165 if (c.gcap != -1.0 && total > c.gcap + 1e-9)
return true;
166 if (c.memcap != -1.0 && memory > c.memcap + 1e-9)
return true;
167 for (std::size_t row = 0; row < c.A.rows() && row < c.b.size(); ++row) {
169 for (std::size_t k = 0; k < x.size() && k < c.A.cols(); ++k)
170 lhs += num_traits<T>::to_double(c.A(row, k)) * x[k];
171 if (lhs > num_traits<T>::to_double(c.b[row]) + 1e-9)
return true;
185std::vector<double> region_aggr(
const NetworkStruct<T>& sn,
const RegionCaps<T>& c,
186 const NetState<T>& net) {
187 const std::size_t K = sn.nclasses;
188 std::vector<double> x(K, 0.0);
189 for (std::size_t i = 0; i < c.member.size(); ++i) {
190 if (!c.member[i])
continue;
191 const std::size_t ind = sn.node_of_station(i + 1);
192 const std::size_t isf = sn.stateful_index(ind);
193 if (isf == 0)
continue;
195 for (std::size_t k = 0; k < K && k < mg.second.size(); ++k) {
196 const double v = num_traits<T>::to_double(mg.second[k]);
200 if (std::isfinite(v)) x[k] += v;
208std::vector<double> waitq_key(
const WaitqState<T>& ws) {
209 std::vector<double> key = ctmc_detail::state_key(ws.net);
210 for (std::size_t f = 0; f < ws.buf.size(); ++f) {
212 for (std::size_t j = 0; j < ws.buf[f].size(); ++j)
213 key.push_back(
static_cast<double>(ws.buf[f][j]));
229void resolve_lmax(
const NetworkStruct<T>& sn,
const std::vector<std::size_t>& cutoff,
230 std::vector<RegionCaps<T>>& caps) {
231 const std::size_t K = sn.nclasses;
232 const std::vector<double> N = sn.njobs();
233 std::vector<std::size_t> tokbound(K, 0);
234 for (std::size_t r = 0; r < K; ++r) {
236 for (std::size_t f = 0; f < caps.size(); ++f)
237 if (caps[f].iswaitq[r]) any =
true;
241 for (std::size_t c = 0; c < sn.nchains; ++c) {
243 for (std::size_t a = 0; a < sn.inchain[c].size(); ++a)
244 if (sn.inchain[c][a] == r + 1) holds =
true;
245 if (!holds)
continue;
246 for (std::size_t a = 0; a < sn.inchain[c].size(); ++a) {
247 const double nj = N[sn.inchain[c][a] - 1];
248 if (std::isfinite(nj))
254 tokbound[r] = finite ?
static_cast<std::size_t
>(chainpop)
255 : (r < cutoff.size() ? cutoff[r] : 0);
257 for (std::size_t f = 0; f < caps.size(); ++f) {
259 for (std::size_t r = 0; r < K; ++r)
260 if (caps[f].iswaitq[r]) s += tokbound[r];
303std::size_t max_row_width(
const NetworkStruct<T>& sn, std::size_t ind,
304 const std::vector<std::size_t>& cutoff) {
305 const std::size_t R = sn.nclasses;
306 const std::size_t ist = sn.nodes[ind - 1].station;
307 std::vector<std::size_t> ph(R, 1);
309 for (std::size_t r = 0; r < R; ++r) ph[r] = sn.phasessz_of(ist, r + 1);
314 if (ist == 0 || sn.stations[ist - 1].nodetype == NodeType::Source) {
318 "' admits no state at all, so the WAITQ walk cannot start");
324 const std::vector<double> N = sn.njobs();
325 std::vector<std::size_t> bound(R, 0);
326 for (std::size_t r = 0; r < R; ++r) {
327 double b = std::isfinite(N[r]) ? N[r]
328 :
static_cast<double>(r < cutoff.size() ? cutoff[r] : 0);
329 const double cc = sn.classcap[ist - 1][r];
331 bound[r] = b > 0 ?
static_cast<std::size_t
>(b) : 0;
333 std::size_t total = 0;
334 for (std::size_t r = 0; r < R; ++r) total += bound[r];
335 const double tcap = sn.cap[ist - 1];
336 if (std::isfinite(tcap) && tcap <
static_cast<double>(total))
337 total = tcap > 0 ?
static_cast<std::size_t
>(tcap) : 0;
339 for (std::size_t t = total + 1; t-- > 0;) {
340 std::vector<std::size_t> n(R, 0);
341 std::size_t left = t;
342 for (std::size_t r = 0; r < R && left > 0; ++r) {
343 n[r] = std::min(left, bound[r]);
346 if (left > 0)
continue;
348 if (!rows.empty())
return rows[0].size();
350 throw UnsupportedError(
"SolverCTMC: station '" + sn.stations[ist - 1].name +
351 "' admits no state at all, so the WAITQ walk cannot start");
363NetState<T> wide_init_state(
const NetworkStruct<T>& sn,
364 const std::vector<std::size_t>& cutoff) {
366 if (!analyzer_detail::default_init_state(sn, init))
368 "SolverCTMC: the model's initial state admits no state at some node; check the class "
369 "populations against their reference stations");
370 for (std::size_t f = 0; f < sn.stateful_nodes.size() && f < init.local.size(); ++f) {
371 const std::size_t w = max_row_width(sn, sn.stateful_nodes[f], cutoff);
372 if (init.local[f].size() >= w)
continue;
373 init.local[f].insert(init.local[f].begin(), w - init.local[f].size(),
374 num_traits<T>::from_int(0));
390 std::vector<std::vector<std::size_t>> buf;
391 T w = num_traits<T>::from_int(0);
392 std::vector<std::pair<std::size_t, std::size_t>> arv;
393 std::size_t dep_isf = 0, dep_cls = 0;
395 std::size_t pcls = 0, pdest = 0;
403 std::vector<std::vector<std::size_t>> buf;
405 std::vector<std::pair<std::size_t, std::size_t>> arv;
406 std::size_t dep_isf = 0, dep_cls = 0;
407 std::size_t pf = 0, pcls = 0, pdest = 0;
422 std::size_t sync = 0;
424 T w = num_traits<T>::from_int(0);
425 std::vector<std::pair<std::size_t, std::size_t>> arv;
426 std::size_t dep_isf = 0, dep_cls = 0;
444void waitq_successors(
const NetworkStruct<T>& sn,
const std::vector<Sync<T>>& sync,
445 const std::vector<RegionCaps<T>>& caps,
const WaitqState<T>& ws,
446 std::vector<Successor<T>>& out) {
447 const std::size_t K = sn.nclasses;
448 const std::size_t F = caps.size();
449 const std::size_t local = sn.nodes.size() + 1;
450 const NetState<T>& net = ws.net;
451 const std::vector<std::vector<std::size_t>>& buf = ws.buf;
454 std::vector<std::vector<double>> xf(F);
455 for (std::size_t f = 0; f < F; ++f) xf[f] = region_aggr(sn, caps[f], net);
463 const bool sdr = sn.has_sdr_routing();
475 const auto emit = [&](std::size_t a,
const Emission<T>& e) {
476 if (num_traits<T>::to_double(e.w) <= 0)
return;
477 std::deque<Work<T>> work;
481 w0.prob = num_traits<T>::from_int(1);
483 w0.dep_isf = e.dep_isf;
484 w0.dep_cls = e.dep_cls;
488 w0.pwaitq = e.pwaitq;
491 while (!work.empty()) {
492 const Work<T> it = work.front();
495 bool progressed =
false;
496 for (std::size_t f = 0; f < F && !progressed; ++f) {
497 if (it.buf[f].empty())
continue;
498 const std::size_t tok = it.buf[f][0];
499 const std::size_t dest = (tok - 1) / K + 1;
500 const std::size_t r = (tok - 1) % K + 1;
501 std::vector<double> x = region_aggr(sn, caps[f], it.net);
505 if (violates(caps[f], x))
continue;
506 const std::size_t isf_d = sn.stateful_index(dest);
507 if (isf_d == 0)
continue;
508 const qn::EventOutcome<T> od =
510 if (od.space.empty())
continue;
511 for (std::size_t
id = 0;
id < od.space.size(); ++id) {
512 if (num_traits<T>::to_double(od.prob[
id]) <= 0)
continue;
514 nx.net.local[isf_d - 1] = od.space[id];
515 nx.buf[f].erase(nx.buf[f].begin());
516 nx.prob = T(it.prob * od.prob[
id]);
517 nx.arv.push_back(std::make_pair(isf_d, r));
522 if (progressed)
continue;
528 const std::size_t f = it.pf - 1;
529 std::vector<double> x = region_aggr(sn, caps[f], it.net);
530 x[it.pcls - 1] += 1.0;
533 if (violates(caps[f], x)) {
537 if (nx.buf[f].size() >= caps[f].lmax)
continue;
538 nx.buf[f].push_back((it.pdest - 1) * K + it.pcls);
543 const std::size_t isf_d = sn.stateful_index(it.pdest);
544 bool admitted =
false;
547 sn, it.pdest, it.net.local[isf_d - 1], EventType::ARV, it.pcls);
548 for (std::size_t
id = 0;
id < od.space.size(); ++id) {
549 if (num_traits<T>::to_double(od.prob[
id]) <= 0)
continue;
551 ny.net.local[isf_d - 1] = od.space[id];
552 ny.prob = T(it.prob * od.prob[
id]);
553 ny.arv.push_back(std::make_pair(isf_d, it.pcls));
562 if (nx.buf[f].size() >= caps[f].lmax)
continue;
563 nx.buf[f].push_back((it.pdest - 1) * K + it.pcls);
569 const T contrib = T(e.w * it.prob);
570 if (num_traits<T>::to_double(contrib) <= 0)
continue;
577 s.dep_isf = it.dep_isf;
578 s.dep_cls = it.dep_cls;
583 for (std::size_t a = 0; a < sync.size(); ++a) {
584 const Sync<T>& sy = sync[a];
585 const std::size_t node_a = sy.active.node;
586 const std::size_t isf_a = sn.stateful_index(node_a);
587 if (isf_a == 0)
continue;
588 const std::size_t cls_a = sy.active.cls;
589 const std::size_t stat_a = sn.nodes[node_a - 1].station;
591 const qn::EventOutcome<T> oa =
592 qn::after_event(sn, node_a, net.local[isf_a - 1], sy.active.event, cls_a);
593 for (std::size_t ia = 0; ia < oa.space.size(); ++ia) {
594 const T rate = oa.rate[ia];
595 if (num_traits<T>::to_double(rate) <= 0)
continue;
597 const std::size_t node_p = sy.passive.node;
598 if (node_p == local) {
601 e.net.local[isf_a - 1] = oa.space[ia];
607 const std::size_t isf_p = sn.stateful_index(node_p);
608 if (isf_p == 0)
continue;
609 const std::size_t cls_p = sy.passive.cls;
610 const std::size_t stat_p = sn.nodes[node_p - 1].station;
611 const T w = T(rate * (sy.passive.statedep
612 ? rt_now(sy.passive.rt_row, sy.passive.rt_col)
614 if (num_traits<T>::to_double(w) <= 0)
continue;
615 const std::size_t dep_isf = sy.active.event == EventType::DEP ? isf_a : 0;
620 std::size_t blockedf = 0, droppedf = 0;
621 if (sy.passive.event == EventType::ARV && stat_p != 0) {
622 for (std::size_t f = 0; f < F; ++f) {
623 if (!caps[f].member[stat_p - 1])
continue;
624 if (stat_a != 0 && caps[f].member[stat_a - 1])
continue;
625 std::vector<double> xn = xf[f];
626 xn[cls_p - 1] += 1.0;
627 if (!violates(caps[f], xn))
continue;
628 if (caps[f].iswaitq[cls_p - 1])
642 e.net.local[isf_a - 1] = oa.space[ia];
653 std::size_t switchf = 0;
654 if (blockedf == 0 && sy.passive.event == EventType::ARV && cls_p != cls_a &&
655 stat_a != 0 && stat_p != 0)
656 for (std::size_t f = 0; f < F; ++f)
657 if (caps[f].member[stat_a - 1] && caps[f].member[stat_p - 1]) {
665 e.net.local[isf_a - 1] = oa.space[ia];
673 e.pwaitq = caps[switchf - 1].iswaitq[cls_p - 1];
683 if (buf[blockedf - 1].size() >= caps[blockedf - 1].lmax)
continue;
686 e.net.local[isf_a - 1] = oa.space[ia];
688 e.buf[blockedf - 1].push_back((node_p - 1) * K + cls_p);
696 const std::vector<T>& psrc = node_p == node_a ? oa.space[ia] : net.local[isf_p - 1];
697 const qn::EventOutcome<T> op =
qn::after_event(sn, node_p, psrc, sy.passive.event, cls_p);
703 for (std::size_t ip = 0; ip < op.space.size(); ++ip) {
704 if (num_traits<T>::to_double(op.prob[ip]) <= 0)
continue;
707 e.net.local[isf_a - 1] = oa.space[ia];
708 e.net.local[isf_p - 1] = op.space[ip];
710 e.w = T(w * op.prob[ip]);
713 if (sy.passive.event == EventType::ARV)
714 e.arv.push_back(std::make_pair(isf_p, cls_p));
726 for (std::size_t f = 0; f <
sn.regions.size(); ++f)
727 for (std::size_t r = 0; r <
sn.regions[f].rule.size(); ++r)
728 if (
sn.regions[f].rule[r] != DropStrategy::DROP)
return true;
742 if (!
sn.transparam.empty())
744 "SolverCTMC: a WAITQ finite capacity region is not supported together with stochastic "
745 "Petri net transitions; a firing is atomic across its arcs and has no single "
746 "destination to park a refused token against");
747 if (
sn.has_fork() || !
sn.fj.empty())
749 "SolverCTMC: a WAITQ finite capacity region is not supported together with fork-join; "
750 "a forked task refused entry would park without its siblings, and the join has no rule "
751 "for a sibling that never arrived");
752 for (std::size_t i = 0; i <
sn.nodes.size(); ++i) {
754 for (std::size_t r = 0; r < nd.
routing.size(); ++r) {
760 "SolverCTMC: a WAITQ finite capacity region is not supported together with the "
761 "state-dependent routing at node '" +
763 "'; the destination a token parks against is fixed when the job is refused, and a "
764 "state-dependent choice would have to be re-decided on release");
767 for (std::size_t f = 0; f <
sn.regions.size(); ++f)
768 for (std::size_t r = 0; r <
sn.regions[f].rule.size(); ++r) {
770 if (d == DropStrategy::DROP || d == DropStrategy::WAITQ)
continue;
772 "SolverCTMC: finite capacity region " + std::to_string(f + 1) +
773 " applies a blocking rule other than DROP or WAITQ to class " +
774 std::to_string(r + 1) +
775 "; BAS, BBS and RSRD hold the job AT ITS SERVER, which needs a blocked-server "
776 "marker column in the node state that this port does not allocate");
786 for (std::size_t i = 0; i <
sn.droprule.size() && i <
sn.stations.size(); ++i)
787 for (std::size_t r = 0; r <
sn.droprule[i].size(); ++r) {
789 if (d != DropStrategy::BAS && d != DropStrategy::BBS && d != DropStrategy::RSRD)
792 "SolverCTMC: station '" +
sn.stations[i].name +
793 "' applies true blocking (BAS/BBS/RSRD) to class " + std::to_string(r + 1) +
794 ", which holds the completing job at its server; this port allocates no "
795 "blocked-server marker in the node state, and voiding the departure instead would "
796 "let the server re-serve the same job and understate throughput");
812 const std::size_t K =
sn.nclasses;
813 const std::size_t NF =
sn.stateful_nodes.size();
816 const std::vector<Sync<T>> sync = refresh_sync(
sn);
817 std::vector<waitq_detail::RegionCaps<T>> caps = waitq_detail::extract_caps(
sn);
818 const std::size_t F = caps.size();
819 const std::vector<std::size_t> cutoff = analyzer_detail::resolve_cutoff(
sn,
opt);
820 waitq_detail::resolve_lmax(
sn, cutoff, caps);
827 init.
net = waitq_detail::wide_init_state(
sn, cutoff);
828 init.
buf.assign(F, std::vector<std::size_t>());
829 for (std::size_t f = 0; f < F; ++f)
830 if (waitq_detail::violates(caps[f], waitq_detail::region_aggr(
sn, caps[f], init.
net)))
832 "SolverCTMC: the initial state violates the constraints of finite capacity region " +
833 std::to_string(f + 1) +
"; the region cannot hold the model's initial population");
835 std::vector<WaitqState<T>> states;
836 states.push_back(init);
837 std::map<std::vector<double>, std::size_t> index;
838 index[waitq_detail::waitq_key(init)] = 0;
839 std::deque<std::size_t> frontier;
840 frontier.push_back(0);
845 std::vector<std::size_t> ta, ti, tj;
847 std::vector<std::vector<std::vector<T>>> arv, dep;
848 arv.push_back(std::vector<std::vector<T>>(NF, std::vector<T>(K, zero)));
849 dep.push_back(std::vector<std::vector<T>>(NF, std::vector<T>(K, zero)));
853 const std::vector<double> key = waitq_detail::waitq_key(
ws);
854 const typename std::map<std::vector<double>, std::size_t>::const_iterator it =
856 if (it != index.end())
return it->second;
857 if (states.size() >=
opt.state_max)
859 "SolverCTMC: the WAITQ state space exceeds the cap of " +
860 std::to_string(
opt.state_max) +
861 " states; the region FIFO multiplies the space by its own occupancy");
862 states.push_back(
ws);
863 arv.push_back(std::vector<std::vector<T>>(NF, std::vector<T>(K, zero)));
864 dep.push_back(std::vector<std::vector<T>>(NF, std::vector<T>(K, zero)));
865 index[key] = states.size() - 1;
866 frontier.push_back(states.size() - 1);
867 return states.size() - 1;
875 std::vector<waitq_detail::Successor<T>> succ;
876 while (!frontier.empty()) {
877 const std::size_t s = frontier.front();
878 frontier.pop_front();
880 waitq_detail::waitq_successors(
sn, sync, caps,
ws, succ);
881 for (std::size_t e = 0; e < succ.size(); ++e) {
882 const waitq_detail::Successor<T>& su = succ[e];
883 const std::size_t dst = state_index(su.next);
884 ta.push_back(su.sync);
888 if (su.dep_isf != 0) dep[s][su.dep_isf - 1][su.dep_cls - 1] += su.w;
889 for (std::size_t q = 0; q < su.arv.size(); ++q)
890 arv[s][su.arv[q].first - 1][su.arv[q].second - 1] += su.w;
894 const std::size_t n = states.size();
897 res.
chain.space.reserve(n);
899 for (std::size_t s = 0; s < n; ++s) {
900 res.
chain.space.push_back(states[s].net);
901 res.
buf.push_back(states[s].buf);
903 res.
chain.arv_rates = arv;
904 res.
chain.dep_rates = dep;
905 if (
opt.keep_filtration) res.
chain.filt.assign(sync.size(),
Matrix<T>(n, n, zero));
906 for (std::size_t e = 0; e < tv.size(); ++e) {
907 res.
chain.Q(ti[e], tj[e]) += tv[e];
908 if (
opt.keep_filtration) res.
chain.filt[ta[e]](ti[e], tj[e]) += tv[e];
926 const std::vector<T>& pi) {
927 const std::size_t K =
sn.nclasses;
929 for (std::size_t s = 0; s < r.
buf.size() && s < pi.size(); ++s)
930 for (std::size_t f = 0; f < r.
buf[s].size(); ++f)
931 for (std::size_t j = 0; j < r.
buf[s][f].size(); ++j) {
932 const std::size_t cls = (r.
buf[s][f][j] - 1) % K + 1;
933 out[cls - 1] += pi[s];
942 std::vector<std::vector<std::vector<std::size_t>>>
buf;
961 out.
sol.cutoff = analyzer_detail::resolve_cutoff(
sn,
opt);
965 out.
sol.actualmethod =
opt.method;
975 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.
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.
CtmcAvg< T > solver_ctmc_avg_from_pi(const NetworkStruct< T > &sn, const CtmcResult< T > &r, const std::vector< T > &pivec)
Port of solver_ctmc_avg_from_pi: map a state distribution to mean metrics.
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...
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...
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.
std::vector< T > ctmc_solve(const Matrix< T > &Qin)
Steady-state distribution of a continuous-time Markov chain.
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.
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.
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.