161 std::vector<std::size_t> lcfs, lcfspr;
162 for (std::size_t i = 0; i < M; ++i) {
163 if (L.
stations[i].sched == SchedStrategy::LCFS) lcfs.push_back(i + 1);
164 if (L.
stations[i].sched == SchedStrategy::LCFSPR) lcfspr.push_back(i + 1);
166 if (!lcfs.empty() && !lcfspr.empty()) {
167 if (lcfs.size() != 1 || lcfspr.size() != 1)
169 "solver_mva: LCFS MVA requires exactly one LCFS and one LCFS-PR station");
170 for (std::size_t c = 0; c < C; ++c)
171 if (std::isinf(d.
Nchain[c]))
172 throw UnsupportedError(
"solver_mva: LCFS MVA requires a closed queueing network");
176 for (std::size_t ist : {lcfs[0], lcfspr[0]}) {
178 for (std::size_t r = 0; r < Rn; ++r)
179 if (L.
rt.rows() == S * Rn && L.
rt(sf * Rn + r, sf * Rn + r) > zero)
181 "solver_mva: LCFS MVA does not support self-loops at stations");
186 throw UnsupportedError(
"solver_mva: LCFS scheduling requires a paired LCFS-PR station");
193 throw UnsupportedError(
"solver_mva: the layer does not have a product form");
195 std::vector<std::size_t> infSET, qSET;
196 for (std::size_t i = 0; i < M; ++i) {
198 case SchedStrategy::EXT:
break;
199 case SchedStrategy::INF: infSET.push_back(i);
break;
200 case SchedStrategy::PS:
201 case SchedStrategy::LCFSPR:
202 case SchedStrategy::FCFS:
203 case SchedStrategy::SIRO: qSET.push_back(i);
break;
205 throw UnsupportedError(std::string(
"solver_mva: unsupported exact MVA analysis for ") +
211 Matrix<T> Lq(qSET.size(), C, zero), Zd(infSET.size(), C, zero);
212 for (std::size_t a = 0; a < qSET.size(); ++a)
213 for (std::size_t c = 0; c < C; ++c)
215 for (std::size_t a = 0; a < infSET.size(); ++a)
216 for (std::size_t c = 0; c < C; ++c)
217 Zd(a, c) = T(d.
STchain(infSET[a], c) * d.
Vchain(infSET[a], c));
220 std::vector<T> lambda(C, zero);
221 std::vector<int> N(C, 0);
222 for (std::size_t c = 0; c < C; ++c) {
223 if (!std::isfinite(d.
Nchain[c])) {
229 N[c] =
static_cast<int>(std::llround(d.
Nchain[c]));
232 for (std::size_t a = 0; a < qSET.size(); ++a) {
233 const double s = L.
stations[qSET[a]].nservers;
234 if (!std::isfinite(s))
235 throw UnsupportedError(
"solver_mva: a queueing station has infinitely many servers but "
236 "is not inf-scheduled");
237 S.push_back(
static_cast<int>(std::llround(s)));
250 Matrix<T> Qchain(M, C, zero), Wchain(M, C, zero), Tchain(M, C, zero), Uchain(M, C, zero);
251 std::vector<T> Xchain = pf.
XN;
252 for (std::size_t a = 0; a < qSET.size(); ++a)
253 for (std::size_t c = 0; c < C; ++c) Qchain(qSET[a], c) = pf.
QN(a, c);
254 for (std::size_t a = 0; a < infSET.size(); ++a)
255 for (std::size_t c = 0; c < C; ++c)
256 Qchain(infSET[a], c) = T(Xchain[c] * d.
STchain(infSET[a], c) * d.
Vchain(infSET[a], c));
258 std::vector<std::size_t> rset;
259 for (std::size_t c = 0; c < C; ++c)
260 if (d.
Nchain[c] != 0.0) rset.push_back(c);
262 for (std::size_t c : rset) {
263 for (std::size_t i : infSET) Wchain(i, c) = d.
STchain(i, c);
264 for (std::size_t i : qSET) {
265 if (std::isinf(L.
stations[i].nservers)) {
266 Wchain(i, c) = d.
STchain(i, c);
267 }
else if (d.
Vchain(i, c) == zero || Xchain[c] == zero) {
270 Wchain(i, c) = T(Qchain(i, c) / (Xchain[c] * d.
Vchain(i, c)));
275 std::vector<T> Cc(C, zero);
276 for (std::size_t c : rset) {
278 for (std::size_t i = 0; i < M; ++i) sw += Wchain(i, c);
283 for (std::size_t i = 0; i < M; ++i) cyc += d.
Vchain(i, c) * Wchain(i, c);
286 if (cyc != zero && std::isfinite(d.
Nchain[c]))
289 for (std::size_t i = 0; i < M; ++i) {
290 Qchain(i, c) = T(Xchain[c] * d.
Vchain(i, c) * Wchain(i, c));
291 Tchain(i, c) = T(Xchain[c] * d.
Vchain(i, c));
294 for (std::size_t i = 0; i < M; ++i)
295 for (std::size_t c : rset) {
297 Uchain(i, c) = std::isinf(L.
stations[i].nservers)
303 for (std::size_t i = 0; i < M; ++i) {
304 if (!(L.
stations[i].sched == SchedStrategy::FCFS || L.
stations[i].sched == SchedStrategy::PS))
307 for (std::size_t c = 0; c < C; ++c) usum += Uchain(i, c);
310 for (std::size_t c = 0; c < C; ++c) den += d.
Vchain(i, c) * d.
STchain(i, c) * Xchain[c];
311 if (den == zero)
continue;
313 for (std::size_t c = 0; c < C; ++c) {
315 Uchain(i, c) = T(cap * d.
Vchain(i, c) * d.
STchain(i, c) * Xchain[c] / den);
320 for (std::size_t i = 0; i < M; ++i)
321 for (std::size_t c = 0; c < C; ++c)
322 if (Tchain(i, c) != zero) Rchain(i, c) = T(Qchain(i, c) / Tchain(i, c));
323 for (std::size_t c = 0; c < C; ++c) {
324 if (d.
Nchain[c] != 0.0)
continue;
326 for (std::size_t i = 0; i < M; ++i) {
459 const MvaOptions&
opt,
const std::string& method,
bool& converged,
474 "solver_amvald: the approximate MVA is an iterative fixed point whose iterates "
475 "accumulate the product of every denominator seen so far, so exact rational "
476 "arithmetic grows without bound and the solve does not terminate; rerun with "
477 "--arith double or --arith real, or give the model a product form (BCMP type 1 "
478 "asks FCFS service to be exponential) so the exact recursion applies");
484 for (std::size_t i = 0; i < M; ++i) {
486 if (!(s == SchedStrategy::INF || s == SchedStrategy::PS || s == SchedStrategy::FCFS ||
487 s == SchedStrategy::SIRO || s == SchedStrategy::LCFSPR || s == SchedStrategy::EXT ||
488 s == SchedStrategy::DPS || s == SchedStrategy::HOL ||
489 s == SchedStrategy::FCFSPRPRIO))
491 " scheduling is not implemented in this port");
502 bool any_queue =
false;
503 for (std::size_t i = 0; i < M && !any_queue; ++i)
504 if (L.
stations[i].sched != SchedStrategy::INF && L.
stations[i].sched != SchedStrategy::EXT)
506 const bool linmethod = (method ==
"lin" || method ==
"qdlin");
508 !(linmethod || method ==
"qd" || method ==
"default" || method ==
"bs" ||
509 method ==
"egflin" || method ==
"gflin" || method ==
"qli" || method ==
"fli" ||
510 method ==
"aql" || method ==
"qdaql" || method ==
"tay" || method ==
"priomva"))
511 throw UnsupportedError(
"solver_amvald: method '" + method +
"' is not implemented");
515 for (std::size_t c = 0; c < K; ++c)
519 std::vector<T> deltaclass(K, one);
520 for (std::size_t c = 0; c < K; ++c)
525 std::vector<std::size_t> nnz, ccl, ocl;
526 std::vector<bool> isopen(K,
false);
527 for (std::size_t c = 0; c < K; ++c) {
528 if (!(d.
Nchain[c] > 0.0))
continue;
530 if (std::isfinite(d.
Nchain[c])) ccl.push_back(c);
531 else { ocl.push_back(c); isopen[c] =
true; }
536 if (init_sol.rows() == M && init_sol.cols() == K) {
540 for (std::size_t c = 0; c < K; ++c) {
541 if (!std::isfinite(d.
Nchain[c]))
continue;
542 for (std::size_t i = 0; i < M; ++i)
548 std::vector<T> Xchain(K, zero);
549 for (std::size_t c = 0; c < K; ++c) {
553 Xchain[c] = st > zero ? T(one / st) : zero;
557 for (std::size_t i = 0; i < M; ++i) s += d.
STchain(i, c);
558 if (s != zero) Xchain[c] = T(one / s);
560 Matrix<T> Uchain(M, K, zero), Tchain(M, K, zero), Wchain(M, K, zero), STeff = d.
STchain;
561 for (std::size_t i = 0; i < M; ++i)
562 for (std::size_t c : nnz) {
564 Uchain(i, c) = std::isinf(L.
stations[i].nservers)
570 std::vector<Matrix<T>> gamma(K,
Matrix<T>(M, K, zero));
574 const int max_totiter = std::min(
opt.iter_max, 10000);
576 const int inner_cap =
static_cast<int>(std::sqrt(
static_cast<double>(
opt.iter_max)));
579 const bool has_prio = [&] {
582 for (std::size_t c = 0; c < K; ++c) {
584 if (first) { lo = hi = p; first =
false; }
585 lo = std::min(lo, p);
586 hi = std::max(hi, p);
590 std::vector<std::vector<std::size_t>> ehprio(K), hprio(K), eprio(K), lprio(K);
591 for (std::size_t r : nnz) {
594 for (std::size_t s : nnz) {
595 ehprio[r].push_back(s);
596 eprio[r].push_back(s);
600 for (std::size_t s : nnz) {
602 if (ps <= pr) ehprio[r].push_back(s);
603 if (ps < pr) hprio[r].push_back(s);
604 if (ps == pr) eprio[r].push_back(s);
605 if (ps > pr) lprio[r].push_back(s);
610 std::size_t smax = 0;
611 for (
const auto& st : L.
stations) smax = std::max(smax, st.lldscaling.size());
615 for (std::size_t i = 0; i < M; ++i)
616 for (std::size_t k = 0; k < L.
stations[i].lldscaling.size(); ++k)
617 lldscaling(i, k) = L.
stations[i].lldscaling[k];
619 std::vector<lang::CdScaling<T>> cdscaling;
625 if (!cdscaling.empty())
626 for (std::size_t i = 0; i < M; ++i) cdscaling[i] = L.
stations[i].cdscaling;
631 std::vector<lang::CdScaling<T>> jdscaling;
637 if (!jdscaling.empty())
638 for (std::size_t i = 0; i < M; ++i) jdscaling[i] = L.
stations[i].jdscaling;
639 const bool has_scaling = (smax > 0) || !cdscaling.empty() || !jdscaling.empty();
642 std::vector<std::vector<T>> tau(K, std::vector<T>(K, zero));
654 auto forward = [&](
const Matrix<T>& Qin,
const std::vector<T>& Xin,
const Matrix<T>& Uin,
657 for (std::size_t c = 0; c < K; ++c)
658 if (std::isfinite(Nc[c])) Ntl += Nc[c];
660 std::vector<T> dcl(K, one);
661 for (std::size_t c = 0; c < K; ++c)
662 if (std::isfinite(Nc[c]) && Nc[c] > 0.0)
665 std::vector<T> interpTot(M, zero);
666 Matrix<T> selfArvl(M, K, zero), totArvl(M, K, zero), totArvlOpen(M, K, zero);
667 for (std::size_t i = 0; i < M; ++i) {
669 for (std::size_t c : nnz) s += Qin(i, c);
670 interpTot[i] = T(deltaL * s);
671 const bool hol = L.
stations[i].sched == SchedStrategy::HOL;
672 for (std::size_t c : nnz) {
673 selfArvl(i, c) = T(dcl[c] * Qin(i, c));
675 T eh = zero, ehx = zero;
676 for (std::size_t s2 : ehprio[c]) {
678 if (s2 != c) ehx += Qin(i, s2);
680 totArvlOpen(i, c) = eh;
681 totArvl(i, c) = T(dcl[c] * Qin(i, c) + ehx);
683 totArvlOpen(i, c) = s;
684 totArvl(i, c) = T(dcl[c] * Qin(i, c) + s - Qin(i, c));
690 std::vector<T> gmean(M, zero);
695 for (std::size_t i = 0; i < M; ++i) {
697 for (std::size_t s : ccl) {
699 for (std::size_t r : ccl)
701 acc += T(deltaL * gs);
703 gmean[i] = T(acc / nccl);
708 for (std::size_t i = 0; i < M; ++i)
709 for (std::size_t r : ccl)
712 for (std::size_t i = 0; i < M; ++i) gmean[i] = sc;
715 std::vector<T> narrival(M, zero);
716 for (std::size_t i = 0; i < M; ++i) narrival[i] = T(one + interpTot[i] + gmean[i]);
717 const std::vector<T> msterm = detail::ms_term(L, narrival,
opt.multiserver);
718 std::vector<T> suri(M, one);
719 if (
opt.multiserver ==
"suri") suri = detail::suri_factor(L, Uin,
opt.tol);
727 "solver_amvald: load-dependent scaling interpolates a rate lattice with a "
728 "soft minimum, which exact arithmetic has no representation for; use the "
729 "double or real backend");
731 if (linmethod && !ccl.empty() && !nnz.empty()) {
732 for (std::size_t r : nnz) {
733 std::vector<T> arg(M, zero);
734 for (std::size_t i = 0; i < M; ++i) {
736 for (std::size_t s : ccl)
738 gcorr -= gamma[r](i, r);
739 arg[i] = T(one + interpTot[i] + gcorr);
741 const std::vector<T> v =
743 for (std::size_t i = 0; i < M; ++i) lldterm(i, r) = v[i];
746 std::vector<T> arg(M, zero);
747 for (std::size_t i = 0; i < M; ++i) arg[i] = T(one + interpTot[i]);
748 const std::vector<T> v =
750 for (std::size_t i = 0; i < M; ++i)
751 for (std::size_t c = 0; c < K; ++c) lldterm(i, c) = v[i];
758 if (!cdscaling.empty()) {
759 for (std::size_t r : nnz) {
761 const bool closed = std::isfinite(Nc[r]);
762 for (std::size_t i = 0; i < M; ++i)
763 for (std::size_t c = 0; c < K; ++c)
764 arg(i, c) = T(one + (closed ? selfArvl(i, c) : Qin(i, c)));
765 if (closed && linmethod)
766 for (std::size_t i = 0; i < M; ++i) {
769 for (std::size_t c = 0; c < K; ++c) arg(i, c) = T(arg(i, c) + gself);
772 for (std::size_t i = 0; i < M; ++i) cdterm(i, r) = v[i];
788 if (!jdscaling.empty()) {
789 for (std::size_t r : nnz) {
790 const bool closed = std::isfinite(Nc[r]);
792 for (std::size_t i = 0; i < M; ++i) {
793 for (std::size_t c : nnz) arg(i, c) = Qin(i, c);
794 T self = T(one + (closed ? selfArvl(i, r) : Qin(i, r)));
795 if (closed && linmethod)
800 for (std::size_t i = 0; i < M; ++i) jdterm(i, r) = v[i];
805 for (std::size_t c : nnz)
806 for (std::size_t i = 0; i < M; ++i)
808 T(d.
STchain(i, c) * lldterm(i, c) * msterm[i] * cdterm(i, c) * jdterm(i, c));
812 if (method ==
"qli" || method ==
"fli") {
813 const bool qli = (method ==
"qli");
814 for (std::size_t i = 0; i < M; ++i) {
815 const bool hol = L.
stations[i].sched == SchedStrategy::HOL;
816 for (std::size_t r : nnz) {
819 for (std::size_t s2 : ehprio[r]) tot += Qin(i, s2);
821 for (std::size_t s2 : nnz) tot += Qin(i, s2);
824 totArvl(i, r) = T(tot - Qin(i, r));
827 const T num = T(STeffOut(i, r) * (one + tot - Qin(i, r)));
829 for (std::size_t m = 0; m < M; ++m)
830 if (L.
stations[m].sched == SchedStrategy::INF) den += STeffOut(m, r);
831 for (std::size_t m = 0; m < M; ++m) {
833 if (L.
stations[m].sched == SchedStrategy::HOL) {
834 for (std::size_t s2 : ehprio[r]) totm += Qin(m, s2);
836 for (std::size_t s2 : nnz) totm += Qin(m, s2);
838 den += T(STeffOut(m, r) * (one + totm - Qin(m, r)));
840 if (den == zero)
continue;
843 totArvl(i, r) = T(tot - f * (Qin(i, r) - num / den));
846 totArvl(i, r) = T(tot - f * Qin(i, r) + num / den);
857 if (!ILchain.
empty()) {
858 for (std::size_t i = 0; i < M; ++i)
859 for (std::size_t c : nnz) {
861 for (std::size_t c2 : nnz)
862 if (c2 != c) ilq += ILchain(c, c2) * Qin(i, c2);
864 T adj = T(totArvl(i, c) - ilq);
865 if (adj < selfArvl(i, c)) adj = selfArvl(i, c);
872 for (std::size_t c : nnz) {
873 for (std::size_t i = 0; i < M; ++i) {
876 if (sc == SchedStrategy::EXT)
continue;
877 if (sc == SchedStrategy::INF) {
878 Wout(i, c) = STeffOut(i, c);
881 const double cs = L.
stations[i].nservers;
883 if (sc == SchedStrategy::PS) {
886 for (std::size_t s : ccl)
888 corr -= gamma[c](i, c);
892 if (
opt.multiserver ==
"suri") {
895 const T Lm = isopen[c] ? totArvlOpen(i, c)
896 : T(totArvl(i, c) + corr);
897 Wout(i, c) = T(STeffOut(i, c) * (one + Lm * suri[i]));
900 if (
opt.multiserver ==
"seidmann")
904 Wout(i, c) = T(Wout(i, c) + STeffOut(i, c) * (one + totArvlOpen(i, c)));
912 Wout(i, c) = T(Wout(i, c) + STeffOut(i, c) * (one + totArvl(i, c) + corr));
917 if (sc == SchedStrategy::DPS) {
920 const std::vector<T>& w = L.
stations[i].schedparam;
923 "solver_amvald: the DPS station '" + L.
stations[i].name +
924 "' has no per-class weights");
926 if (cs > 1.0 && std::isfinite(cs))
928 acc += T(STeffOut(i, c) * (one + selfArvl(i, c)));
929 for (std::size_t s : nnz) {
930 if (s == c)
continue;
932 acc += T(STeffOut(i, c) * Qin(i, s));
934 acc += T(STeffOut(i, c) * Qin(i, s) * w[s] / w[c]);
940 const bool fcfs_family = (sc == SchedStrategy::FCFS || sc == SchedStrategy::SIRO ||
941 sc == SchedStrategy::LCFSPR);
942 if (!fcfs_family && sc != SchedStrategy::HOL && sc != SchedStrategy::FCFSPRPRIO)
944 " scheduling is not implemented in this port");
945 if (STeffOut(i, c) <= zero)
continue;
947 const std::vector<T>& Xarv = Xref.empty() ? Xin : Xref;
960 if (sc == SchedStrategy::FCFSPRPRIO) {
961 if (cs > 1.0 && std::isfinite(cs))
963 "solver_amvald: FCFSPRPRIO with more than one server. The "
964 "preemptive-resume priority arm (priomva) is implemented for "
965 "single-server stations only; use SolverCTMC or SolverSSA for "
970 for (std::size_t h : hprio[c])
971 uh_prs += T(d.
Vchain(i, h) * STeffOut(i, h) * T(Xarv[h] + tau[c][h]));
973 vps = std::max(
opt.tol, std::min(vps, 1.0 -
opt.tol));
977 T queued = T(STeffOut(i, c) * (isopen[c] ? Qin(i, c) : selfArvl(i, c)));
978 for (std::size_t s : ehprio[c])
979 if (s != c) queued += T(STeffOut(i, s) * Qin(i, s));
981 Wout(i, c) = T((STeffOut(i, c) + queued) / ps_prs);
985 auto Ur = [&](std::size_t k, std::size_t s) -> T {
986 if (!(Xin[s] > zero))
return Uin(k, s);
987 return T(Uin(k, s) / Xin[s] * (Xarv[s] + tau[c][s]));
991 auto prio_scaling = [&](std::size_t r) -> T {
992 if (sc != SchedStrategy::HOL)
return one;
994 for (std::size_t h : hprio[r]) {
997 const T xh =
opt.np_priority ==
"shadow" ? Xarv[h] : T(Xarv[h] + tau[r][h]);
998 uh += T(d.
Vchain(i, h) * STeffOut(i, h) * xh);
1001 v = std::max(
opt.tol, std::min(v, 1.0 -
opt.tol));
1004 const T ps_r = prio_scaling(c);
1011 auto hol_ehprio_backlog = [&](
const std::vector<T>& Bkw) -> T {
1013 for (std::size_t s : ehprio[c])
1014 if (s != c) acc += T(STeffOut(i, s) * Qin(i, s) * Bkw[s]);
1017 auto hol_np_residual = [&](
const std::vector<T>& Bkw) -> T {
1018 if (
opt.highvar ==
"hvmva")
return zero;
1020 for (std::size_t s : lprio[c])
1021 acc += T(d.
Vchain(i, s) * STeffOut(i, s) * (Xarv[s] + tau[c][s]) *
1022 STeffOut(i, s) * Bkw[s]);
1027 std::vector<T> Bk(K, one);
1028 if (cs > 1.0 && std::isfinite(cs)) {
1030 for (std::size_t s : nnz) {
1031 const T dr = (sc == SchedStrategy::HOL) ? dcl[s] : ((s == c) ? dcl[c] : one);
1032 load += dr * Xin[s] * d.
Vchain(i, s) * STeffOut(i, s);
1035 for (std::size_t s : nnz) {
1036 const T dr = (sc == SchedStrategy::HOL) ? dcl[s] : ((s == c) ? dcl[c] : one);
1037 T base = T(dr * Xin[s] * d.
Vchain(i, s) * STeffOut(i, s));
1038 if (sc == SchedStrategy::HOL &&
opt.multiserver !=
"softmin")
1043 const unsigned e =
static_cast<unsigned>(
1044 std::llround(cs) - (sc == SchedStrategy::HOL ? 0 : 1));
1053 if (
opt.highvar ==
"hvmva") {
1055 for (std::size_t s : ccl) usum += Ur(i, s);
1056 w = T(STeffOut(i, c) * (one - usum));
1057 for (std::size_t s : ccl)
1058 w += T(STeffOut(i, s) / prio_scaling(s) * Ur(i, s) *
1060 }
else if (
opt.highvar !=
"default") {
1062 "' is not implemented");
1066 if (sc == SchedStrategy::HOL) {
1068 const std::vector<T>
ones(K, one);
1069 w += T((STeffOut(i, c) * (isopen[c] ? Qin(i, c) : selfArvl(i, c)) +
1070 hol_ehprio_backlog(
ones) + hol_np_residual(
ones)) / ps_r);
1072 w += STeffOut(i, c) * (isopen[c] ? Qin(i, c) : selfArvl(i, c));
1073 for (std::size_t s : nnz)
1074 if (s != c) w += STeffOut(i, s) * Qin(i, s);
1081 if (
opt.multiserver ==
"suri") {
1082 T Lm = isopen[c] ? T(deltaclass[c] * Qin(i, c)) : selfArvl(i, c);
1083 if (sc != SchedStrategy::HOL)
1084 for (std::size_t s : nnz)
1085 if (s != c) Lm += Qin(i, s);
1086 if (sc == SchedStrategy::HOL) {
1087 Lm = isopen[c] ? Qin(i, c) : selfArvl(i, c);
1088 const std::vector<T>
ones(K, one);
1089 Wout(i, c) = T(STeffOut(i, c) +
1090 ((STeffOut(i, c) * Lm + hol_ehprio_backlog(
ones)) * suri[i] +
1091 hol_np_residual(
ones)) / ps_r);
1094 Wout(i, c) = T(STeffOut(i, c) / ps_r + STeffOut(i, c) * Lm * suri[i] / ps_r);
1097 if (
opt.multiserver ==
"softmin") {
1098 T w = STeffOut(i, c);
1099 if (sc == SchedStrategy::HOL)
1100 w += T((STeffOut(i, c) * (isopen[c] ? Qin(i, c) : selfArvl(i, c)) * Bk[c] +
1101 hol_ehprio_backlog(Bk) + hol_np_residual(Bk)) / ps_r);
1103 w += T(STeffOut(i, c) * (isopen[c] ? Qin(i, c) : selfArvl(i, c)) * Bk[c] / ps_r);
1104 if (sc != SchedStrategy::HOL)
1105 for (std::size_t s : nnz)
1106 if (s != c) w += STeffOut(i, s) * Bk[s] * Qin(i, s);
1112 w += STeffOut(i, c);
1113 if (sc == SchedStrategy::HOL) {
1114 w += T((STeffOut(i, c) * (isopen[c] ? Qin(i, c) : selfArvl(i, c)) * Bk[c] +
1115 hol_ehprio_backlog(Bk) + hol_np_residual(Bk)) / ps_r);
1117 w += STeffOut(i, c) *
1118 (isopen[c] ? T(deltaclass[c] * Qin(i, c)) : selfArvl(i, c)) * Bk[c];
1119 for (std::size_t s : nnz)
1120 if (s != c) w += STeffOut(i, s) * Bk[s] * Qin(i, s);
1134 const std::vector<T> Xprev = X;
1137 forward(Qprev, Xprev, Uprev, Nc, Wout, STeffOut);
1141 for (std::size_t i = 0; i < Q.
rows(); ++i)
1142 for (std::size_t c = 0; c < Q.
cols(); ++c)
1146 "AMVA sweep %d: queue-length residual %.3e",
1149 if (totiter >= max_totiter)
break;
1150 for (std::size_t c : nnz) {
1152 for (std::size_t i = 0; i < M; ++i) sw += Wout(i, c);
1155 }
else if (!isopen[c]) {
1157 for (std::size_t i = 0; i < M; ++i) cyc += d.
Vchain(i, c) * Wout(i, c);
1160 (one - omicron) * Xprev[c]);
1164 for (std::size_t i = 0; i < M; ++i) {
1165 Q(i, c) = T(omicron * X[c] * d.
Vchain(i, c) * Wout(i, c) +
1166 (one - omicron) * Qprev(i, c));
1167 U(i, c) = T(omicron * d.
Vchain(i, c) * STeffOut(i, c) * X[c] +
1168 (one - omicron) * Uprev(i, c));
1173 for (std::size_t i = 0; i < M; ++i)
1174 for (std::size_t c = 0; c < K; ++c)
1176 if (err <=
opt.iter_tol)
break;
1178 if (iter > inner_cap)
break;
1188 QouterPrev = Qchain;
1189 const std::vector<T> XouterPrev = Xchain;
1191 if (linmethod) Xref = XouterPrev;
1194 if (linmethod && std::isfinite(Nt) && Nt > 0.0) {
1195 for (std::size_t s = 0; s < K; ++s) {
1196 if (!std::isfinite(d.
Nchain[s]))
continue;
1197 std::vector<double> Ns = d.
Nchain;
1201 std::vector<T> Xs = Xchain;
1203 for (std::size_t i = 0; i < M; ++i)
1204 for (std::size_t c = 0; c < K; ++c) {
1205 Qs(i, c) = T(Qs(i, c) * shrink);
1206 Us(i, c) = T(Us(i, c) * shrink);
1208 for (std::size_t c = 0; c < K; ++c) Xs[c] = T(Xs[c] * shrink);
1210 const std::vector<T> Xs_in = Xs;
1211 const Matrix<T> Qs_prev = inner_loop(Qs, Xs, Us, Ns, Ws, STs);
1213 for (std::size_t c : nnz) tau[s][c] = T(Xs[c] - XouterPrev[c]);
1232 if (method ==
"qdlin") {
1233 for (std::size_t i = 0; i < M; ++i) {
1234 T qs = zero, qo = zero;
1235 for (std::size_t c = 0; c < K; ++c) {
1236 qs += Qs_prev(i, c);
1237 qo += QouterPrev(i, c);
1248 for (std::size_t i = 0; i < M; ++i)
1249 for (std::size_t c : nnz)
1250 if (std::isfinite(d.
Nchain[c]) && Ns[c] > 0.0)
1255 if (totiter >= max_totiter)
break;
1258 if (totiter >= max_totiter)
break;
1261 const Matrix<T> Qprev = inner_loop(Qchain, Xchain, Uchain, d.
Nchain, Wtmp, STtmp);
1266 for (std::size_t i = 0; i < M; ++i)
1267 for (std::size_t c = 0; c < K; ++c)
1269 converged = err <=
opt.iter_tol;
1270 if (outer >= 2 && converged)
break;
1271 if (outer >= inner_cap || totiter > max_totiter)
break;
1274 for (std::size_t i = 0; i < M; ++i)
1275 for (std::size_t c = 0; c < K; ++c) Tchain(i, c) = T(Xchain[c] * d.
Vchain(i, c));
1278 for (std::size_t i = 0; i < M; ++i) {
1280 if (!(sc == SchedStrategy::FCFS || sc == SchedStrategy::SIRO || sc == SchedStrategy::PS ||
1281 sc == SchedStrategy::LCFSPR || sc == SchedStrategy::DPS || sc == SchedStrategy::HOL))
1284 for (std::size_t c = 0; c < K; ++c) usum += Uchain(i, c);
1287 for (std::size_t c = 0; c < K; ++c) den += d.
Vchain(i, c) * STeff(i, c) * Xchain[c];
1288 if (den == zero)
continue;
1289 for (std::size_t c = 0; c < K; ++c) {
1290 if (!(d.
Vchain(i, c) * STeff(i, c) > zero))
continue;
1291 Uchain(i, c) = T(one * d.
Vchain(i, c) * STeff(i, c) * Xchain[c] / den);
1296 for (std::size_t i = 0; i < M; ++i)
1297 for (std::size_t c = 0; c < K; ++c)
1298 if (Tchain(i, c) != zero) Rchain(i, c) = T(Qchain(i, c) / Tchain(i, c));
1299 for (std::size_t c = 0; c < K; ++c) {
1300 if (d.
Nchain[c] != 0.0)
continue;
1302 for (std::size_t i = 0; i < M; ++i) {
1303 Uchain(i, c) = zero;
1304 Rchain(i, c) = zero;
1305 Tchain(i, c) = zero;
1330 for (std::size_t ist = 0; ist < M; ++ist) {
1333 "SolverMVA: station '" + L.
stations[ist].name +
1334 "' declares class-dependent service without a peak rate. Utilization at a "
1335 "class-dependent station is reported as T*S/peak, so pass the peak to "
1336 "setClassDependence");
1339 "SolverMVA: station '" + L.
stations[ist].name +
1340 "' declares joint-dependent service without a peak rate; pass the peak to "
1341 "setJointDependence");
1343 bool anyOpen =
false;
1344 for (
const auto& c : L.
classes)
1345 if (std::isinf(c.population)) anyOpen =
true;
1347 const std::size_t Kcls = L.
nclasses;
1348 for (std::size_t ist = 0; ist < M; ++ist) {
1349 const std::vector<T>* peak =
nullptr;
1351 peak = &L.
stations[ist].cdscalingpeak;
1352 else if (L.
stations[ist].jdscaling)
1353 peak = &L.
stations[ist].jdscalingpeak;
1354 if (peak ==
nullptr)
continue;
1355 for (std::size_t k = 0; k < Kcls; ++k) {
1357 const T bmax = k < peak->size() ? (*peak)[k] : zero;
1358 if (std::isfinite(rate) && rate > 0.0 && bmax > zero)
1359 out.
U(ist, k) = T(out.
Tp(ist, k) / L.
rates(ist, k) / bmax);
1361 out.
U(ist, k) = zero;
1372 std::snprintf(buf,
sizeof(buf),
"AMVA finished after %d sweeps", totiter);
1373 line::util::LineConsole::detail(buf);
1419 const Matrix<T>& init_sol,
bool& converged) {
1423 opt.iter_max = std::min(
opt.iter_max, 10000);
1427 if (method ==
"default" || method ==
"amva") {
1429 bool anysmall =
false;
1430 for (std::size_t c = 0; c < C; ++c) {
1432 if (d.
Nchain[c] < 1.0) anysmall =
true;
1434 if (Nsum <= 2.0 || anysmall) {
1437 bool anyfinite =
false, allone =
true;
1439 if (std::isfinite(s.nservers)) {
1441 if (s.nservers != 1.0) allone =
false;
1444 method = (anyfinite && allone) ?
"egflin" :
"lin";
1465 return solver_amvald(L, d, o2, method, converged, init_sol);
1470 const bool het_fcfs_own =
1471 (method ==
"ab" || method ==
"schmidt" || method ==
"schmidt-ext");
1476 if (!st.lldscaling.empty()) cond2 =
false;
1482 if (!(cond1 && cond2 && cond3))
return solver_amvald(L, d,
opt, method, converged, init_sol);
1496 std::vector<int> N(C, 0);
1497 for (std::size_t c = 0; c < C; ++c)
1498 N[c] = std::isfinite(d.
Nchain[c]) ?
static_cast<int>(std::llround(d.
Nchain[c]))
1502 std::vector<T> Nt(C, zero);
1503 for (std::size_t c = 0; c < C; ++c)
1507 std::vector<pfqn::SchedStrategy> types;
1508 for (std::size_t a = 0; a < nq; ++a) {
1522 for (std::size_t a = 0; a < nq && !bad; ++a)
1523 for (std::size_t c = 0; c < C; ++c) {
1525 if (Q0(a, c) < zero) {
1534 const bool direct_ms = (method ==
"ab" || method ==
"schmidt" || method ==
"schmidt-ext");
1535 const bool seidmann = (
opt.multiserver ==
"default" ||
opt.multiserver ==
"seidmann");
1542 for (std::size_t a = 0; a < nq; ++a) {
1543 const double c = pf.
S[a];
1544 if (!std::isfinite(c))
continue;
1545 for (std::size_t k = 0; k < C; ++k)
1548 for (std::size_t k = 0; k < C; ++k)
1552 }
else if (
opt.multiserver ==
"softmin") {
1560 std::vector<T> Zsum(C, zero);
1561 for (std::size_t c = 0; c < C; ++c)
1562 for (std::size_t a = 0; a < Zm.rows(); ++a) Zsum[c] += Zm(a, c);
1564 bool allone =
true, anyinf =
false;
1565 for (
double s : pf.
S) {
1566 if (!std::isfinite(s)) anyinf =
true;
1567 else if (s != 1.0) allone =
false;
1571 Matrix<T> Qq(nq, C, zero), Uq(nq, C, zero), Qz(nz, C, zero);
1572 std::vector<T> X(C, zero);
1574 bool have_delay_rows =
false;
1576 if (method ==
"sqni") {
1578 if (!(M == 2 && nq == 1 && nz == 1))
1580 "solver_amva: method 'sqni' applies only to a model of one queueing station and "
1581 "one infinite server");
1584 "solver_amva: method 'sqni' solves a quadratic and evaluates a square root, "
1585 "which exact arithmetic has no representation for; use the double or real "
1588 std::vector<T> Lv(C, zero);
1589 for (std::size_t c = 0; c < C; ++c) Lv[c] = Dm(0, c);
1591 for (std::size_t c = 0; c < C; ++c) {
1598 }
else if (method ==
"bs") {
1599 std::vector<pfqn::AmvaSched> bstype;
1600 for (std::size_t a = 0; a < nq; ++a)
1610 }
else if (method ==
"lcp" || method ==
"chow") {
1614 std::vector<pfqn::AmvaSched> lctype;
1615 for (std::size_t a = 0; a < nq; ++a)
1622 static_cast<std::size_t
>(
opt.iter_max), Q0)
1624 static_cast<std::size_t
>(
opt.iter_max), Q0);
1629 }
else if (method ==
"pamb" || method ==
"pami" || method ==
"pamt") {
1639 }
else if (method ==
"clust") {
1643 Dm, Nt, Zsum, std::vector<std::vector<std::size_t> >(),
1645 static_cast<std::size_t
>(
opt.iter_max));
1650 }
else if (method ==
"dmlin") {
1660 }
else if (method ==
"tay") {
1667 "solver_amva: Tay's approximation is defined for single-server stations; use "
1668 "'default' or 'lin'");
1671 "solver_amva: method 'tay' stops on a tolerance evaluated with transcendentals, "
1672 "which exact arithmetic has no representation for; use the double or real "
1676 Dm, Nt, Zsum,
opt.tol,
static_cast<std::size_t
>(
opt.iter_max), Q0);
1682 }
else if (method ==
"scat") {
1692 }
else if (method ==
"aql") {
1697 "solver_amva: AQL cannot handle multi-server stations; use 'default' or 'lin'");
1700 "solver_amva: method 'aql' stops on a relative tolerance evaluated with "
1701 "transcendentals, which exact arithmetic has no representation for; use the "
1702 "double or real backend");
1711 }
else if (method ==
"qsa") {
1716 "solver_amva: QSA cannot handle multi-server stations; use 'default' or 'lin'");
1719 "solver_amva: method 'qsa' stops on a residual tolerance evaluated with "
1720 "transcendentals, which exact arithmetic has no representation for; use the "
1721 "double or real backend");
1727 static_cast<std::size_t
>(
opt.iter_max));
1733 }
else if (direct_ms) {
1737 std::vector<int> nsfull;
1738 std::vector<pfqn::SchedStrategy> schedfull;
1740 for (std::size_t a = 0; a < nz; ++a) {
1741 for (std::size_t c = 0; c < C; ++c) Dfull(a, c) = pf.
Z(a, c);
1742 nsfull.push_back(1);
1746 for (std::size_t a = 0; a < nq; ++a) {
1747 for (std::size_t c = 0; c < C; ++c) {
1748 Dfull(nz + a, c) = pf.
D(a, c);
1753 const int c_i = std::isfinite(pf.
S[a]) ?
static_cast<int>(std::llround(pf.
S[a])) : 1;
1754 nsfull.push_back(c_i);
1755 Sfull(nz + a, 0) = c_i;
1756 schedfull.push_back(types[a]);
1760 "solver_amva: the Akyildiz-Bolch and Schmidt multiserver methods use marginal "
1761 "weights with non-integer powers and floors, which exact arithmetic has no "
1762 "representation for; use the double or real backend");
1763 }
else if (method ==
"ab") {
1766 for (std::size_t a = 0; a < nq; ++a)
1767 for (std::size_t c = 0; c < C; ++c) {
1768 Qq(a, c) = r.
QN(nz + a, c);
1769 Uq(a, c) = r.
UN(nz + a, c);
1771 for (std::size_t a = 0; a < nz; ++a)
1772 for (std::size_t c = 0; c < C; ++c) Qz(a, c) = r.
QN(a, c);
1774 iters =
static_cast<int>(r.
totiter);
1775 }
else if (method ==
"schmidt") {
1777 for (std::size_t a = 0; a < nq; ++a)
1778 for (std::size_t c = 0; c < C; ++c) Qq(a, c) = r.
QN(nz + a, c);
1779 for (std::size_t a = 0; a < nz; ++a)
1780 for (std::size_t c = 0; c < C; ++c) Qz(a, c) = r.
QN(a, c);
1789 std::vector<double> sx_n(C, 0.0);
1790 for (std::size_t c = 0; c < C; ++c) sx_n[c] = d.
Nchain[c];
1791 std::vector<bool> sx_fcfs;
1792 for (std::size_t a = 0; a < nq; ++a)
1794 SchedStrategy::FCFS);
1795 const std::string sx_reason =
1800 for (std::size_t a = 0; a < nq; ++a)
1801 for (std::size_t c = 0; c < C; ++c) Qq(a, c) = r.
QN(nz + a, c);
1802 for (std::size_t a = 0; a < nz; ++a)
1803 for (std::size_t c = 0; c < C; ++c) Qz(a, c) = r.
QN(a, c);
1807 have_delay_rows =
true;
1810 for (std::size_t a = 0; a < nq; ++a) {
1811 const double c_i = std::isfinite(pf.
S[a]) ? pf.
S[a] : 1.0;
1812 for (std::size_t k = 0; k < C; ++k)
1815 }
else if (method ==
"lin" || method ==
"gflin" || method ==
"egflin") {
1819 if (st.cdscaling || st.jdscaling)
1825 if (allone ||
opt.multiserver ==
"krzesinski") {
1826 std::vector<int> ns;
1827 for (std::size_t a = 0; a < nq; ++a)
1828 ns.push_back(std::isfinite(pf.
S[a]) ?
static_cast<int>(std::llround(pf.
S[a])) : 1);
1829 if (allone) ns.assign(nq, 1);
1831 pf.
lambda, Dm, N, Zm, ns, types,
opt.tol,
opt.iter_max, mx, Q0);
1836 }
else if (
opt.multiserver ==
"conway") {
1839 "solver_amva: the Conway multiserver correction evaluates transcendentals, "
1840 "which exact arithmetic has no representation for; use the double or real "
1843 std::vector<int> ns;
1844 for (std::size_t a = 0; a < nq; ++a)
1845 ns.push_back(std::isfinite(pf.
S[a]) ?
static_cast<int>(std::llround(pf.
S[a]))
1864 return solver_amvald(L, d, o2, method, converged, init_sol);
1867 Matrix<T> Q(M, C, zero), U(M, C, zero), Tp(M, C, zero), R(M, C, zero);
1868 for (std::size_t a = 0; a < nq; ++a)
1869 for (std::size_t c = 0; c < C; ++c) {
1877 for (std::size_t a = 0; a < nz; ++a)
1878 for (std::size_t c = 0; c < C; ++c) {
1882 (void)have_delay_rows;
1885 if (seidmann && !direct_ms) {
1886 for (std::size_t a = 0; a < nq; ++a) {
1887 const double c = pf.
S[a];
1888 if (!std::isfinite(c) || c <= 1.0)
continue;
1889 for (std::size_t k = 0; k < C; ++k)
1895 for (std::size_t i = 0; i < M; ++i)
1896 for (std::size_t c = 0; c < C; ++c) {
1897 Tp(i, c) = T(d.
Vchain(i, c) * X[c]);
1898 if (Tp(i, c) != zero) R(i, c) = T(Q(i, c) / Tp(i, c));
1903 std::vector<T> Cyc(C, zero);
1904 for (std::size_t c = 0; c < C; ++c) {
1905 if (!(X[c] > zero))
continue;
1907 for (std::size_t a = 0; a < nz; ++a) z += pf.
Z(a, c);