162 std::vector<std::size_t> lcfs, lcfspr;
163 for (std::size_t i = 0; i < M; ++i) {
164 if (L.
stations[i].sched == SchedStrategy::LCFS) lcfs.push_back(i + 1);
165 if (L.
stations[i].sched == SchedStrategy::LCFSPR) lcfspr.push_back(i + 1);
167 if (!lcfs.empty() && !lcfspr.empty()) {
168 if (lcfs.size() != 1 || lcfspr.size() != 1)
170 "solver_mva: LCFS MVA requires exactly one LCFS and one LCFS-PR station");
171 for (std::size_t c = 0; c < C; ++c)
172 if (std::isinf(d.
Nchain[c]))
173 throw UnsupportedError(
"solver_mva: LCFS MVA requires a closed queueing network");
177 for (std::size_t ist : {lcfs[0], lcfspr[0]}) {
179 for (std::size_t r = 0; r < Rn; ++r)
180 if (L.
rt.rows() == S * Rn && L.
rt(sf * Rn + r, sf * Rn + r) > zero)
182 "solver_mva: LCFS MVA does not support self-loops at stations");
187 throw UnsupportedError(
"solver_mva: LCFS scheduling requires a paired LCFS-PR station");
194 throw UnsupportedError(
"solver_mva: the layer does not have a product form");
196 std::vector<std::size_t> infSET, qSET;
197 for (std::size_t i = 0; i < M; ++i) {
199 case SchedStrategy::EXT:
break;
200 case SchedStrategy::INF: infSET.push_back(i);
break;
201 case SchedStrategy::PS:
202 case SchedStrategy::LCFSPR:
203 case SchedStrategy::FCFS:
204 case SchedStrategy::SIRO: qSET.push_back(i);
break;
206 throw UnsupportedError(std::string(
"solver_mva: unsupported exact MVA analysis for ") +
212 Matrix<T> Lq(qSET.size(), C, zero), Zd(infSET.size(), C, zero);
213 for (std::size_t a = 0; a < qSET.size(); ++a)
214 for (std::size_t c = 0; c < C; ++c)
216 for (std::size_t a = 0; a < infSET.size(); ++a)
217 for (std::size_t c = 0; c < C; ++c)
218 Zd(a, c) = T(d.
STchain(infSET[a], c) * d.
Vchain(infSET[a], c));
221 std::vector<T> lambda(C, zero);
222 std::vector<int> N(C, 0);
223 for (std::size_t c = 0; c < C; ++c) {
224 if (!std::isfinite(d.
Nchain[c])) {
230 N[c] =
static_cast<int>(std::llround(d.
Nchain[c]));
233 for (std::size_t a = 0; a < qSET.size(); ++a) {
234 const double s = L.
stations[qSET[a]].nservers;
235 if (!std::isfinite(s))
236 throw UnsupportedError(
"solver_mva: a queueing station has infinitely many servers but "
237 "is not inf-scheduled");
238 S.push_back(
static_cast<int>(std::llround(s)));
251 Matrix<T> Qchain(M, C, zero), Wchain(M, C, zero), Tchain(M, C, zero), Uchain(M, C, zero);
252 std::vector<T> Xchain = pf.
XN;
253 for (std::size_t a = 0; a < qSET.size(); ++a)
254 for (std::size_t c = 0; c < C; ++c) Qchain(qSET[a], c) = pf.
QN(a, c);
255 for (std::size_t a = 0; a < infSET.size(); ++a)
256 for (std::size_t c = 0; c < C; ++c)
257 Qchain(infSET[a], c) = T(Xchain[c] * d.
STchain(infSET[a], c) * d.
Vchain(infSET[a], c));
259 std::vector<std::size_t> rset;
260 for (std::size_t c = 0; c < C; ++c)
261 if (d.
Nchain[c] != 0.0) rset.push_back(c);
263 for (std::size_t c : rset) {
264 for (std::size_t i : infSET) Wchain(i, c) = d.
STchain(i, c);
265 for (std::size_t i : qSET) {
266 if (std::isinf(L.
stations[i].nservers)) {
267 Wchain(i, c) = d.
STchain(i, c);
268 }
else if (d.
Vchain(i, c) == zero || Xchain[c] == zero) {
271 Wchain(i, c) = T(Qchain(i, c) / (Xchain[c] * d.
Vchain(i, c)));
276 std::vector<T> Cc(C, zero);
277 for (std::size_t c : rset) {
279 for (std::size_t i = 0; i < M; ++i) sw += Wchain(i, c);
284 for (std::size_t i = 0; i < M; ++i) cyc += d.
Vchain(i, c) * Wchain(i, c);
287 if (cyc != zero && std::isfinite(d.
Nchain[c]))
290 for (std::size_t i = 0; i < M; ++i) {
291 Qchain(i, c) = T(Xchain[c] * d.
Vchain(i, c) * Wchain(i, c));
292 Tchain(i, c) = T(Xchain[c] * d.
Vchain(i, c));
295 for (std::size_t i = 0; i < M; ++i)
296 for (std::size_t c : rset) {
298 Uchain(i, c) = std::isinf(L.
stations[i].nservers)
304 for (std::size_t i = 0; i < M; ++i) {
305 if (!(L.
stations[i].sched == SchedStrategy::FCFS || L.
stations[i].sched == SchedStrategy::PS))
308 for (std::size_t c = 0; c < C; ++c) usum += Uchain(i, c);
311 for (std::size_t c = 0; c < C; ++c) den += d.
Vchain(i, c) * d.
STchain(i, c) * Xchain[c];
312 if (den == zero)
continue;
314 for (std::size_t c = 0; c < C; ++c) {
316 Uchain(i, c) = T(cap * d.
Vchain(i, c) * d.
STchain(i, c) * Xchain[c] / den);
321 for (std::size_t i = 0; i < M; ++i)
322 for (std::size_t c = 0; c < C; ++c)
323 if (Tchain(i, c) != zero) Rchain(i, c) = T(Qchain(i, c) / Tchain(i, c));
324 for (std::size_t c = 0; c < C; ++c) {
325 if (d.
Nchain[c] != 0.0)
continue;
327 for (std::size_t i = 0; i < M; ++i) {
460 const MvaOptions&
opt,
const std::string& method,
bool& converged,
475 "solver_amvald: the approximate MVA is an iterative fixed point whose iterates "
476 "accumulate the product of every denominator seen so far, so exact rational "
477 "arithmetic grows without bound and the solve does not terminate; rerun with "
478 "--arith double or --arith real, or give the model a product form (BCMP type 1 "
479 "asks FCFS service to be exponential) so the exact recursion applies");
485 for (std::size_t i = 0; i < M; ++i) {
487 if (!(s == SchedStrategy::INF || s == SchedStrategy::PS || s == SchedStrategy::FCFS ||
488 s == SchedStrategy::SIRO || s == SchedStrategy::LCFSPR || s == SchedStrategy::EXT ||
489 s == SchedStrategy::DPS || s == SchedStrategy::HOL ||
490 s == SchedStrategy::FCFSPRPRIO))
492 " scheduling is not implemented in this port");
503 bool any_queue =
false;
504 for (std::size_t i = 0; i < M && !any_queue; ++i)
505 if (L.
stations[i].sched != SchedStrategy::INF && L.
stations[i].sched != SchedStrategy::EXT)
507 const bool linmethod = (method ==
"lin" || method ==
"qdlin");
509 !(linmethod || method ==
"qd" || method ==
"default" || method ==
"bs" ||
510 method ==
"egflin" || method ==
"gflin" || method ==
"qli" || method ==
"fli" ||
511 method ==
"aql" || method ==
"qdaql" || method ==
"tay" || method ==
"priomva"))
512 throw UnsupportedError(
"solver_amvald: method '" + method +
"' is not implemented");
516 for (std::size_t c = 0; c < K; ++c)
520 std::vector<T> deltaclass(K, one);
521 for (std::size_t c = 0; c < K; ++c)
526 std::vector<std::size_t> nnz, ccl, ocl;
527 std::vector<bool> isopen(K,
false);
528 for (std::size_t c = 0; c < K; ++c) {
529 if (!(d.
Nchain[c] > 0.0))
continue;
531 if (std::isfinite(d.
Nchain[c])) ccl.push_back(c);
532 else { ocl.push_back(c); isopen[c] =
true; }
542 std::vector<std::size_t> ocl_exo, ocl_aux;
543 for (std::size_t c : ocl) {
546 allaux = !L.
inchain[c].empty();
547 for (std::size_t r : L.
inchain[c])
550 if (allaux) ocl_aux.push_back(c);
else ocl_exo.push_back(c);
555 if (init_sol.rows() == M && init_sol.cols() == K) {
559 for (std::size_t c = 0; c < K; ++c) {
560 if (!std::isfinite(d.
Nchain[c]))
continue;
561 for (std::size_t i = 0; i < M; ++i)
567 std::vector<T> Xchain(K, zero);
568 for (std::size_t c = 0; c < K; ++c) {
572 Xchain[c] = st > zero ? T(one / st) : zero;
576 for (std::size_t i = 0; i < M; ++i) s += d.
STchain(i, c);
577 if (s != zero) Xchain[c] = T(one / s);
579 Matrix<T> Uchain(M, K, zero), Tchain(M, K, zero), Wchain(M, K, zero), STeff = d.
STchain;
580 for (std::size_t i = 0; i < M; ++i)
581 for (std::size_t c : nnz) {
583 Uchain(i, c) = std::isinf(L.
stations[i].nservers)
589 std::vector<Matrix<T>> gamma(K,
Matrix<T>(M, K, zero));
593 const int max_totiter = std::min(
opt.iter_max, 10000);
595 const int inner_cap =
static_cast<int>(std::sqrt(
static_cast<double>(
opt.iter_max)));
599 std::vector<int> chainprio(K, 0);
600 for (std::size_t c = 0; c < K && c < L.
inchain.size(); ++c) {
602 for (std::size_t k1 : L.
inchain[c]) {
603 const int p = L.
classes[k1 - 1].prio;
604 chainprio[c] = first ? p : std::min(chainprio[c], p);
609 const bool has_prio = [&] {
612 for (std::size_t c = 0; c < K; ++c) {
613 const int p = chainprio[c];
614 if (first) { lo = hi = p; first =
false; }
615 lo = std::min(lo, p);
616 hi = std::max(hi, p);
620 std::vector<std::vector<std::size_t>> ehprio(K), hprio(K), eprio(K), lprio(K);
621 for (std::size_t r : nnz) {
624 for (std::size_t s : nnz) {
625 ehprio[r].push_back(s);
626 eprio[r].push_back(s);
630 for (std::size_t s : nnz) {
631 const int pr = chainprio[r], ps = chainprio[s];
632 if (ps <= pr) ehprio[r].push_back(s);
633 if (ps < pr) hprio[r].push_back(s);
634 if (ps == pr) eprio[r].push_back(s);
635 if (ps > pr) lprio[r].push_back(s);
640 std::size_t smax = 0;
641 for (
const auto& st : L.
stations) smax = std::max(smax, st.lldscaling.size());
645 for (std::size_t i = 0; i < M; ++i)
646 for (std::size_t k = 0; k < L.
stations[i].lldscaling.size(); ++k)
647 lldscaling(i, k) = L.
stations[i].lldscaling[k];
649 std::vector<lang::CdScaling<T>> cdscaling;
655 if (!cdscaling.empty())
656 for (std::size_t i = 0; i < M; ++i) cdscaling[i] = L.
stations[i].cdscaling;
661 std::vector<lang::CdScaling<T>> jdscaling;
667 if (!jdscaling.empty())
668 for (std::size_t i = 0; i < M; ++i) jdscaling[i] = L.
stations[i].jdscaling;
669 const bool has_scaling = (smax > 0) || !cdscaling.empty() || !jdscaling.empty();
672 std::vector<std::vector<T>> tau(K, std::vector<T>(K, zero));
684 auto forward = [&](
const Matrix<T>& Qin,
const std::vector<T>& Xin,
const Matrix<T>& Uin,
687 for (std::size_t c = 0; c < K; ++c)
688 if (std::isfinite(Nc[c])) Ntl += Nc[c];
690 std::vector<T> dcl(K, one);
691 for (std::size_t c = 0; c < K; ++c)
692 if (std::isfinite(Nc[c]) && Nc[c] > 0.0)
695 std::vector<T> interpTot(M, zero);
696 Matrix<T> selfArvl(M, K, zero), totArvl(M, K, zero), totArvlOpen(M, K, zero);
697 for (std::size_t i = 0; i < M; ++i) {
699 for (std::size_t c : nnz) s += Qin(i, c);
712 T sc = zero, so = zero;
713 for (std::size_t c : ccl) sc += Qin(i, c);
714 for (std::size_t c : ocl_aux) sc += Qin(i, c);
715 for (std::size_t c : ocl_exo) so += Qin(i, c);
716 interpTot[i] = T(deltaL * sc + so);
717 const bool hol = L.
stations[i].sched == SchedStrategy::HOL;
718 for (std::size_t c : nnz) {
719 selfArvl(i, c) = T(dcl[c] * Qin(i, c));
721 T eh = zero, ehx = zero;
722 for (std::size_t s2 : ehprio[c]) {
724 if (s2 != c) ehx += Qin(i, s2);
726 totArvlOpen(i, c) = eh;
727 totArvl(i, c) = T(dcl[c] * Qin(i, c) + ehx);
729 totArvlOpen(i, c) = s;
730 totArvl(i, c) = T(dcl[c] * Qin(i, c) + s - Qin(i, c));
736 std::vector<T> gmean(M, zero);
741 for (std::size_t i = 0; i < M; ++i) {
743 for (std::size_t s : ccl) {
745 for (std::size_t r : ccl)
747 acc += T(deltaL * gs);
749 gmean[i] = T(acc / nccl);
754 for (std::size_t i = 0; i < M; ++i)
755 for (std::size_t r : ccl)
758 for (std::size_t i = 0; i < M; ++i) gmean[i] = sc;
761 std::vector<T> narrival(M, zero);
762 for (std::size_t i = 0; i < M; ++i) narrival[i] = T(one + interpTot[i] + gmean[i]);
763 const std::vector<T> msterm = detail::ms_term(L, narrival,
opt.multiserver);
764 std::vector<T> suri(M, one);
765 if (
opt.multiserver ==
"suri") suri = detail::suri_factor(L, Uin,
opt.tol);
773 "solver_amvald: load-dependent scaling interpolates a rate lattice with a "
774 "soft minimum, which exact arithmetic has no representation for; use the "
775 "double or real backend");
777 if (linmethod && !ccl.empty() && !nnz.empty()) {
778 for (std::size_t r : nnz) {
779 std::vector<T> arg(M, zero);
780 for (std::size_t i = 0; i < M; ++i) {
782 for (std::size_t s : ccl)
784 gcorr -= gamma[r](i, r);
785 arg[i] = T(one + interpTot[i] + gcorr);
787 const std::vector<T> v =
789 for (std::size_t i = 0; i < M; ++i) lldterm(i, r) = v[i];
792 std::vector<T> arg(M, zero);
793 for (std::size_t i = 0; i < M; ++i) arg[i] = T(one + interpTot[i]);
794 const std::vector<T> v =
796 for (std::size_t i = 0; i < M; ++i)
797 for (std::size_t c = 0; c < K; ++c) lldterm(i, c) = v[i];
804 if (!cdscaling.empty()) {
805 for (std::size_t r : nnz) {
807 const bool closed = std::isfinite(Nc[r]);
808 for (std::size_t i = 0; i < M; ++i)
809 for (std::size_t c = 0; c < K; ++c)
810 arg(i, c) = T(one + (closed ? selfArvl(i, c) : Qin(i, c)));
811 if (closed && linmethod)
812 for (std::size_t i = 0; i < M; ++i) {
815 for (std::size_t c = 0; c < K; ++c) arg(i, c) = T(arg(i, c) + gself);
818 for (std::size_t i = 0; i < M; ++i) cdterm(i, r) = v[i];
834 if (!jdscaling.empty()) {
835 for (std::size_t r : nnz) {
836 const bool closed = std::isfinite(Nc[r]);
838 for (std::size_t i = 0; i < M; ++i) {
839 for (std::size_t c : nnz) arg(i, c) = Qin(i, c);
840 T self = T(one + (closed ? selfArvl(i, r) : Qin(i, r)));
841 if (closed && linmethod)
846 for (std::size_t i = 0; i < M; ++i) jdterm(i, r) = v[i];
851 for (std::size_t c : nnz)
852 for (std::size_t i = 0; i < M; ++i)
854 T(d.
STchain(i, c) * lldterm(i, c) * msterm[i] * cdterm(i, c) * jdterm(i, c));
858 if (method ==
"qli" || method ==
"fli") {
859 const bool qli = (method ==
"qli");
860 for (std::size_t i = 0; i < M; ++i) {
861 const bool hol = L.
stations[i].sched == SchedStrategy::HOL;
862 for (std::size_t r : nnz) {
865 for (std::size_t s2 : ehprio[r]) tot += Qin(i, s2);
867 for (std::size_t s2 : nnz) tot += Qin(i, s2);
870 totArvl(i, r) = T(tot - Qin(i, r));
873 const T num = T(STeffOut(i, r) * (one + tot - Qin(i, r)));
875 for (std::size_t m = 0; m < M; ++m)
876 if (L.
stations[m].sched == SchedStrategy::INF) den += STeffOut(m, r);
877 for (std::size_t m = 0; m < M; ++m) {
879 if (L.
stations[m].sched == SchedStrategy::HOL) {
880 for (std::size_t s2 : ehprio[r]) totm += Qin(m, s2);
882 for (std::size_t s2 : nnz) totm += Qin(m, s2);
884 den += T(STeffOut(m, r) * (one + totm - Qin(m, r)));
886 if (den == zero)
continue;
889 totArvl(i, r) = T(tot - f * (Qin(i, r) - num / den));
892 totArvl(i, r) = T(tot - f * Qin(i, r) + num / den);
903 if (!ILchain.
empty()) {
904 for (std::size_t i = 0; i < M; ++i)
905 for (std::size_t c : nnz) {
907 for (std::size_t c2 : nnz)
908 if (c2 != c) ilq += ILchain(c, c2) * Qin(i, c2);
910 T adj = T(totArvl(i, c) - ilq);
911 if (adj < selfArvl(i, c)) adj = selfArvl(i, c);
918 for (std::size_t c : nnz) {
919 for (std::size_t i = 0; i < M; ++i) {
922 if (sc == SchedStrategy::EXT)
continue;
923 if (sc == SchedStrategy::INF) {
924 Wout(i, c) = STeffOut(i, c);
927 const double cs = L.
stations[i].nservers;
929 if (sc == SchedStrategy::PS) {
932 for (std::size_t s : ccl)
934 corr -= gamma[c](i, c);
938 if (
opt.multiserver ==
"suri") {
941 const T Lm = isopen[c] ? totArvlOpen(i, c)
942 : T(totArvl(i, c) + corr);
943 Wout(i, c) = T(STeffOut(i, c) * (one + Lm * suri[i]));
946 if (
opt.multiserver ==
"seidmann")
950 Wout(i, c) = T(Wout(i, c) + STeffOut(i, c) * (one + totArvlOpen(i, c)));
958 Wout(i, c) = T(Wout(i, c) + STeffOut(i, c) * (one + totArvl(i, c) + corr));
963 if (sc == SchedStrategy::DPS) {
966 const std::vector<T>& w = L.
stations[i].schedparam;
969 "solver_amvald: the DPS station '" + L.
stations[i].name +
970 "' has no per-class weights");
972 if (cs > 1.0 && std::isfinite(cs))
974 acc += T(STeffOut(i, c) * (one + selfArvl(i, c)));
975 for (std::size_t s : nnz) {
976 if (s == c)
continue;
978 acc += T(STeffOut(i, c) * Qin(i, s));
980 acc += T(STeffOut(i, c) * Qin(i, s) * w[s] / w[c]);
986 const bool fcfs_family = (sc == SchedStrategy::FCFS || sc == SchedStrategy::SIRO ||
987 sc == SchedStrategy::LCFSPR);
988 if (!fcfs_family && sc != SchedStrategy::HOL && sc != SchedStrategy::FCFSPRPRIO)
990 " scheduling is not implemented in this port");
991 if (STeffOut(i, c) <= zero)
continue;
993 const std::vector<T>& Xarv = Xref.empty() ? Xin : Xref;
1006 if (sc == SchedStrategy::FCFSPRPRIO) {
1007 if (cs > 1.0 && std::isfinite(cs))
1009 "solver_amvald: FCFSPRPRIO with more than one server. The "
1010 "preemptive-resume priority arm (priomva) is implemented for "
1011 "single-server stations only; use SolverCTMC or SolverSSA for "
1012 "multiserver PRS.");
1016 for (std::size_t h : hprio[c])
1017 uh_prs += T(d.
Vchain(i, h) * STeffOut(i, h) * T(Xarv[h] + tau[c][h]));
1019 vps = std::max(
opt.tol, std::min(vps, 1.0 -
opt.tol));
1023 T queued = T(STeffOut(i, c) * (isopen[c] ? Qin(i, c) : selfArvl(i, c)));
1024 for (std::size_t s : ehprio[c])
1025 if (s != c) queued += T(STeffOut(i, s) * Qin(i, s));
1027 Wout(i, c) = T((STeffOut(i, c) + queued) / ps_prs);
1031 auto Ur = [&](std::size_t k, std::size_t s) -> T {
1032 if (!(Xin[s] > zero))
return Uin(k, s);
1033 return T(Uin(k, s) / Xin[s] * (Xarv[s] + tau[c][s]));
1037 auto prio_scaling = [&](std::size_t r) -> T {
1038 if (sc != SchedStrategy::HOL)
return one;
1040 for (std::size_t h : hprio[r]) {
1043 const T xh =
opt.np_priority ==
"shadow" ? Xarv[h] : T(Xarv[h] + tau[r][h]);
1044 uh += T(d.
Vchain(i, h) * STeffOut(i, h) * xh);
1047 v = std::max(
opt.tol, std::min(v, 1.0 -
opt.tol));
1050 const T ps_r = prio_scaling(c);
1057 auto hol_ehprio_backlog = [&](
const std::vector<T>& Bkw) -> T {
1059 for (std::size_t s : ehprio[c])
1060 if (s != c) acc += T(STeffOut(i, s) * Qin(i, s) * Bkw[s]);
1063 auto hol_np_residual = [&](
const std::vector<T>& Bkw) -> T {
1064 if (
opt.highvar ==
"hvmva")
return zero;
1066 for (std::size_t s : lprio[c])
1067 acc += T(d.
Vchain(i, s) * STeffOut(i, s) * (Xarv[s] + tau[c][s]) *
1068 STeffOut(i, s) * Bkw[s]);
1073 std::vector<T> Bk(K, one);
1074 if (cs > 1.0 && std::isfinite(cs)) {
1076 for (std::size_t s : nnz) {
1077 const T dr = (sc == SchedStrategy::HOL) ? dcl[s] : ((s == c) ? dcl[c] : one);
1078 load += dr * Xin[s] * d.
Vchain(i, s) * STeffOut(i, s);
1081 for (std::size_t s : nnz) {
1082 const T dr = (sc == SchedStrategy::HOL) ? dcl[s] : ((s == c) ? dcl[c] : one);
1083 T base = T(dr * Xin[s] * d.
Vchain(i, s) * STeffOut(i, s));
1084 if (sc == SchedStrategy::HOL &&
opt.multiserver !=
"softmin")
1089 const unsigned e =
static_cast<unsigned>(
1090 std::llround(cs) - (sc == SchedStrategy::HOL ? 0 : 1));
1099 if (
opt.highvar ==
"hvmva") {
1101 for (std::size_t s : ccl) usum += Ur(i, s);
1102 w = T(STeffOut(i, c) * (one - usum));
1103 for (std::size_t s : ccl)
1104 w += T(STeffOut(i, s) / prio_scaling(s) * Ur(i, s) *
1106 }
else if (
opt.highvar !=
"default") {
1108 "' is not implemented");
1112 if (sc == SchedStrategy::HOL) {
1114 const std::vector<T>
ones(K, one);
1115 w += T((STeffOut(i, c) * (isopen[c] ? Qin(i, c) : selfArvl(i, c)) +
1116 hol_ehprio_backlog(
ones) + hol_np_residual(
ones)) / ps_r);
1118 w += STeffOut(i, c) * (isopen[c] ? Qin(i, c) : selfArvl(i, c));
1119 for (std::size_t s : nnz)
1120 if (s != c) w += STeffOut(i, s) * Qin(i, s);
1127 if (
opt.multiserver ==
"suri") {
1128 T Lm = isopen[c] ? T(deltaclass[c] * Qin(i, c)) : selfArvl(i, c);
1129 if (sc != SchedStrategy::HOL)
1130 for (std::size_t s : nnz)
1131 if (s != c) Lm += Qin(i, s);
1132 if (sc == SchedStrategy::HOL) {
1133 Lm = isopen[c] ? Qin(i, c) : selfArvl(i, c);
1134 const std::vector<T>
ones(K, one);
1135 Wout(i, c) = T(STeffOut(i, c) +
1136 ((STeffOut(i, c) * Lm + hol_ehprio_backlog(
ones)) * suri[i] +
1137 hol_np_residual(
ones)) / ps_r);
1140 Wout(i, c) = T(STeffOut(i, c) / ps_r + STeffOut(i, c) * Lm * suri[i] / ps_r);
1143 if (
opt.multiserver ==
"softmin") {
1144 T w = STeffOut(i, c);
1145 if (sc == SchedStrategy::HOL)
1146 w += T((STeffOut(i, c) * (isopen[c] ? Qin(i, c) : selfArvl(i, c)) * Bk[c] +
1147 hol_ehprio_backlog(Bk) + hol_np_residual(Bk)) / ps_r);
1149 w += T(STeffOut(i, c) * (isopen[c] ? Qin(i, c) : selfArvl(i, c)) * Bk[c] / ps_r);
1150 if (sc != SchedStrategy::HOL)
1151 for (std::size_t s : nnz)
1152 if (s != c) w += STeffOut(i, s) * Bk[s] * Qin(i, s);
1158 w += STeffOut(i, c);
1159 if (sc == SchedStrategy::HOL) {
1160 w += T((STeffOut(i, c) * (isopen[c] ? Qin(i, c) : selfArvl(i, c)) * Bk[c] +
1161 hol_ehprio_backlog(Bk) + hol_np_residual(Bk)) / ps_r);
1163 w += STeffOut(i, c) *
1164 (isopen[c] ? T(deltaclass[c] * Qin(i, c)) : selfArvl(i, c)) * Bk[c];
1165 for (std::size_t s : nnz)
1166 if (s != c) w += STeffOut(i, s) * Bk[s] * Qin(i, s);
1180 const std::vector<T> Xprev = X;
1183 forward(Qprev, Xprev, Uprev, Nc, Wout, STeffOut);
1187 for (std::size_t i = 0; i < Q.
rows(); ++i)
1188 for (std::size_t c = 0; c < Q.
cols(); ++c)
1192 "AMVA sweep %d: queue-length residual %.3e",
1195 if (totiter >= max_totiter)
break;
1196 for (std::size_t c : nnz) {
1198 for (std::size_t i = 0; i < M; ++i) sw += Wout(i, c);
1201 }
else if (!isopen[c]) {
1203 for (std::size_t i = 0; i < M; ++i) cyc += d.
Vchain(i, c) * Wout(i, c);
1206 (one - omicron) * Xprev[c]);
1210 for (std::size_t i = 0; i < M; ++i) {
1211 Q(i, c) = T(omicron * X[c] * d.
Vchain(i, c) * Wout(i, c) +
1212 (one - omicron) * Qprev(i, c));
1213 U(i, c) = T(omicron * d.
Vchain(i, c) * STeffOut(i, c) * X[c] +
1214 (one - omicron) * Uprev(i, c));
1219 for (std::size_t i = 0; i < M; ++i)
1220 for (std::size_t c = 0; c < K; ++c)
1222 if (err <=
opt.iter_tol)
break;
1224 if (iter > inner_cap)
break;
1234 QouterPrev = Qchain;
1235 const std::vector<T> XouterPrev = Xchain;
1237 if (linmethod) Xref = XouterPrev;
1240 if (linmethod && std::isfinite(Nt) && Nt > 0.0) {
1241 for (std::size_t s = 0; s < K; ++s) {
1242 if (!std::isfinite(d.
Nchain[s]))
continue;
1243 std::vector<double> Ns = d.
Nchain;
1247 std::vector<T> Xs = Xchain;
1249 for (std::size_t i = 0; i < M; ++i)
1250 for (std::size_t c = 0; c < K; ++c) {
1251 Qs(i, c) = T(Qs(i, c) * shrink);
1252 Us(i, c) = T(Us(i, c) * shrink);
1254 for (std::size_t c = 0; c < K; ++c) Xs[c] = T(Xs[c] * shrink);
1256 const std::vector<T> Xs_in = Xs;
1257 const Matrix<T> Qs_prev = inner_loop(Qs, Xs, Us, Ns, Ws, STs);
1259 for (std::size_t c : nnz) tau[s][c] = T(Xs[c] - XouterPrev[c]);
1278 if (method ==
"qdlin") {
1279 for (std::size_t i = 0; i < M; ++i) {
1280 T qs = zero, qo = zero;
1281 for (std::size_t c = 0; c < K; ++c) {
1282 qs += Qs_prev(i, c);
1283 qo += QouterPrev(i, c);
1294 for (std::size_t i = 0; i < M; ++i)
1295 for (std::size_t c : nnz)
1296 if (std::isfinite(d.
Nchain[c]) && Ns[c] > 0.0)
1301 if (totiter >= max_totiter)
break;
1304 if (totiter >= max_totiter)
break;
1307 const Matrix<T> Qprev = inner_loop(Qchain, Xchain, Uchain, d.
Nchain, Wtmp, STtmp);
1312 for (std::size_t i = 0; i < M; ++i)
1313 for (std::size_t c = 0; c < K; ++c)
1315 converged = err <=
opt.iter_tol;
1316 if (outer >= 2 && converged)
break;
1317 if (outer >= inner_cap || totiter > max_totiter)
break;
1320 for (std::size_t i = 0; i < M; ++i)
1321 for (std::size_t c = 0; c < K; ++c) Tchain(i, c) = T(Xchain[c] * d.
Vchain(i, c));
1324 for (std::size_t i = 0; i < M; ++i) {
1326 if (!(sc == SchedStrategy::FCFS || sc == SchedStrategy::SIRO || sc == SchedStrategy::PS ||
1327 sc == SchedStrategy::LCFSPR || sc == SchedStrategy::DPS || sc == SchedStrategy::HOL))
1330 for (std::size_t c = 0; c < K; ++c) usum += Uchain(i, c);
1333 for (std::size_t c = 0; c < K; ++c) den += d.
Vchain(i, c) * STeff(i, c) * Xchain[c];
1334 if (den == zero)
continue;
1335 for (std::size_t c = 0; c < K; ++c) {
1336 if (!(d.
Vchain(i, c) * STeff(i, c) > zero))
continue;
1337 Uchain(i, c) = T(one * d.
Vchain(i, c) * STeff(i, c) * Xchain[c] / den);
1342 for (std::size_t i = 0; i < M; ++i)
1343 for (std::size_t c = 0; c < K; ++c)
1344 if (Tchain(i, c) != zero) Rchain(i, c) = T(Qchain(i, c) / Tchain(i, c));
1345 for (std::size_t c = 0; c < K; ++c) {
1346 if (d.
Nchain[c] != 0.0)
continue;
1348 for (std::size_t i = 0; i < M; ++i) {
1349 Uchain(i, c) = zero;
1350 Rchain(i, c) = zero;
1351 Tchain(i, c) = zero;
1363 bool declares_dependence =
false;
1365 if (!st.lldscaling.empty() || st.cdscaling || st.jdscaling) declares_dependence =
true;
1367 L, d,
Matrix<T>(), declares_dependence ? Uchain :
Matrix<T>(), Rchain, Tchain, Xchain);
1387 for (std::size_t ist = 0; ist < M; ++ist) {
1390 "SolverMVA: station '" + L.
stations[ist].name +
1391 "' declares class-dependent service without a peak rate. Utilization at a "
1392 "class-dependent station is reported as T*S/peak, so pass the peak to "
1393 "setClassDependence");
1396 "SolverMVA: station '" + L.
stations[ist].name +
1397 "' declares joint-dependent service without a peak rate; pass the peak to "
1398 "setJointDependence");
1400 bool anyOpen =
false;
1401 for (
const auto& c : L.
classes)
1402 if (std::isinf(c.population)) anyOpen =
true;
1404 const std::size_t Kcls = L.
nclasses;
1405 for (std::size_t ist = 0; ist < M; ++ist) {
1406 const std::vector<T>* peak =
nullptr;
1408 peak = &L.
stations[ist].cdscalingpeak;
1409 else if (L.
stations[ist].jdscaling)
1410 peak = &L.
stations[ist].jdscalingpeak;
1411 if (peak ==
nullptr)
continue;
1412 for (std::size_t k = 0; k < Kcls; ++k) {
1414 const T bmax = k < peak->size() ? (*peak)[k] : zero;
1415 if (std::isfinite(rate) && rate > 0.0 && bmax > zero)
1416 out.
U(ist, k) = T(out.
Tp(ist, k) / L.
rates(ist, k) / bmax);
1418 out.
U(ist, k) = zero;
1429 std::snprintf(buf,
sizeof(buf),
"AMVA finished after %d sweeps", totiter);
1430 line::util::LineConsole::detail(buf);
1476 const Matrix<T>& init_sol,
bool& converged) {
1480 opt.iter_max = std::min(
opt.iter_max, 10000);
1484 if (method ==
"default" || method ==
"amva") {
1486 bool anysmall =
false;
1487 for (std::size_t c = 0; c < C; ++c) {
1489 if (d.
Nchain[c] < 1.0) anysmall =
true;
1491 if (Nsum <= 2.0 || anysmall) {
1494 bool anyfinite =
false, allone =
true;
1496 if (std::isfinite(s.nservers)) {
1498 if (s.nservers != 1.0) allone =
false;
1501 method = (anyfinite && allone) ?
"egflin" :
"lin";
1522 return solver_amvald(L, d, o2, method, converged, init_sol);
1527 const bool het_fcfs_own =
1528 (method ==
"ab" || method ==
"schmidt" || method ==
"schmidt-ext");
1533 if (!st.lldscaling.empty()) cond2 =
false;
1539 if (!(cond1 && cond2 && cond3))
return solver_amvald(L, d,
opt, method, converged, init_sol);
1553 std::vector<int> N(C, 0);
1554 for (std::size_t c = 0; c < C; ++c)
1555 N[c] = std::isfinite(d.
Nchain[c]) ?
static_cast<int>(std::llround(d.
Nchain[c]))
1559 std::vector<T> Nt(C, zero);
1560 for (std::size_t c = 0; c < C; ++c)
1564 std::vector<pfqn::SchedStrategy> types;
1565 for (std::size_t a = 0; a < nq; ++a) {
1579 for (std::size_t a = 0; a < nq && !bad; ++a)
1580 for (std::size_t c = 0; c < C; ++c) {
1582 if (Q0(a, c) < zero) {
1591 const bool direct_ms = (method ==
"ab" || method ==
"schmidt" || method ==
"schmidt-ext");
1592 const bool seidmann = (
opt.multiserver ==
"default" ||
opt.multiserver ==
"seidmann");
1599 for (std::size_t a = 0; a < nq; ++a) {
1600 const double c = pf.
S[a];
1601 if (!std::isfinite(c))
continue;
1602 for (std::size_t k = 0; k < C; ++k)
1605 for (std::size_t k = 0; k < C; ++k)
1609 }
else if (
opt.multiserver ==
"softmin") {
1617 std::vector<T> Zsum(C, zero);
1618 for (std::size_t c = 0; c < C; ++c)
1619 for (std::size_t a = 0; a < Zm.rows(); ++a) Zsum[c] += Zm(a, c);
1621 bool allone =
true, anyinf =
false;
1622 for (
double s : pf.
S) {
1623 if (!std::isfinite(s)) anyinf =
true;
1624 else if (s != 1.0) allone =
false;
1628 Matrix<T> Qq(nq, C, zero), Uq(nq, C, zero), Qz(nz, C, zero);
1629 std::vector<T> X(C, zero);
1631 bool have_delay_rows =
false;
1633 if (method ==
"sqni") {
1635 if (!(M == 2 && nq == 1 && nz == 1))
1637 "solver_amva: method 'sqni' applies only to a model of one queueing station and "
1638 "one infinite server");
1641 "solver_amva: method 'sqni' solves a quadratic and evaluates a square root, "
1642 "which exact arithmetic has no representation for; use the double or real "
1645 std::vector<T> Lv(C, zero);
1646 for (std::size_t c = 0; c < C; ++c) Lv[c] = Dm(0, c);
1648 for (std::size_t c = 0; c < C; ++c) {
1655 }
else if (method ==
"bs") {
1656 std::vector<pfqn::AmvaSched> bstype;
1657 for (std::size_t a = 0; a < nq; ++a)
1667 }
else if (method ==
"lcp" || method ==
"chow") {
1671 std::vector<pfqn::AmvaSched> lctype;
1672 for (std::size_t a = 0; a < nq; ++a)
1679 static_cast<std::size_t
>(
opt.iter_max), Q0)
1681 static_cast<std::size_t
>(
opt.iter_max), Q0);
1686 }
else if (method ==
"pamb" || method ==
"pami" || method ==
"pamt") {
1696 }
else if (method ==
"clust") {
1700 Dm, Nt, Zsum, std::vector<std::vector<std::size_t> >(),
1702 static_cast<std::size_t
>(
opt.iter_max));
1707 }
else if (method ==
"dmlin") {
1717 }
else if (method ==
"tay") {
1724 "solver_amva: Tay's approximation is defined for single-server stations; use "
1725 "'default' or 'lin'");
1728 "solver_amva: method 'tay' stops on a tolerance evaluated with transcendentals, "
1729 "which exact arithmetic has no representation for; use the double or real "
1733 Dm, Nt, Zsum,
opt.tol,
static_cast<std::size_t
>(
opt.iter_max), Q0);
1739 }
else if (method ==
"scat") {
1749 }
else if (method ==
"aql") {
1754 "solver_amva: AQL cannot handle multi-server stations; use 'default' or 'lin'");
1757 "solver_amva: method 'aql' stops on a relative tolerance evaluated with "
1758 "transcendentals, which exact arithmetic has no representation for; use the "
1759 "double or real backend");
1768 }
else if (method ==
"qsa") {
1773 "solver_amva: QSA cannot handle multi-server stations; use 'default' or 'lin'");
1776 "solver_amva: method 'qsa' stops on a residual tolerance evaluated with "
1777 "transcendentals, which exact arithmetic has no representation for; use the "
1778 "double or real backend");
1784 static_cast<std::size_t
>(
opt.iter_max));
1790 }
else if (direct_ms) {
1794 std::vector<int> nsfull;
1795 std::vector<pfqn::SchedStrategy> schedfull;
1797 for (std::size_t a = 0; a < nz; ++a) {
1798 for (std::size_t c = 0; c < C; ++c) Dfull(a, c) = pf.
Z(a, c);
1799 nsfull.push_back(1);
1803 for (std::size_t a = 0; a < nq; ++a) {
1804 for (std::size_t c = 0; c < C; ++c) {
1805 Dfull(nz + a, c) = pf.
D(a, c);
1810 const int c_i = std::isfinite(pf.
S[a]) ?
static_cast<int>(std::llround(pf.
S[a])) : 1;
1811 nsfull.push_back(c_i);
1812 Sfull(nz + a, 0) = c_i;
1813 schedfull.push_back(types[a]);
1817 "solver_amva: the Akyildiz-Bolch and Schmidt multiserver methods use marginal "
1818 "weights with non-integer powers and floors, which exact arithmetic has no "
1819 "representation for; use the double or real backend");
1820 }
else if (method ==
"ab") {
1823 for (std::size_t a = 0; a < nq; ++a)
1824 for (std::size_t c = 0; c < C; ++c) {
1825 Qq(a, c) = r.
QN(nz + a, c);
1826 Uq(a, c) = r.
UN(nz + a, c);
1828 for (std::size_t a = 0; a < nz; ++a)
1829 for (std::size_t c = 0; c < C; ++c) Qz(a, c) = r.
QN(a, c);
1831 iters =
static_cast<int>(r.
totiter);
1832 }
else if (method ==
"schmidt") {
1834 for (std::size_t a = 0; a < nq; ++a)
1835 for (std::size_t c = 0; c < C; ++c) Qq(a, c) = r.
QN(nz + a, c);
1836 for (std::size_t a = 0; a < nz; ++a)
1837 for (std::size_t c = 0; c < C; ++c) Qz(a, c) = r.
QN(a, c);
1846 std::vector<double> sx_n(C, 0.0);
1847 for (std::size_t c = 0; c < C; ++c) sx_n[c] = d.
Nchain[c];
1848 std::vector<bool> sx_fcfs;
1849 for (std::size_t a = 0; a < nq; ++a)
1851 SchedStrategy::FCFS);
1852 const std::string sx_reason =
1857 for (std::size_t a = 0; a < nq; ++a)
1858 for (std::size_t c = 0; c < C; ++c) Qq(a, c) = r.
QN(nz + a, c);
1859 for (std::size_t a = 0; a < nz; ++a)
1860 for (std::size_t c = 0; c < C; ++c) Qz(a, c) = r.
QN(a, c);
1864 have_delay_rows =
true;
1867 for (std::size_t a = 0; a < nq; ++a) {
1868 const double c_i = std::isfinite(pf.
S[a]) ? pf.
S[a] : 1.0;
1869 for (std::size_t k = 0; k < C; ++k)
1872 }
else if (method ==
"lin" || method ==
"gflin" || method ==
"egflin") {
1876 if (st.cdscaling || st.jdscaling)
1884 return solver_amvald(L, d, o2, method, converged, init_sol);
1886 if (allone ||
opt.multiserver ==
"krzesinski") {
1887 std::vector<int> ns;
1888 for (std::size_t a = 0; a < nq; ++a)
1889 ns.push_back(std::isfinite(pf.
S[a]) ?
static_cast<int>(std::llround(pf.
S[a])) : 1);
1890 if (allone) ns.assign(nq, 1);
1892 pf.
lambda, Dm, N, Zm, ns, types,
opt.tol,
opt.iter_max, mx, Q0);
1897 }
else if (
opt.multiserver ==
"conway") {
1900 "solver_amva: the Conway multiserver correction evaluates transcendentals, "
1901 "which exact arithmetic has no representation for; use the double or real "
1904 std::vector<int> ns;
1905 for (std::size_t a = 0; a < nq; ++a)
1906 ns.push_back(std::isfinite(pf.
S[a]) ?
static_cast<int>(std::llround(pf.
S[a]))
1915 }
else if (
opt.multiserver ==
"default" ||
opt.multiserver ==
"softmin" ||
1916 opt.multiserver ==
"seidmann" ||
opt.multiserver ==
"suri" ||
1917 opt.multiserver ==
"erlang") {
1928 bool conserves =
true;
1929 for (std::size_t c = 0; c < C && conserves; ++c) {
1930 const double Nc = d.
Nchain[c];
1931 if (!std::isfinite(Nc) || Nc <= 0.0)
continue;
1933 for (std::size_t k : L.
inchain[c])
1934 for (std::size_t i = 0; i < sol.
Q.rows(); ++i) {
1936 if (!std::isnan(v)) qc += v;
1938 if (!std::isfinite(qc) || std::abs(qc - Nc) > 1e-3 * Nc) conserves =
false;
1940 if (converged || conserves)
return sol;
1949 throw UnsupportedError(
"solver_amva: unrecognized multiserver approximation '" +
1951 "'. Supported: default, softmin, seidmann, suri, conway, "
1952 "erlang, krzesinski.");
1960 return solver_amvald(L, d, o2, method, converged, init_sol);
1963 Matrix<T> Q(M, C, zero), U(M, C, zero), Tp(M, C, zero), R(M, C, zero);
1964 for (std::size_t a = 0; a < nq; ++a)
1965 for (std::size_t c = 0; c < C; ++c) {
1973 for (std::size_t a = 0; a < nz; ++a)
1974 for (std::size_t c = 0; c < C; ++c) {
1978 (void)have_delay_rows;
1981 if (seidmann && !direct_ms) {
1982 for (std::size_t a = 0; a < nq; ++a) {
1983 const double c = pf.
S[a];
1984 if (!std::isfinite(c) || c <= 1.0)
continue;
1985 for (std::size_t k = 0; k < C; ++k)
1991 for (std::size_t i = 0; i < M; ++i)
1992 for (std::size_t c = 0; c < C; ++c) {
1993 Tp(i, c) = T(d.
Vchain(i, c) * X[c]);
1994 if (Tp(i, c) != zero) R(i, c) = T(Q(i, c) / Tp(i, c));
1999 std::vector<T> Cyc(C, zero);
2000 for (std::size_t c = 0; c < C; ++c) {
2001 if (!(X[c] > zero))
continue;
2003 for (std::size_t a = 0; a < nz; ++a) z += pf.
Z(a, c);