5#ifndef LINE_SOLVERS_MAM_SOLVER_MAM_BASIC_H
6#define LINE_SOLVERS_MAM_SOLVER_MAM_BASIC_H
104using lang::GlobalConstants;
108namespace basic_detail {
124 case ProcessType::EXP:
125 case ProcessType::ERLANG:
126 case ProcessType::HYPEREXP:
127 case ProcessType::PH:
128 case ProcessType::APH:
129 case ProcessType::ME:
130 case ProcessType::RAP:
131 case ProcessType::MAP:
132 case ProcessType::MMAP:
133 case ProcessType::MPH:
134 case ProcessType::COXIAN:
135 case ProcessType::COX2:
136 case ProcessType::MMPP2:
137 case ProcessType::IMMEDIATE:
138 case ProcessType::DISABLED:
160const lang::Distrib<T>& mam_declared_law(
const lang::Distrib<T>& d) {
164 return d.declared ? *d.declared : d;
168bool mam_gk1_applicable(
const qn::NetworkStruct<T>& L, std::size_t i0, std::size_t K) {
169 for (std::size_t r = 0; r < K; ++r) {
186bool is_renewal_map(
const Map<T>& m) {
187 const std::size_t n = m.D0.rows();
188 if (n == 1)
return true;
190 for (std::size_t i = 0; i < n; ++i)
191 for (std::size_t j = 0; j < n; ++j)
192 scale = std::max(scale, std::fabs(num_traits<T>::to_double(m.D1(i, j))));
193 const double tol = 1e-9 * scale;
194 std::vector<double> exitrate(n, 0.0), sigma(n, 0.0);
196 for (std::size_t i = 0; i < n; ++i) {
197 for (std::size_t j = 0; j < n; ++j) exitrate[i] += num_traits<T>::to_double(m.D1(i, j));
200 if (tot <= 0.0)
return true;
203 for (std::size_t i = 0; i < n; ++i)
204 if (exitrate[i] > tol) {
208 if (ref == n)
return true;
209 for (std::size_t j = 0; j < n; ++j)
210 sigma[j] = num_traits<T>::to_double(m.D1(ref, j)) / exitrate[ref];
211 for (std::size_t i = 0; i < n; ++i)
212 for (std::size_t j = 0; j < n; ++j)
213 if (std::fabs(num_traits<T>::to_double(m.D1(i, j)) - exitrate[i] * sigma[j]) > tol)
237bool mam_source_feeds_station(
const qn::NetworkStruct<T>& L, std::size_t jst, std::size_t ist) {
238 const std::size_t M = L.nstations, K = L.nclasses;
239 if (jst < 1 || jst > M || ist < 1 || ist > M || jst == ist)
return false;
240 if (L.nodes[L.node_of_station(jst) - 1].nodetype != qn::NodeType::Source)
return false;
243 if (rtst.rows() < M * K)
return false;
247 double emitted = 0.0;
248 for (std::size_t r = 0; r < K; ++r) {
249 const std::size_t row = (jst - 1) * K + r;
250 double into = 0.0, total = 0.0;
251 for (std::size_t c = 0; c < K; ++c)
252 into += num_traits<T>::to_double(rtst(row, (ist - 1) * K + c));
253 for (std::size_t c = 0; c < rtst.cols(); ++c)
254 total += num_traits<T>::to_double(rtst(row, c));
255 if (std::fabs(into - total) > tol)
return false;
258 if (emitted <= tol)
return false;
261 for (std::size_t kst = 1; kst <= M; ++kst) {
262 if (kst == jst)
continue;
264 for (std::size_t r = 0; r < K; ++r)
265 for (std::size_t c = 0; c < K; ++c)
266 into += num_traits<T>::to_double(rtst((kst - 1) * K + r, (ist - 1) * K + c));
267 if (into > tol)
return false;
274bool is_markovian_map(
const Map<T>& m) {
275 const std::size_t n = m.D0.rows();
277 for (std::size_t i = 0; i < n; ++i)
278 for (std::size_t j = 0; j < n; ++j) {
279 scale = std::max(scale, std::fabs(num_traits<T>::to_double(m.D0(i, j))));
280 scale = std::max(scale, std::fabs(num_traits<T>::to_double(m.D1(i, j))));
282 const double tol = 1e-9 * scale;
283 for (std::size_t i = 0; i < n; ++i) {
285 for (std::size_t j = 0; j < n; ++j) {
286 const double a = num_traits<T>::to_double(m.D0(i, j));
287 const double b = num_traits<T>::to_double(m.D1(i, j));
288 if (i != j && a < -tol)
return false;
289 if (b < -tol)
return false;
292 if (std::fabs(rowsum) > tol)
return false;
299qsys::ServiceLaw<T> svc_mixture(
const Mmap<T>& arv,
const std::vector<PhService<T>>& svc) {
300 const T zero = num_traits<T>::from_int(0);
301 const std::size_t K = svc.size();
302 Matrix<T> Q = arv.D0;
303 for (std::size_t k = 0; k < arv.classes(); ++k)
304 for (std::size_t i = 0; i < Q.rows(); ++i)
305 for (std::size_t j = 0; j < Q.cols(); ++j) Q(i, j) += arv.Dc[k](i, j);
307 std::vector<T> lam(K, zero);
309 for (std::size_t k = 0; k < K && k < arv.classes(); ++k) {
310 const std::vector<T> t =
vecmul(theta, arv.Dc[k]);
311 for (
const T& v : t) lam[k] += v;
314 std::vector<T> w(K, T(num_traits<T>::from_int(1) / num_traits<T>::from_int((
int)K)));
316 for (std::size_t k = 0; k < K; ++k) w[k] = T(lam[k] / sumL);
318 std::size_t ntot = 0;
319 for (
const PhService<T>& s : svc) ntot += s.S.rows();
320 std::vector<T> alpha(ntot, zero);
321 Matrix<T> Tm(ntot, ntot, zero);
323 for (std::size_t k = 0; k < K; ++k) {
324 for (std::size_t i = 0; i < svc[k].S.rows(); ++i) {
325 alpha[off + i] = T(w[k] * svc[k].sigma[i]);
326 for (std::size_t j = 0; j < svc[k].S.cols(); ++j) Tm(off + i, off + j) = svc[k].S(i, j);
328 off += svc[k].S.rows();
344Matrix<T> station_visits(
const qn::NetworkStruct<T>& L) {
345 Matrix<T> V(L.nstations, L.nclasses, num_traits<T>::from_int(0));
346 for (std::size_t c = 0; c < L.nchains; ++c)
347 for (std::size_t i = 0; i < L.nstations; ++i) {
348 const std::size_t sf = L.stateful_of_station(i + 1) - 1;
349 for (std::size_t k = 0; k < L.nclasses; ++k) V(i, k) += L.visits[c](sf, k);
356void zero_nans(Matrix<T>& A) {
357 for (std::size_t i = 0; i < A.rows(); ++i)
358 for (std::size_t j = 0; j < A.cols(); ++j)
359 if (std::isnan(num_traits<T>::to_double(A(i, j)))) A(i, j) = num_traits<T>::from_int(0);
380TruncRenorm<T> truncate_renorm(
const Mmap<T>& arv,
const std::vector<PhService<T>>& svc,
382 const T zero = num_traits<T>::from_int(0);
384 std::vector<PhService<T>> scall = svc;
385 if (svc.size() > 1) {
386 const qsys::ServiceLaw<T> mix = svc_mixture(arv, svc);
387 Matrix<T> Dsum(arv.order(), arv.order(), zero);
388 for (std::size_t k = 0; k < arv.classes(); ++k)
389 for (std::size_t i = 0; i < Dsum.rows(); ++i)
390 for (std::size_t j = 0; j < Dsum.cols(); ++j) Dsum(i, j) += arv.Dc[k](i, j);
393 call.Dc.assign(1, Dsum);
394 scall.assign(1, PhService<T>{mix.ph_alpha, mix.ph_T});
398 out.p.assign(capK + 1, zero);
400 for (std::size_t n = 0; n <= capK; ++n) {
404 if (!(mass > zero)) {
405 out.p.assign(capK + 1, zero);
406 out.p[0] = num_traits<T>::from_int(1);
408 for (std::size_t n = 0; n <= capK; ++n) out.p[n] /= mass;
411 for (std::size_t n = 0; n <= capK; ++n) m += num_traits<T>::from_int((int)n) * out.p[n];
412 if (m < zero) m = zero;
413 if (num_traits<T>::to_double(m) >
static_cast<double>(capK))
414 m = num_traits<T>::from_int((
int)capK);
416 out.lossProb = out.p[capK];
444bool station_setup_pair(
const qn::NetworkStruct<T>& L, std::size_t ist, lang::Distrib<T>& setup,
445 lang::Distrib<T>& delayoff) {
446 typename std::map<std::size_t, qn::SetupDelayOffParam<T>>::const_iterator it =
447 L.setupparam.find(ist);
448 if (it == L.setupparam.end())
return false;
449 return it->second.last(setup, delayoff);
462void mam_setup_qbd(
const qn::NetworkStruct<T>& L, std::size_t ist,
463 const std::vector<T>& aggrLambda,
464 const std::vector<std::vector<PhService<T>>>& svc,
const Matrix<T>& S,
465 const std::vector<std::vector<T>>& rates, std::size_t R,
double ns,
466 const lang::Distrib<T>& setup,
const lang::Distrib<T>& delayoff,
467 std::vector<T>& Qret) {
468 const T zero = num_traits<T>::from_int(0), one = num_traits<T>::from_int(1);
469 const std::size_t i0 = ist - 1, K = L.nclasses;
470 for (std::size_t r = 0; r < K; ++r) Qret[r] = zero;
471 if constexpr (!num_traits<T>::has_transcendental) {
473 "solver_mam_basic: a setup/delay-off station is solved by a QBD whose G matrix needs "
474 "a logarithm; rerun with --arith double or real");
480 std::vector<T> rho(K, zero);
482 for (std::size_t r = 0; r < K; ++r) {
483 if (L.disabled[i0][r])
continue;
485 const Matrix<T>& Sr = svc[i0][r].S;
486 if (Sr.rows() == 0)
continue;
488 for (std::size_t a = 0; a < negS.rows(); ++a)
489 for (std::size_t b = 0; b < negS.cols(); ++b) negS(a, b) = -negS(a, b);
490 const Matrix<T> negSinv =
inverse(negS);
492 for (std::size_t a = 0; a < negSinv.rows() && a < svc[i0][r].sigma.size(); ++a)
493 for (std::size_t b = 0; b < negSinv.cols(); ++b)
494 mean += svc[i0][r].sigma[a] * negSinv(a, b);
495 if (!(mean > zero))
continue;
496 std::size_t c = L.nchains;
497 for (std::size_t cc = 0; cc < L.nchains; ++cc)
498 if (L.chains[cc][r]) c = cc;
499 if (c == L.nchains)
continue;
501 rho[r] = T(rates[c][r] * mean);
504 if (!(rho_total > zero))
return;
507 for (std::size_t r = 0; r < K && r < aggrLambda.size(); ++r) lam_total += aggrLambda[r];
508 if (R == 1 && !aggrLambda.empty()) lam_total = aggrLambda[0];
509 if (!(lam_total > zero))
return;
510 const T aggrRate = T(lam_total / rho_total);
513 for (std::size_t r = 0; r < K; ++r)
514 if (rho[r] > zero) Qret[r] = T(qtot * rho[r] / rho_total);
535void mam_setup_closed(
const qn::NetworkStruct<T>& L, std::size_t ist,
536 const std::vector<std::vector<PhService<T>>>& svc,
const Matrix<T>& S,
537 const std::vector<std::vector<T>>& rates,
const std::vector<T>& ztchain,
538 const Matrix<T>& V,
double ns,
const lang::Distrib<T>& setup,
539 const lang::Distrib<T>& delayoff,
const Matrix<T>& QNprev,
540 std::vector<T>& Qret) {
541 const T zero = num_traits<T>::from_int(0), one = num_traits<T>::from_int(1);
542 const std::size_t i0 = ist - 1, K = L.nclasses;
543 for (std::size_t r = 0; r < K; ++r) Qret[r] = zero;
544 if constexpr (!num_traits<T>::has_transcendental) {
546 "solver_mam_basic: the closed setup/delay-off chain fits a phase-type setup and "
547 "delay-off, which needs a square root; rerun with --arith double or real");
554 for (std::size_t c = 0; c < L.nchains; ++c) {
556 bool finiteNc =
true, anyActive =
false;
557 for (std::size_t r = 0; r < K; ++r) {
558 if (!L.chains[c][r])
continue;
559 if (!std::isfinite(L.classes[r].population)) { finiteNc =
false;
break; }
560 Nc += num_traits<T>::from_double(L.classes[r].population);
561 if (rates[c][r] > zero && std::isfinite(num_traits<T>::to_double(S(i0, r))))
564 if (!finiteNc || !anyActive || !(Nc > zero))
continue;
567 for (std::size_t j = 0; j < K; ++j)
568 if (L.chains[c][j]) vtot += V(i0, j);
569 const T ZT = T(ztchain[c] / (vtot > ft ? vtot : ft));
579 T lamHere = zero, qnHere = zero, tnS = zero, tnTot = zero, svcAny = zero;
580 std::size_t svcAnyCount = 0;
581 for (std::size_t r = 0; r < K; ++r) {
582 if (!L.chains[c][r])
continue;
583 T lamK = rates[c][r];
584 if (!std::isfinite(num_traits<T>::to_double(lamK))) lamK = zero;
586 if (!std::isfinite(num_traits<T>::to_double(sk))) sk = zero;
588 if (i0 < QNprev.rows() && r < QNprev.cols() &&
589 std::isfinite(num_traits<T>::to_double(QNprev(i0, r))))
590 qnHere += QNprev(i0, r);
593 if (sk > zero) { svcAny += sk; ++svcAnyCount; }
596 if (lamHere > ft && T(Nc - qnHere) > zero) {
597 const T zalt = T(T(Nc - qnHere) / lamHere);
598 Zc = zalt > ZT ? zalt : ZT;
602 T Sbar = tnTot > ft ? T(tnS / tnTot)
604 ? T(svcAny / num_traits<T>::from_int(
605 static_cast<long>(svcAnyCount)))
607 if (!(Sbar > ft))
continue;
609 Nc, Zc, T(one / Sbar), alpharate, alphascv, betarate, betascv);
610 if (!std::isfinite(num_traits<T>::to_double(cr.QN)) || !(cr.XN > zero))
continue;
611 T Wq = T(T(cr.QN / cr.XN) - Sbar);
612 if (!(Wq > zero)) Wq = zero;
613 for (std::size_t r = 0; r < K; ++r) {
614 if (!L.chains[c][r])
continue;
615 if (L.disabled[i0][r])
continue;
621 const T cserv = (std::isfinite(ns) && ns >= 1.0)
622 ? num_traits<T>::from_double(ns)
624 if (rates[c][r] > zero && std::isfinite(num_traits<T>::to_double(S(i0, r))))
625 Qret[r] = T(rates[c][r] * T(Wq + T(S(i0, r) / cserv)));
633void solve_fcfs_station(
const qn::NetworkStruct<T>& L,
const MamOptions& opt, std::size_t ist,
634 const std::vector<T>& lambda,
const Matrix<T>& V,
const Matrix<T>& S,
635 const std::vector<std::vector<bool>>& Sknown,
636 const std::vector<std::vector<Map<T>>>& PH,
637 const std::vector<std::vector<bool>>& PHset,
638 const std::vector<std::vector<PhService<T>>>& svc,
639 const std::vector<Mmap<T>>& chainSysArrivals,
640 const std::vector<T>& ztchain, Matrix<T>& QN, Matrix<T>& UN,
641 Matrix<T>& RN, Matrix<T>& TN, std::vector<bool>& exact_station) {
642 const T zero = num_traits<T>::from_int(0), one = num_traits<T>::from_int(1);
643 const std::size_t M = L.nstations, K = L.nclasses, C = L.nchains;
644 const double ns = L.stations[ist - 1].nservers;
645 const std::size_t i0 = ist - 1;
650 bool prio_distinct =
false;
651 if (sched_i == SchedStrategy::HOL || sched_i == SchedStrategy::FCFSPRPRIO)
652 for (std::size_t r = 1; r < K; ++r)
653 if (L.classes[r].prio != L.classes[0].prio) prio_distinct =
true;
656 std::vector<std::vector<T>> rates(C, std::vector<T>(K, zero));
657 for (std::size_t c = 0; c < C; ++c)
658 for (std::size_t r = 0; r < K; ++r) rates[c][r] = T(V(i0, r) * lambda[c]);
661 for (std::size_t c = 0; c < C; ++c) {
662 const std::vector<std::size_t>& ic = L.inchain[c];
664 for (std::size_t r : ic) tot += rates[c][r - 1];
665 Matrix<T> markProb(1, ic.size(), zero);
666 for (std::size_t j = 0; j < ic.size(); ++j)
667 markProb(0, j) = (tot > zero) ? T(rates[c][ic[j] - 1] / tot) : zero;
674 for (std::size_t j = 0; j < ic.size(); ++j)
675 if (rates[c][ic[j] - 1] > zero) anypos =
true;
677 std::vector<T> tgt(ic.size(), zero);
678 for (std::size_t j = 0; j < ic.size(); ++j)
679 tgt[j] = (rates[c][ic[j] - 1] > zero)
680 ? T(one / rates[c][ic[j] - 1])
681 : num_traits<T>::from_double(1.0 / GlobalConstants::Zero);
689 aggr =
mmap_super_safe(std::vector<Mmap<T>>{aggr, cur}, opt.space_max);
696 std::vector<std::size_t> markorder;
697 for (std::size_t c = 0; c < C; ++c)
698 for (std::size_t r : L.inchain[c]) markorder.push_back(r);
699 if (markorder.size() == aggr.Dc.size() &&
700 !std::is_sorted(markorder.begin(), markorder.end())) {
701 std::vector<std::size_t> perm(markorder.size());
702 for (std::size_t j = 0; j < perm.size(); ++j) perm[j] = j;
703 std::stable_sort(perm.begin(), perm.end(),
704 [&markorder](std::size_t a, std::size_t b) {
705 return markorder[a] < markorder[b];
707 std::vector<Matrix<T>> reordered(perm.size());
708 for (std::size_t j = 0; j < perm.size(); ++j) reordered[j] = aggr.Dc[perm[j]];
709 aggr.Dc.swap(reordered);
713 const std::size_t R = aggr.classes();
714 lang::Distrib<T> setup_dist, delayoff_dist;
715 const bool has_setup = station_setup_pair(L, ist, setup_dist, delayoff_dist);
739 const std::string mark_refusal =
740 std::string(
"solver_mam_basic: the assembled arrival stream at station '") +
741 L.stations[i0].name +
"' carries " + std::to_string(R) +
742 " marked classes against the model's " + std::to_string(K) +
743 "; the reference collapses chain 1 to a single mark, so this analyzer has no arrival "
744 "stream to pair with each service law. A class-switching chain at an FCFS station is "
745 "refused rather than solved with a mismatched marking";
747 const std::vector<T> aggrLambda =
mmap_lambda(aggr);
748 double aggrUtil = 0.0;
749 for (std::size_t r = 0; r < K; ++r) {
752 if (L.disabled[i0][r])
continue;
753 const double lam = num_traits<T>::to_double(aggrLambda[R == 1 ? 0 : r]);
754 const double mu = num_traits<T>::to_double(L.rates(i0, r));
756 if (den > 0.0 && std::isfinite(den)) aggrUtil += lam / den;
759 std::vector<T> Qret(K, zero);
760 bool exact_here =
false;
762 bool finiteCapUsed =
false;
763 T finiteCapMeanQ = zero, finiteCapLossProb = zero;
764 std::vector<T> finiteCapLossPerClass;
766 bool anyopen =
false;
767 for (std::size_t r = 0; r < K; ++r)
768 if (std::isinf(L.classes[r].population)) anyopen =
true;
776 std::vector<std::size_t> iK(K);
777 for (std::size_t r = 0; r < K; ++r) iK[r] = r;
778 std::stable_sort(iK.begin(), iK.end(), [&L](std::size_t a, std::size_t b) {
779 return L.classes[a].prio > L.classes[b].prio;
781 for (std::size_t j = 1; j < K; ++j)
782 if (L.classes[iK[j]].prio == L.classes[iK[j - 1]].prio)
784 "solver_mam_basic: Solver MAM requires either identical priorities or all "
785 "distinct priorities");
789 std::vector<PhService<T>> sl;
790 for (std::size_t j = 0; j < K; ++j) {
791 pa.Dc.push_back(aggr.Dc[iK[j]]);
792 sl.push_back(svc[i0][iK[j]]);
794 const std::vector<std::vector<T>> m = (sched_i == SchedStrategy::FCFSPRPRIO)
797 for (std::size_t j = 0; j < K; ++j) Qret[iK[j]] = m[j][0];
799 for (std::size_t r = 0; r < K; ++r)
800 Qret[r] = num_traits<T>::from_double(L.classes[r].population);
801 }
else if (anyopen) {
802 bool isMapDc = (K == 1) && (L.service[i0][0].type == ProcessType::DET);
807 std::size_t dmcSource = 0;
808 if (K == 1 && !isMapDc && L.service[i0][0].type == ProcessType::EXP) {
809 for (std::size_t j = 1; j <= M; ++j)
810 if (j != ist && L.service[j - 1][0].type == ProcessType::DET &&
811 mam_source_feeds_station(L, j, ist)) {
818 std::size_t phSource = 0;
819 if (K == 1 && !isMapDc && !isDMc && L.service[i0][0].type == ProcessType::EXP &&
820 std::isfinite(ns) && ns >= 1.0) {
821 for (std::size_t j = 1; j <= M; ++j) {
822 if (j == ist)
continue;
824 if (sp != ProcessType::EXP && sp != ProcessType::DET &&
825 sp != ProcessType::IMMEDIATE && sp != ProcessType::DISABLED) {
833 if (!mam_source_feeds_station(L, j, ist))
continue;
837 }
else if (sp == ProcessType::EXP && ns > 1.0 && M == 2 &&
838 L.nodes[L.node_of_station(j) - 1].nodetype == qn::NodeType::Source) {
871 const T muQ = T(one / S(i0, 0));
874 map_pie(src), src.D0, muQ,
static_cast<unsigned>(std::llround(ns)));
875 Qret[0] = r.meanQueueLength;
876 exactRespT = r.meanSojournTime;
878 }
catch (
const std::exception&) {
882 if (!exact_here && isDMc) {
884 const T muQ = T(one / S(i0, 0));
886 L.rates(dmcSource - 1, 0), muQ,
static_cast<unsigned>(std::llround(ns)));
887 Qret[0] = r.meanQueueLength;
888 exactRespT = r.meanSojournTime;
890 }
catch (
const std::exception&) {
894 if (!exact_here && !isFiniteCap && isMapDc) {
899 const qsys::MapDcResult<T> r =
901 Qret[0] = r.meanQueueLength;
902 exactRespT = r.meanSojournTime;
904 }
catch (
const std::exception&) {
913 bool isMapMc = !exact_here && !isFiniteCap && (K == 1) && !isMapDc && !isDMc &&
914 !isPhM1 && L.service[i0][0].type == ProcessType::EXP &&
915 std::isfinite(ns) && ns > 1.0;
921 const T muQ = T(one / S(i0, 0));
922 const qsys::MapMcResult<T> r =
924 Qret[0] = r.meanQueueLength;
925 exactRespT = r.meanSojournTime;
927 }
catch (
const std::exception&) {
937 bool isMapPhc =
false;
938 if (!exact_here && !isFiniteCap && K == 1 && std::isfinite(ns) && ns > 1.0) {
940 if (st != ProcessType::EXP && st != ProcessType::DET && st != ProcessType::ME &&
941 st != ProcessType::RAP && st != ProcessType::IMMEDIATE &&
942 st != ProcessType::DISABLED) {
953 arv,
map_pie(svc0), svc0.D0,
static_cast<unsigned>(std::llround(ns)),
954 static_cast<std::size_t
>(500),
static_cast<std::size_t
>(1), std::vector<T>());
955 Qret[0] = r.meanQueueLength;
956 exactRespT = r.meanSojournTime;
958 }
catch (
const std::exception&) {
964 }
else if (isFiniteCap) {
968 const std::size_t capK =
static_cast<std::size_t
>(std::llround(L.cap[i0]));
971 bool isMmck = (aggr.order() == 1);
975 double lo = 0.0, hi = 0.0;
976 for (std::size_t r = 0; r < K; ++r) {
977 if (L.disabled[i0][r])
continue;
978 if (L.service[i0][r].type != ProcessType::EXP) {
982 const double v = num_traits<T>::to_double(L.rates(i0, r));
983 if (!(v > 0.0))
continue;
987 muMmck = L.rates(i0, r);
989 lo = std::min(lo, v);
990 hi = std::max(hi, v);
993 if (!any) isMmck =
false;
994 if (isMmck && hi - lo > 1e-9 * std::max(1.0, hi)) isMmck =
false;
998 for (
const T& v : aggrLambda) lamTot += v;
999 const qsys::MmckResult<T> r =
1000 qsys::qsys_mmck(lamTot, muMmck,
static_cast<unsigned>(std::llround(ns)),
1001 static_cast<unsigned>(capK));
1002 finiteCapMeanQ = r.meanQueueLength;
1003 finiteCapLossProb = r.lossProbability;
1004 }
else if (ns == 1.0) {
1008 std::vector<PhService<T>> sl;
1009 for (std::size_t r = 0; r < K; ++r) sl.push_back(svc[i0][r]);
1010 const qsys::ServiceLaw<T> mix = svc_mixture(aggr, sl);
1012 finiteCapMeanQ = r.meanQueueLength;
1013 finiteCapLossProb = r.lossAggregate;
1014 finiteCapLossPerClass = r.lossRatio;
1016 std::vector<PhService<T>> sl;
1017 for (std::size_t r = 0; r < K; ++r) sl.push_back(svc[i0][r]);
1018 const TruncRenorm<T> r = truncate_renorm(aggr, sl, capK);
1019 finiteCapMeanQ = r.meanQ;
1020 finiteCapLossProb = r.lossProb;
1022 finiteCapUsed =
true;
1023 exact_station[i0] =
true;
1024 }
else if (has_setup) {
1030 mam_setup_qbd(L, ist, aggrLambda, svc, S, rates, R, ns, setup_dist, delayoff_dist,
1038 (K == 1) && (ns == 1.0) && PHset[i0][0] &&
1039 std::fabs(num_traits<T>::to_double(
map_acf(PH[i0][0], std::vector<unsigned>{1})[0])) >
1044 arv.D1 = aggr.Dc[0];
1045 const QbdMapMap1Result<T> r =
qbd_mapmap1(arv, PH[i0][0]);
1056 bool gk_done =
false;
1057 if (ns == 1.0 && mam_gk1_applicable(L, i0, K)) {
1059 std::vector<Matrix<T>> MM;
1060 MM.push_back(aggr.D0);
1061 Matrix<T> D1sum(aggr.D0.rows(), aggr.D0.cols(),
1062 num_traits<T>::from_int(0));
1063 for (std::size_t r = 0; r < K; ++r)
1064 for (std::size_t a = 0; a < D1sum.rows(); ++a)
1065 for (std::size_t b = 0; b < D1sum.cols(); ++b)
1066 D1sum(a, b) += aggr.Dc[r](a, b);
1067 MM.push_back(D1sum);
1068 for (std::size_t r = 0; r < K; ++r) MM.push_back(aggr.Dc[r]);
1069 std::vector<lang::Distrib<T>> laws;
1070 for (std::size_t r = 0; r < K; ++r)
1071 laws.push_back(mam_declared_law(L.service[i0][r]));
1073 MM, laws, std::vector<T>(),
static_cast<std::size_t
>(1), 1e-12,
1074 static_cast<std::size_t
>(10000));
1075 for (std::size_t r = 0; r < K; ++r)
1076 Qret[r] = gk.lambdas[r] * gk.meanSojournTime[r];
1083 }
catch (
const std::exception&) {
1088 std::vector<PhService<T>> sl;
1089 for (std::size_t r = 0; r < R; ++r) sl.push_back(svc[i0][r]);
1091 for (std::size_t r = 0; r < K; ++r) Qret[r] = m[r];
1099 std::size_t maxLevel = 1;
1100 for (std::size_t r = 0; r < K; ++r)
1101 if (std::isfinite(L.classes[r].population))
1102 maxLevel +=
static_cast<std::size_t
>(std::llround(L.classes[r].population));
1107 const Map<T> probe = aggr.map();
1109 for (std::size_t r = 0; r < K; ++r)
1110 Qret[r] = (L.rates(i0, 0) > zero)
1114 }
else if (has_setup) {
1121 mam_setup_closed(L, ist, svc, S, rates, ztchain, V, ns, setup_dist, delayoff_dist,
1126 std::vector<PhService<T>> sl;
1127 for (std::size_t r = 0; r < R; ++r) sl.push_back(svc[i0][r]);
1129 for (std::size_t r = 0; r < K; ++r) {
1130 const std::size_t Nk =
1131 static_cast<std::size_t
>(std::llround(L.classes[r].population));
1132 std::vector<T> p(Nk + 1, zero);
1133 const std::vector<T>& src = pd[r];
1135 for (std::size_t n = 0; n < Nk; ++n) {
1141 p[Nk] =
num_abs(T(one - acc));
1143 for (std::size_t n = 0; n <= Nk; ++n) mass += p[n];
1146 for (std::size_t n = 0; n <= Nk; ++n)
1147 m += num_traits<T>::from_int((
int)n) * T(p[n] / mass);
1148 if (m < zero) m = zero;
1149 if (num_traits<T>::to_double(m) >
static_cast<double>(Nk))
1150 m = num_traits<T>::from_int((
int)Nk);
1157 if (finiteCapUsed) {
1160 std::vector<T> inflow(K, zero), eff(K, zero);
1161 for (std::size_t r = 0; r < K; ++r) {
1163 for (std::size_t cc = 0; cc < C; ++cc)
1164 if (L.chains[cc][r]) {
1168 inflow[r] = rates[c][r];
1169 const T loss = finiteCapLossPerClass.empty() ? finiteCapLossProb
1170 : finiteCapLossPerClass[r];
1171 eff[r] = T(inflow[r] * T(one - loss));
1174 for (std::size_t r = 0; r < K; ++r) sumTN += eff[r];
1178 for (std::size_t r = 0; r < K; ++r)
1179 if (Sknown[i0][r]) sw += eff[r] * S(i0, r);
1180 const T w = T(T(finiteCapMeanQ / sumTN) - T(sw / sumTN));
1181 Wq = (w > zero) ? w : zero;
1183 for (std::size_t r = 0; r < K; ++r) {
1185 UN(i0, r) = T(TN(i0, r) * S(i0, r) / num_traits<T>::from_double(ns));
1186 if (TN(i0, r) > zero) {
1187 RN(i0, r) = T(Wq + S(i0, r));
1188 QN(i0, r) = T(TN(i0, r) * RN(i0, r));
1195 for (std::size_t r = 0; r < K; ++r) {
1197 for (std::size_t cc = 0; cc < C; ++cc)
1198 if (L.chains[cc][r]) {
1202 TN(i0, r) = rates[c][r];
1203 UN(i0, r) = T(TN(i0, r) * S(i0, r) / num_traits<T>::from_double(ns));
1204 QN(i0, r) = Qret[r];
1206 RN(i0, r) = exactRespT;
1207 exact_station[i0] =
true;
1211 if (Sknown[i0][r] && std::isfinite(ns))
1212 QN(i0, r) = T(
QN(i0, r) + TN(i0, r) * S(i0, r) *
1213 num_traits<T>::from_double((ns - 1.0) / ns));
1214 RN(i0, r) = (TN(i0, r) > zero) ? T(
QN(i0, r) / TN(i0, r)) : zero;
1233 "solver_mam_basic: the matrix-analytic station solves run tolerance-terminated "
1234 "iterations (the Riccati doubling behind MMAP[K]/PH[K]/1, the QBD cyclic reduction) "
1235 "and need transcendental arithmetic; rerun this model with --arith double or "
1238 using namespace basic_detail;
1241 const double tol =
opt.tol;
1245 std::vector<std::vector<bool>> Sknown(M, std::vector<bool>(K,
false));
1246 for (std::size_t i = 0; i < M; ++i)
1247 for (std::size_t r = 0; r < K; ++r)
1249 S(i, r) = T(one / L.
rates(i, r));
1250 Sknown[i][r] =
true;
1258 std::vector<T> ztchain(C, zero);
1259 for (std::size_t c = 0; c < C; ++c)
1260 for (std::size_t i = 0; i < M; ++i)
1261 if (std::isinf(L.
stations[i].nservers)) ztchain[c] += dem.
Lchain(i, c);
1263 Matrix<T> QN(M, K, zero), UN(M, K, zero), RN(M, K, zero), TN(M, K, zero);
1264 std::vector<T> CN(K, zero), XN(K, zero);
1265 std::vector<bool> exact_station(M,
false);
1269 std::vector<std::vector<Map<T>>> PH(M, std::vector<
Map<T>>(K));
1270 std::vector<std::vector<bool>> PHset(M, std::vector<bool>(K,
false));
1271 std::vector<std::vector<PhService<T>>> svc(M, std::vector<
PhService<T>>(K));
1272 for (std::size_t i = 0; i < M; ++i) {
1274 if (!(sc == SchedStrategy::FCFS || sc == SchedStrategy::HOL ||
1275 sc == SchedStrategy::FCFSPRPRIO || sc == SchedStrategy::PS))
1277 for (std::size_t r = 0; r < K; ++r) {
1278 if (L.
service[i][r].type == ProcessType::DET &&
opt.preserve_det_resolved())
continue;
1279 const double ns = L.
stations[i].nservers;
1282 if (L.
disabled[i][r] || !Sknown[i][r] || !(target > zero)) {
1291 svc[i][r].sigma.assign(1, one);
1300 PH[i][r] = (pt == ProcessType::ME || pt == ProcessType::RAP)
1304 svc[i][r].sigma =
map_pie(PH[i][r]);
1305 svc[i][r].S = PH[i][r].D0;
1310 std::vector<bool> isopenchain(C,
false), isclosedchain(C,
false);
1311 std::vector<T> lambda(C, zero);
1312 std::vector<Mmap<T>> chainSysArrivals(C);
1313 for (std::size_t c = 0; c < C; ++c) {
1316 for (std::size_t r : L.
inchain[c]) {
1317 const double n = L.
classes[r - 1].population;
1318 if (std::isinf(n)) inf =
true;
1322 isopenchain[c] = inf;
1323 isclosedchain[c] = !inf;
1328 std::vector<Mmap<T>> parts;
1329 for (std::size_t r : L.
inchain[c]) {
1331 if (L.
disabled[ist - 1][r - 1] || !(L.
rates(ist - 1, r - 1) > zero)) {
1341 m.
Dc.assign(1, src.
D1);
1342 lambda[c] += L.
rates(ist - 1, r - 1);
1346 chainSysArrivals[c] = parts[0];
1347 for (std::size_t p = 1; p < parts.size(); ++p)
1349 std::vector<
Mmap<T>>{chainSysArrivals[c], parts[p]},
opt.space_max);
1350 for (std::size_t r : L.
inchain[c])
1351 TN(ist - 1, r - 1) = L.
disabled[ist - 1][r - 1] ? zero : L.
rates(ist - 1, r - 1);
1354 std::vector<bool> finite_srv(M,
false);
1355 for (std::size_t i = 0; i < M; ++i) finite_srv[i] = std::isfinite(L.
stations[i].nservers);
1357 bool ismixed =
false;
1359 bool anyc =
false, anyo =
false;
1360 for (std::size_t c = 0; c < C; ++c) {
1361 anyc = anyc || isclosedchain[c];
1362 anyo = anyo || isopenchain[c];
1364 ismixed = anyc && anyo;
1369 std::vector<std::size_t> all_classes(K);
1370 for (std::size_t r = 0; r < K; ++r) all_classes[r] = r + 1;
1371 auto resptime_floor = [&](
const std::vector<std::size_t>& rlist) {
1372 for (std::size_t i = 0; i < M; ++i) {
1373 if (exact_station[i])
continue;
1374 for (std::size_t r : rlist) {
1375 const std::size_t k = r - 1;
1376 if (V(i, k) > zero) {
1377 if (!finite_srv[i]) {
1380 const T byLittle = (TN(i, k) > zero) ? T(QN(i, k) / TN(i, k)) : zero;
1381 RN(i, k) = (S(i, k) > byLittle) ? S(i, k) : byLittle;
1386 QN(i, k) = T(RN(i, k) * TN(i, k));
1393 double delta = std::numeric_limits<double>::infinity();
1395 while (delta > tol && it <=
opt.iter_max) {
1399 for (std::size_t i = 0; i < M; ++i) {
1400 if (!finite_srv[i])
continue;
1402 for (std::size_t r = 0; r < K; ++r) s += num_traits<T>::to_double(UN(i, r));
1403 if (s > Umax) Umax = s;
1405 if (ismixed || Umax < 1.0) {
1406 for (std::size_t c = 0; c < C; ++c) {
1407 if (!isclosedchain[c])
continue;
1409 for (std::size_t r : L.
inchain[c]) Nc += L.
classes[r - 1].population;
1411 for (std::size_t i = 0; i < M; ++i)
1413 QNc = std::max(tol, QNc);
1415 for (std::size_t i = 0; i < M; ++i) Dsum += dem.
Lchain(i, c);
1422 const double w =
static_cast<double>(it) /
opt.iter_max;
1431 bool binding =
false;
1432 for (std::size_t i = 0; i < M; ++i) {
1433 if (!finite_srv[i])
continue;
1434 double Uopen = 0.0, Uclosed = 0.0;
1435 for (std::size_t c = 0; c < C; ++c) {
1438 if (isclosedchain[c]) Uclosed += u;
1441 if (Uclosed > tol) {
1443 theta = std::min(theta, (Ulim - Uopen) / Uclosed);
1446 if (binding && theta < 1.0)
1447 for (std::size_t c = 0; c < C; ++c)
1448 if (isclosedchain[c])
1450 }
else if (Umax >= 1.0) {
1451 for (std::size_t c = 0; c < C; ++c)
1455 for (std::size_t c = 0; c < C; ++c) {
1456 if (isclosedchain[c]) {
1460 chainSysArrivals[c] =
1463 for (std::size_t i = 0; i < M; ++i)
1464 for (std::size_t r : L.
inchain[c]) TN(i, r - 1) = T(V(i, r - 1) * lambda[c]);
1467 for (std::size_t ind = 1; ind <= I; ++ind) {
1469 if (nd.
station == 0)
continue;
1470 const std::size_t ist = nd.
station;
1471 if (nd.
nodetype == qn::NodeType::Join) {
1472 for (std::size_t c = 0; c < C; ++c)
1473 for (std::size_t r : L.
inchain[c]) {
1474 std::size_t fanin = 0;
1476 for (std::size_t row = 0; row < L.
rtnodes.rows(); ++row)
1477 if (L.
rtnodes(row, (ind - 1) * K + (r - 1)) != zero) ++fanin;
1478 if (fanin == 0) fanin = 1;
1479 TN(ist - 1, r - 1) =
1481 UN(ist - 1, r - 1) = zero;
1482 QN(ist - 1, r - 1) = zero;
1483 RN(ist - 1, r - 1) = zero;
1488 const double ns = L.
stations[ist - 1].nservers;
1489 if (sched == SchedStrategy::INF) {
1490 for (std::size_t c = 0; c < C; ++c)
1491 for (std::size_t r : L.
inchain[c]) {
1492 const std::size_t k = r - 1;
1493 if (!(V(ist - 1, k) > zero)) {
1494 TN(ist - 1, k) = UN(ist - 1, k) = QN(ist - 1, k) = RN(ist - 1, k) = zero;
1497 TN(ist - 1, k) = T(lambda[c] * V(ist - 1, k));
1500 UN(ist - 1, k) = T(S(ist - 1, k) * TN(ist - 1, k));
1501 QN(ist - 1, k) = T(TN(ist - 1, k) * S(ist - 1, k));
1502 RN(ist - 1, k) = (TN(ist - 1, k) > zero)
1503 ? T(QN(ist - 1, k) / TN(ist - 1, k))
1506 }
else if (sched == SchedStrategy::PS) {
1507 for (std::size_t c = 0; c < C; ++c) {
1508 for (std::size_t r : L.
inchain[c]) {
1509 const std::size_t k = r - 1;
1510 if (!(V(ist - 1, k) > zero)) {
1511 TN(ist - 1, k) = UN(ist - 1, k) = zero;
1514 TN(ist - 1, k) = T(lambda[c] * V(ist - 1, k));
1515 UN(ist - 1, k) = T(S(ist - 1, k) * TN(ist - 1, k) /
1519 for (std::size_t k = 0; k < K; ++k) Uden += num_traits<T>::to_double(UN(ist - 1, k));
1521 for (std::size_t r : L.
inchain[c]) {
1522 const std::size_t k = r - 1;
1523 if (!(V(ist - 1, k) > zero)) {
1524 QN(ist - 1, k) = RN(ist - 1, k) = zero;
1529 RN(ist - 1, k) = (TN(ist - 1, k) > zero)
1530 ? T(QN(ist - 1, k) / TN(ist - 1, k))
1534 }
else if (sched == SchedStrategy::FCFS || sched == SchedStrategy::HOL ||
1535 sched == SchedStrategy::FCFSPRPRIO) {
1536 solve_fcfs_station(L,
opt, ist, lambda, V, S, Sknown, PH, PHset, svc,
1537 chainSysArrivals, ztchain, QN, UN, RN, TN, exact_station);
1538 }
else if (sched != SchedStrategy::EXT) {
1545 std::string(
"solver_mam_basic: station '") + L.
stations[ist - 1].name +
1547 " discipline, which the dec.source decomposition does not model (it solves "
1548 "INF, PS, FCFS and HOL stations only)");
1553 resptime_floor(all_classes);
1556 for (std::size_t i = 0; i < M; ++i)
1557 for (std::size_t r = 0; r < K; ++r)
1563 for (std::size_t i = 0; i < M; ++i)
1564 for (std::size_t r = 0; r < K; ++r) QN(i, r) =
num_abs(QN(i, r));
1565 for (
int pass = 0; pass < 2; ++pass) {
1566 for (std::size_t c = 0; c < C; ++c) {
1569 for (std::size_t r : L.
inchain[c]) {
1570 if (std::isinf(L.
classes[r - 1].population)) closed =
false;
1571 else Nc += L.
classes[r - 1].population;
1575 for (std::size_t i = 0; i < M; ++i)
1576 for (std::size_t r : L.
inchain[c]) QNc += QN(i, r - 1);
1579 for (std::size_t i = 0; i < M; ++i)
1580 for (std::size_t r : L.
inchain[c]) QN(i, r - 1) *= f;
1584 if (closed && Nc == 0.0) {
1585 for (std::size_t i = 0; i < M; ++i)
1586 for (std::size_t r : L.
inchain[c]) {
1587 QN(i, r - 1) = UN(i, r - 1) = RN(i, r - 1) = TN(i, r - 1) = zero;
1589 for (std::size_t r : L.
inchain[c]) CN[r - 1] = XN[r - 1] = zero;
1594 for (std::size_t r = 0; r < K; ++r) {
1596 for (std::size_t i = 0; i < M; ++i) s += RN(i, r);
1599 for (std::size_t c = 0; c < C; ++c)
1600 for (std::size_t r : L.
inchain[c]) XN[r - 1] = TN(L.
classes[r - 1].refstat - 1, r - 1);
Acyclic phase-type fitters from the first two moments.
UnsupportedError(const std::string &what)
A network plus its refreshed NetworkStruct.
std::size_t nof_nodes() const
std::vector< std::vector< Distrib< T > > > service
service[i][r], 0-based station and class; a disabled entry marks a pair never visited.
std::vector< std::vector< bool > > disabled
std::vector< JobClass > classes
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 ...
std::vector< std::vector< std::size_t > > inchain
1-based class indices per chain
std::vector< NodeDef > nodes
every node, in creation order
Steady-state distribution of a continuous-time Markov chain.
What refreshProcessRepresentations and refreshLST compute FROM a distribution: the (D0,...
The exception types the port throws.
The option and result types SolverMAM shares with its analyzers.
Markovian arrival process descriptors: stationary vectors, rate, moments, autocorrelation and the ind...
Dense matrix and non-owning view.
The MMAP assembly primitives solver_mam_basic.m builds its per-station arrival stream from: mmap_expo...
Marked MAP (MMAP) algebra: per-class rates, class probabilities, superposition, normalization and sca...
The MMAP[K]/PH[K]/1 FCFS queue: per-class mean number in system and per-class queue-length distributi...
The MMAP[K]/PH[K]/1 priority queue, preemptive resume (mmapph1prpr_*) and non-preemptive (mmapph1nppr...
SnRtStations< T > sn_rt_stations(const qn::NetworkStruct< T > &sn)
mam::Map< T > dist_to_map(const Distrib< T > &d)
SchedStrategy
Scheduling disciplines, with the values of MATLAB SchedStrategy.
ProcessType
Distribution kinds, with the values of MATLAB ProcessType.
const char * sched_to_text(SchedStrategy s)
std::vector< T > map_acf(const Map< T > &m, const std::vector< unsigned > &lags)
Autocorrelation coefficients of the inter-arrival times at the given lags,.
Mmap< T > mmap_normalize(const Mmap< T > &in)
Clamp negative off-diagonal and per-class entries to zero and rebuild D1 and the diagonal of D0 from ...
std::vector< T > mmapph1fcfs_ncmean(const Mmap< T > &arrival, const std::vector< PhService< T > > &svc)
Per-class mean number of customers in the system, BUTools' 'ncMoms', 1.
std::vector< T > mmap_lambda(const Mmap< T > &m)
Alias kept for parity with the MATLAB name.
Mmap< T > mmap_mark_probs(const Mmap< T > &in, const Matrix< T > &prob)
Re-mark an MMAP by a (K x R) probability matrix (mmap_mark.m): a type-k arrival is reported as class ...
Mmap< T > mmap_scale_perclass(const Mmap< T > &in, const std::vector< T > &M)
Retarget the per-class MEAN inter-arrival times (mmap_scale.m, vector form).
QbdMapMap1Result< T > qbd_mapmap1(const Map< T > &arrival, const Map< T > &service_in, const T &util, std::size_t max_levels)
MAP/MAP/1 queue (qbd_mapmap1.m).
T qbd_setupdelayoff(const T &lambda, const T &mu, const T &alpharate, const T &alphascv, const T &betarate, const T &betascv)
Mean queue length of the M/M/1 queue with setup delay and delay-off (qbd_setupdelayoff....
Mmap< T > mmap_exponential_vec(const std::vector< T > &lambda, std::size_t n=1)
Order-n MMAP with the given per-class arrival rates (mmap_exponential.m).
mva::MvaSolution< T > solver_mam_basic(const qn::NetworkStruct< T > &L, const MamOptions &opt)
Port of solver_mam_basic.m.
std::vector< std::vector< T > > mmapph1prpr_ncmoms(const Mmap< T > &arrival, const std::vector< PhService< T > > &svc, std::size_t n, const PrioQueueOptions &opt=PrioQueueOptions())
Per-class moments 1..n of the number of jobs, MMAP[K]/PH[K]/1 preemptive resume priority.
std::vector< std::vector< T > > mmapph1fcfs_ncdistr(const Mmap< T > &arrival, const std::vector< PhService< T > > &svc, std::size_t levels)
Per-class queue-length distribution, BUTools' 'ncDistr', n: P(N_k = 0..n-1).
Map< T > map_exponential_mean(const T &mean)
Poisson process with the given mean inter-arrival time (map_exponential.m).
Map< T > map_scale_rate(const Map< T > &in, const T &new_mean)
Rescale to a target mean WITHOUT the feasibility repair, for a matrix exponential.
Map< T > map_scale(const Map< T > &in, const T &new_mean)
Rescale time so that the mean inter-arrival time becomes new_mean.
std::vector< T > map_pie(const Map< T > &m)
Phase distribution seen by an arriving job, pie = pi D1 / (pi D1 e).
T map_scv(const Map< T > &m)
Squared coefficient of variation.
SetupDelayoffClosed< T > qbd_setupdelayoff_closed(const T &N, const T &Z, const T &mu, const T &alpharate, const T &alphascv, const T &betarate, const T &betascv)
Mean queue length and throughput of a FINITE-POPULATION queue with setup delay and delay-off,...
T map_lambda(const Map< T > &m)
Stationary arrival rate, lambda = pi D1 e.
std::vector< std::vector< T > > mmapph1nppr_ncmoms(const Mmap< T > &arrival, const std::vector< PhService< T > > &svc, std::size_t n, const PrioQueueOptions &opt=PrioQueueOptions())
Per-class moments 1..n of the number of jobs, MMAP[K]/PH[K]/1 non-preemptive priority.
Mmap< T > mmap_super_safe(const std::vector< Mmap< T > > &in, std::size_t maxorder)
Order-bounded superposition of several MMAPs (mmap_super_safe.m).
std::vector< T > ctmc_solve(const Matrix< T > &Qin)
Steady-state distribution of a continuous-time Markov chain.
ChainDemands< T > sn_get_demands_chain(const qn::NetworkStruct< T > &L)
Port of sn_get_demands_chain.
MapMcResult< T > qsys_mapmc(const mam::Map< T > &arrival, const T &mu, unsigned c, std::size_t dist_size)
MAP/M/c by the matrix-geometric solution.
MmapGk1Result< T > qsys_mmapgk1(const std::vector< Matrix< T > > &MMAP, const std::vector< lang::Distrib< T > > &svc, const std::vector< T > &w_points, std::size_t num_w_moms, double tol, std::size_t iter_max)
MMAP[K]/G[K]/1 FCFS, per type.
MmckResult< T > qsys_mmck(const T &lambda, const T &mu, unsigned c, unsigned K)
Exact analysis of the M/M/c/K queue (truncated Erlang form).
MapPhcResult< T > qsys_mapphc(const mam::Map< T > &arrival, const std::vector< T > &alpha, const Matrix< T > &S, unsigned c, std::size_t dist_size, std::size_t num_w_moms, const std::vector< T > &w_points)
MAP/PH/c FCFS, exactly.
MapDcResult< T > qsys_mapdc(const mam::Map< T > &arrival, const T &s, unsigned c, std::size_t dist_size, unsigned max_arrivals, std::size_t max_levels, const T &tol)
MAP/D/c by Crommelin's exact embedded lattice chain.
MmapG1kResult< T > qsys_mmapg1k(const Matrix< T > &D0, const std::vector< Matrix< T > > &D1c, const ServiceLaw< T > &svc, std::size_t K, const T &tol, std::size_t nmaxCap)
MMAP[K]/G/1/K with tail drop.
DmcResult< T > qsys_dmc(const T &lambda, const T &mu, unsigned c, unsigned truncation, unsigned quadSteps)
D/M/c: deterministic interarrival times, exponential service.
PhMcResult< T > qsys_phmc(const std::vector< T > &alpha, const Matrix< T > &Tm, const T &mu, unsigned c, unsigned maxIter, const T &tol)
Exact PH/M/c by Neuts' matrix-geometric method.
double sn_get_buffer_size(const qn::NetworkStruct< T > &sn, std::size_t ist)
Physical buffer size of a station, in jobs, the one in service included.
Conservation laws of a layered queueing network, enumerated from its structure.
std::vector< T > vecmul(const std::vector< T > &v, const Matrix< T > &A)
Row vector times matrix, v A.
Matrix< T > inverse(const Matrix< T > &A)
Inverse by LU with one factorization and n back substitutions.
A queueing network and its refreshed NetworkStruct.
The MAP/MAP/1 queue solved as a quasi-birth-death process.
Mean queue length of an M/M/1 queue with a setup delay and a delayed-off period, solved as a QBD.
D/M/c: deterministic interarrival times, exponential service.
The MAP/D/c FCFS queue: c servers, deterministic service of length s, fed by a Markovian arrival proc...
The MAP/M/c FCFS queue: c identical exponential servers of rate mu fed by a Markovian arrival process...
The MAP/PH/c FCFS queue, solved exactly.
Exact per-class throughput and loss ratio of an MMAP[K]/G/1/K tail-drop queue.
The MMAP[K]/G[K]/1 FCFS queue: K customer types with class-dependent GENERAL service,...
Exact analysis of the M/M/c/K queue (truncated Erlang form).
Exact PH/M/c by Neuts' matrix-geometric method.
Chain aggregation and de-aggregation.
Physical buffer size of a station, in jobs, the one in service included.
Port of matlab/src/api/sn/sn_rt_stations.m.
static constexpr double Immediate
Rate of an Immediate distribution; its mean is 1/Immediate = 1e-8.
static constexpr double FineTol
static constexpr double CoarseTol
The options SolverMAM reads.
A MAP as the pair of matrices (D0, D1).
An MMAP: the underlying MAP plus the per-class arrival matrices.
std::vector< Matrix< T > > Dc
per-class matrices, sum_c Dc = D1
One class's phase-type service law, He's (sigma_k, S_k).
The chain-level view of a layer, as sn_get_demands_chain returns it.
Matrix< T > Lchain
(M x C) demand
Class-level results, the [Q,U,R,T,C,X] of the MATLAB analyzers.
static ServiceLaw phase_type(const std::vector< T > &alpha, const Matrix< T > &Tmat)
Phase type (alpha, Tmat).