537 const std::string& method =
opt.method;
561 if (method.compare(0, 5,
"spnlp") != 0) {
562 for (std::size_t i = 0; i < L.
nodes.size(); ++i)
565 "solver_ba_analyzer: Method '" + method +
"' is parameterized by demands and a "
566 "population, which the marking of a Petri net is not. Use "
567 "'spnlp2.upper'/'spnlp2.lower' or the 'spnlp1.upper'/'spnlp1.lower' pair on this model.");
574 if (method ==
"auto.upper" || method ==
"auto.lower") {
575 const bool up = method ==
"auto.upper";
576 const detail::ScDemands<T> sc = detail::ba_sc_demands(L);
577 static const char*
const cand_up[] = {
"aba.upper",
"bjb.upper",
"pb.upper",
"gb.upper",
578 "sb.upper",
"mw.upper",
"ssd.upper",
"cub.upper"};
579 static const char*
const cand_lo[] = {
"aba.lower",
"bjb.lower",
"pb.lower",
580 "gb.lower",
"sb.lower",
"mw.lower",
581 "ssd.lower",
"mbjb.lower",
"ldac.lower"};
582 const char*
const* cand = up ? cand_up : cand_lo;
583 const std::size_t ncand = up ?
sizeof(cand_up) /
sizeof(*cand_up)
584 :
sizeof(cand_lo) /
sizeof(*cand_lo);
587 for (std::size_t ci = 0; ci < ncand; ++ci) {
593 }
catch (
const std::exception&) {
596 if (r.
X.empty())
continue;
599 if (!have || (up && Xc < Xbest) || (!up && Xc > Xbest)) {
606 "' found no feasible bound for this model");
607 return detail::ba_fill(L, sc.V, sc.N, Xbest, sc.Z, sc.D, up);
612 const bool single_class_family =
613 method ==
"aba.upper" || method ==
"aba.lower" || method ==
"bjb.upper" ||
614 method ==
"bjb.lower" || method ==
"pb.upper" || method ==
"pb.lower" ||
615 method ==
"sb.upper" || method ==
"sb.lower" || method ==
"gb.upper" ||
616 method ==
"gb.lower";
618 if (single_class_family) {
619 const detail::ScDemands<T> sc = detail::ba_sc_demands(L);
620 const std::vector<T>& V = sc.V;
621 const std::vector<T>& D = sc.D;
625 const detail::DemandStats<T> st = detail::ba_demand_stats(D);
627 if (method ==
"aba.upper") {
628 const T X = detail::ba_min(T(one / st.max), T(Nt / T(Z + st.sum)));
629 return detail::ba_fill_xc(L, V, N, X, T(Z + Nt * st.sum),
true);
631 if (method ==
"aba.lower") {
632 const T X = T(Nt / T(Z + Nt * st.sum));
633 return detail::ba_fill_xc(L, V, N, X, T(Z + st.sum),
false);
640 const T xup1 = detail::ba_min(T(one / st.max), T(Nm1 / T(Z + st.sum)));
641 const T xlo1 = T(Nm1 / T(Z + Nm1 * st.sum));
642 if (method ==
"bjb.upper") {
643 const T C = T(Z + st.sum + st.max * T(Nm1 - Z * xlo1));
644 const T X = detail::ba_min(
645 T(one / st.max), T(Nt / T(Z + st.sum + st.mean * T(Nm1 - Z * xup1))));
646 return detail::ba_fill_xc(L, V, N, X, C,
true);
648 if (method ==
"bjb.lower") {
649 const T C = T(Z + st.sum + st.mean * T(Nm1 - Z * xup1));
650 const T X = T(Nt / T(Z + st.sum + st.max * T(Nm1 - Z * xlo1)));
651 return detail::ba_fill_xc(L, V, N, X, C,
false);
653 if (method ==
"pb.upper" || method ==
"pb.lower") {
654 T d2 = zero, dN = zero, dNm1 = zero;
655 for (
const T& d : D) {
658 dNm1 +=
num_pow_int(d,
static_cast<unsigned>(N - 1));
660 const T Dpb2 = T(d2 / st.sum);
661 const T DpbN = T(dN / dNm1);
662 if (method ==
"pb.upper") {
663 const T C = T(Z + st.sum + DpbN * T(Nm1 - Z * xlo1));
664 const T X = detail::ba_min(
665 T(one / st.max), T(Nt / T(Z + st.sum + Dpb2 * T(Nm1 - Z * xup1))));
666 return detail::ba_fill_xc(L, V, N, X, C,
true);
668 const T C = T(Z + st.sum + Dpb2 * T(Nm1 - Z * xup1));
669 const T X = T(Nt / T(Z + st.sum + DpbN * T(Nm1 - Z * xlo1)));
670 return detail::ba_fill_xc(L, V, N, X, C,
false);
672 if (method ==
"sb.upper" || method ==
"sb.lower") {
676 for (std::size_t i = 0; i < L.
nstations; ++i)
677 if (detail::ba_is_delay(L, i))
679 "' does not support infinite-server stations");
680 T A1 = zero, A2 = zero, A3 = zero;
681 for (
const T& d : D) {
686 if (method ==
"sb.upper") {
687 const T C = T(Z + A1 + Nm1 * T(T(A1 * A2 + A3) / T(A1 * A1 + A2)));
688 const T X = detail::ba_min(T(one / st.max), T(Nt / C));
692 return detail::ba_fill_xc(L, V, N, X, C,
false);
697 "solver_ba_analyzer: method 'sb.lower' needs an (N-1)-st root and is "
698 "unavailable in exact arithmetic");
702 for (
const T& d : D) AN +=
num_pow_int(d,
static_cast<unsigned>(N));
705 const T root = pow(T(AN / A1), T(one / Nm1));
706 const T C = T(Z + A1 + Nm1 * root);
707 return detail::ba_fill_xc(L, V, N, T(Nt / C), C,
false);
713 "' solve a quadratic and are unavailable in exact arithmetic");
715 const bool up = (method ==
"gb.upper");
718 const T X = up ? detail::ba_min(T(one / st.max), Xup) : Xlo;
719 const T C = T(Nt / (up ? Xlo : Xup));
724 const T Xden = up ? Xlo : Xup;
731 for (std::size_t i = 0; i < L.
nstations; ++i) {
732 s.
Tp(i, 0) = T(V[i] * X);
733 if (detail::ba_is_delay(L, i)) {
734 s.
R(i, 0) = T(one / L.
rates(i, 0));
735 s.
Q(i, 0) = T(X * s.
R(i, 0));
738 s.
R(i, 0) = T(T(s.
Q(i, 0) / Xden) / V[i]);
741 s.
U(i, 0) = detail::ba_is_delay(L, i) ? s.
Q(i, 0) : T(s.
Tp(i, 0) / L.
rates(i, 0));
750 if (method ==
"lr.upper" || method ==
"lr.lower") {
754 const detail::ScDemands<T> sc = detail::ba_sc_demands(L);
755 for (std::size_t i = 0; i < L.
nstations; ++i)
756 if (detail::ba_is_delay(L, i))
758 "' does not support delay (infinite-server) stations");
759 const bool up = (method ==
"lr.upper");
761 const std::vector<T>& V = sc.V;
764 par.
M =
static_cast<int>(M);
767 par.
N =
static_cast<int>(sc.N);
770 for (std::size_t i = 0; i < M; ++i) {
778 if (totV == zero)
throw NumericError(
ErrorKind{
"Degenerate"},
"solver_ba_analyzer: the visit ratios are all zero");
779 for (std::size_t i = 0; i < M; ++i)
780 for (std::size_t j = 0; j < M; ++j) par.
r(i, j) = T(V[j] / totV);
783 std::vector<T> U(M, zero);
784 for (std::size_t ti = 0; ti < M; ++ti) {
789 "' linear program did not solve (" + r.
status +
")");
797 for (std::size_t i = 0; i < M; ++i) {
798 if (V[i] == zero)
continue;
799 const T cand = T(U[i] * L.
rates(i, 0) / V[i]);
800 if (!any || cand < X) {
809 BaSolution<T> s = detail::ba_fill(L, V, sc.N, X, zero, sc.D, up);
810 for (std::size_t i = 0; i < M; ++i) s.
U(i, 0) = U[i];
811 s.
lG = std::numeric_limits<double>::quiet_NaN();
815 if (method ==
"mapamva.upper" || method ==
"mapamva.lower") {
831 const detail::ScDemands<T> sc = detail::ba_sc_demands(L);
832 for (std::size_t i = 0; i < L.
nstations; ++i)
833 if (detail::ba_is_delay(L, i))
835 "solver_ba_analyzer: method '" + method +
836 "' does not support delay (infinite-server) stations: the MAP-AMVA program "
837 "of Casale-Smirni (DSN 2009) is written for a network of queues and the "
838 "paper names the delay extension as open work. Use a QRF method, which "
839 "carries the load-dependent rate law");
840 const bool up = (method ==
"mapamva.upper");
842 const std::vector<T>& V = sc.V;
847 std::vector<std::pair<Matrix<T>,
Matrix<T>>> MAPs(M);
848 std::vector<int> kph(M, 1);
849 for (std::size_t i = 0; i < M; ++i) {
851 if (m.
D0.rows() == 0) {
856 MAPs[i] = std::make_pair(m.
D0, m.
D1);
857 kph[i] =
static_cast<int>(m.
D0.rows());
860 std::vector<std::size_t> phased;
861 for (std::size_t i = 0; i < M; ++i)
862 if (kph[i] > 1) phased.push_back(i);
863 if (phased.size() > 1) {
865 for (std::size_t j = 0; j < phased.size(); ++j)
866 names += (j ?
", " :
"") + std::to_string(phased[j] + 1);
868 "solver_ba_analyzer: method '" + method +
869 "' carries phases at ONE station: the LP gives queue M the (D0,D1) pair and "
870 "every other queue a scalar rate. Stations " + names +
" are all "
871 "non-exponential. Use a QRF method, whose q carries a phase at every station");
876 const std::size_t map_idx = phased.empty() ? M - 1 : phased.front();
877 std::vector<std::size_t>
perm;
879 for (std::size_t i = 0; i < M; ++i)
880 if (i != map_idx)
perm.push_back(i);
881 perm.push_back(map_idx);
882 const int K = kph[map_idx];
883 const std::size_t Ks =
static_cast<std::size_t
>(K);
886 par.
M =
static_cast<int>(M);
887 par.
N =
static_cast<int>(sc.N);
889 par.
muM.resize(M - 1);
890 for (std::size_t a = 0; a + 1 < M; ++a) par.
muM[a] = L.
rates(
perm[a], 0);
898 for (std::size_t a = 0; a < Ks; ++a)
899 for (std::size_t b = 0; b < Ks; ++b) {
900 par.
muMAP(a, b) = MAPs[map_idx].second(a, b);
901 par.
v(a, b) = (a == b) ? zero : MAPs[map_idx].first(a, b);
904 for (std::size_t a = 0; a < M; ++a)
905 for (std::size_t b = 0; b < M; ++b) par.
r(a, b) = L.
rt(
perm[a],
perm[b]);
907 std::vector<T> Vp(M, zero), Sp(M, zero);
908 for (std::size_t a = 0; a < M; ++a) {
909 const std::size_t i =
perm[a];
912 mi.
D0 = MAPs[i].first;
913 mi.
D1 = MAPs[i].second;
923 std::vector<T> umax(M, zero), umin(M, zero), qbnd(M, zero);
924 for (std::size_t a = 0; a < M; ++a) {
925 const int ti =
static_cast<int>(a);
934 if (!ru.
ok || !rl.
ok || !rq.
ok)
936 "' linear program did not solve (" +
948 T x_up = zero, x_lo = zero;
949 for (std::size_t a = 0; a < M; ++a) {
950 const T load = T(Vp[a] * Sp[a]);
951 if (load <= zero)
continue;
952 const T cu = T(umax[a] / load), cl = T(umin[a] / load);
958 if (cu < x_up) x_up = cu;
959 if (cl > x_lo) x_lo = cl;
964 "' found no station with both a positive visit ratio and a "
965 "positive mean service time");
966 const T xb = up ? x_up : x_lo;
967 const T xopp = up ? x_lo : x_up;
975 for (std::size_t a = 0; a < M; ++a) {
976 const std::size_t i =
perm[a];
977 s.
U(i, 0) = up ? umax[a] : umin[a];
979 s.
Tp(i, 0) = T(Vp[a] * xb);
980 s.
R(i, 0) = (xopp > zero && Vp[a] > zero) ? T(qbnd[a] / (Vp[a] * xopp)) : zero;
986 s.
C.assign(1, xopp > zero
994 if (method ==
"mw.upper" || method ==
"mw.lower") {
998 const detail::ChainView<T> v = detail::ba_chain_view(L);
1000 Matrix<T> Vq(Kq, C, zero), Sq(Kq, C, zero);
1001 std::vector<pfqn::MwrbbSched> schedq(Kq);
1002 for (std::size_t k = 0; k < Kq; ++k) {
1003 const std::size_t i = v.qstat[k];
1004 schedq[k] = detail::mwrbb_disc_code(L.
stations[i].sched);
1005 for (std::size_t c = 0; c < C; ++c) {
1006 Vq(k, c) = v.d.Vchain(i, c);
1007 Sq(k, c) = v.d.STchain(i, c);
1012 std::vector<int> prioc(C, 0);
1013 for (std::size_t c = 0; c < C; ++c) {
1018 for (std::size_t r : L.
inchain[c]) p = std::min(p, L.
classes[r - 1].prio);
1023 const bool up = (method ==
"mw.upper");
1024 const std::vector<T>& Xchain = up ? b.
Xup : b.
Xlo;
1025 Matrix<T> Tchain(M, C, zero), Uchain(M, C, zero), Qchain(M, C, zero);
1026 for (std::size_t c = 0; c < C; ++c)
1027 for (std::size_t i = 0; i < M; ++i) {
1028 Tchain(i, c) = T(Xchain[c] * v.d.Vchain(i, c));
1029 Uchain(i, c) = T(Xchain[c] * v.d.Lchain(i, c));
1031 Qchain = detail::ba_chain_qfill(Uchain, Xchain, v.d.Lchain, v.Nv, v.isdelay, up);
1033 L, v.d, Qchain, Uchain,
Matrix<T>(), Tchain, Xchain);
1041 s.
lG = std::numeric_limits<double>::quiet_NaN();
1048 if (method ==
"looping.upper" || method ==
"looping.lower") {
1049 const detail::ChainView<T> v = detail::ba_chain_view(L);
1052 for (std::size_t k = 0; k < Kq; ++k)
1053 for (std::size_t c = 0; c < C; ++c) Lq(k, c) = v.d.Lchain(v.qstat[k], c);
1055 const bool up = (method ==
"looping.upper");
1056 const std::vector<T>& Xchain = up ? b.
Xup : b.
Xlo;
1057 Matrix<T> Tchain(M, C, zero), Uchain(M, C, zero), Qchain(M, C, zero);
1058 for (std::size_t c = 0; c < C; ++c)
1059 for (std::size_t i = 0; i < M; ++i) {
1060 Tchain(i, c) = T(Xchain[c] * v.d.Vchain(i, c));
1061 Uchain(i, c) = T(Xchain[c] * v.d.Lchain(i, c));
1063 Qchain = detail::ba_chain_qfill(Uchain, Xchain, v.d.Lchain, v.Nv, v.isdelay, up);
1065 L, v.d, Qchain, Uchain,
Matrix<T>(), Tchain, Xchain);
1073 s.
lG = std::numeric_limits<double>::quiet_NaN();
1081 if (method ==
"bpt.lower") {
1090 if (method ==
"bgt.upper") {
1099 if (method ==
"snc.upper") {
1105 if (method ==
"cub.upper" || method ==
"mbjb.lower") {
1106 const detail::ChainView<T> v = detail::ba_chain_view(L);
1109 for (std::size_t k = 0; k < Kq; ++k)
1110 for (std::size_t c = 0; c < C; ++c) Lq(k, c) = v.d.Lchain(v.qstat[k], c);
1112 const bool up = (method ==
"cub.upper");
1113 const std::vector<T>& Xchain = up ? b.
Xub : b.
Xlb;
1114 Matrix<T> Tchain(M, C, zero), Uchain(M, C, zero), Qchain(M, C, zero);
1115 for (std::size_t c = 0; c < C; ++c)
1116 for (std::size_t i = 0; i < M; ++i) {
1117 Tchain(i, c) = T(Xchain[c] * v.d.Vchain(i, c));
1118 Uchain(i, c) = T(Xchain[c] * v.d.Lchain(i, c));
1120 Qchain = detail::ba_chain_qfill(Uchain, Xchain, v.d.Lchain, v.Nv, v.isdelay, up);
1122 L, v.d, Qchain, Uchain,
Matrix<T>(), Tchain, Xchain);
1130 s.
lG = std::numeric_limits<double>::quiet_NaN();
1137 if (method ==
"harel.upper" || method ==
"harel.lower") {
1138 const detail::ScDemands<T> sch = detail::ba_sc_demands(L);
1139 if (!(sch.Z == zero))
1141 "' does not support think times (infinite-server stations)");
1142 const int Nh =
static_cast<int>(sch.N);
1143 const int maxUB = Nh < 7 ? Nh : 7;
1145 const bool uph = method ==
"harel.upper";
1149 for (
const T& d : sch.D)
1150 if (d > dmax) dmax = d;
1152 const T ext = maxUB >= 2 ? b.
UB[
static_cast<std::size_t
>(maxUB)] : b.
TH[1];
1153 Xb = ext < cap ? ext : cap;
1155 return detail::ba_fill(L, sch.V, sch.N, Xb, sch.Z, sch.D, uph);
1159 const bool hier = method ==
"pbh.upper" || method ==
"pbh.lower" || method ==
"cbh.upper" ||
1160 method ==
"cbh.lower" || method ==
"bjbh.upper" || method ==
"bjbh.lower" ||
1161 method ==
"ssd.upper" ||
1162 method ==
"ssd.lower" || method ==
"sib.upper" || method ==
"sib.lower" ||
1163 method ==
"scb.upper" || method ==
"scb.lower" ||
1164 method ==
"ldac.lower";
1168 const detail::ScDemands<T> sc = detail::ba_sc_demands(L);
1169 const bool up = method.size() > 6 && method.compare(method.size() - 6, 6,
".upper") == 0;
1171 const int lvl =
opt.level;
1176 if (method ==
"ssd.upper" || method ==
"ssd.lower") {
1177 std::vector<T> cvec;
1178 for (std::size_t i = 0; i < L.
nstations; ++i)
1179 if (!detail::ba_is_delay(L, i))
1182 return detail::ba_fill(L, sc.V, sc.N, up ? b.
Xhi : b.
Xlo, sc.Z, sc.D, up);
1184 if (method ==
"ldac.lower") {
1188 const std::vector<T> cz(sc.D.size(), zero);
1193 "solver_ba_analyzer: method 'ldac.lower' requires the asymptotic regime N >= "
1195 return detail::ba_fill(L, sc.V, sc.N, b.
Xlo, sc.Z, sc.D,
false);
1197 if (method ==
"scb.upper" || method ==
"scb.lower") {
1204 if (!(sc.Z == zero))
1206 "' supports Z=0 (no delay station) only; Theorem 3 rests on "
1207 "the delay-free balanced-network throughput");
1209 return detail::ba_fill(L, sc.V, sc.N, up ? b.Xhi : b.Xlo, sc.Z, sc.D, up);
1211 if (method ==
"pbh.upper" || method ==
"pbh.lower") {
1213 return detail::ba_fill(L, sc.V, sc.N, up ? b.
Xhi : b.
Xlo, sc.Z, sc.D, up);
1215 if (method ==
"bjbh.upper" || method ==
"bjbh.lower") {
1217 return detail::ba_fill(L, sc.V, sc.N, up ? b.
Xhi : b.
Xlo, sc.Z, sc.D, up);
1219 if (method ==
"cbh.upper" || method ==
"cbh.lower") {
1221 return detail::ba_fill(L, sc.V, sc.N, up ? b.
Xhi : b.
Xlo, sc.Z, sc.D, up);
1224 if (!(sc.Z == zero))
1226 "' supports Z=0 (no delay station) only; delay needs the SIB "
1227 "Section-3.2 extension");
1230 "' solves a quadratic and is unavailable in exact arithmetic");
1233 return detail::ba_fill(L, sc.V, sc.N, up ? b.
Xhi : b.
Xlo, sc.Z, sc.D, up);