5#ifndef LINE_API_PFQN_MVAMS_H
6#define LINE_API_PFQN_MVAMS_H
134 const Matrix<T>& mu,
bool stabilize =
true) {
135 const std::size_t M = L.
rows();
136 const std::size_t R = N.size();
138 throw InputError(
"pfqn_mvald: demand matrix and population vector disagree on the class count");
144 res.
XN.assign(R, zero);
146 res.
UN.assign(M, zero);
147 res.
CN.assign(R, zero);
159 res.
lG = -std::numeric_limits<double>::infinity();
166 throw InputError(
"pfqn_mvald: rate matrix and demand matrix disagree on the station count");
167 if (
static_cast<long>(mu.
cols()) < Nt)
168 throw InputError(
"pfqn_mvald: rate matrix needs one column per job in the total population");
170 std::vector<T> Zsum(R, zero);
172 if (Z.
cols() != R)
throw InputError(
"pfqn_mvald: Z and N disagree on the class count");
173 for (std::size_t k = 0; k < Z.
rows(); ++k)
174 for (std::size_t r = 0; r < R; ++r) Zsum[r] += Z(k, r);
177 const std::size_t K =
static_cast<std::size_t
>(Nt) + 1;
178 const std::vector<std::size_t> prods =
plane_sizes(N);
182 std::vector<T> pi(total * M * K, zero);
185 std::vector<int> n(R, 0);
186 std::vector<T> x(R, zero);
189 const std::size_t idx =
pop_index(n, prods);
191 for (
int v : n) nsum += v;
194 std::fill(x.begin(), x.end(), zero);
196 for (std::size_t s = 0; s < R; ++s) {
197 if (n[s] == 0)
continue;
198 const std::size_t idx_s = idx - prods[s];
200 for (std::size_t i = 0; i < M; ++i) {
202 for (
long k = 1; k <= nsum; ++k)
203 w += (L(i, s) / mu(i,
static_cast<std::size_t
>(k - 1))) *
205 pi[(idx_s * M + i) * K +
static_cast<std::size_t
>(k - 1)];
209 if (ctot == zero)
throw NumericError(
"pfqn_mvald: zero total residence time");
213 for (
long k = 1; k <= nsum; ++k) {
214 for (std::size_t i = 0; i < M; ++i) {
216 for (std::size_t s = 0; s < R; ++s) {
217 if (n[s] == 0)
continue;
218 const std::size_t idx_s = idx - prods[s];
219 acc += (L(i, s) / mu(i,
static_cast<std::size_t
>(k - 1))) * x[s] *
220 pi[(idx_s * M + i) * K +
static_cast<std::size_t
>(k - 1)];
222 pi[(idx * M + i) * K +
static_cast<std::size_t
>(k)] = acc;
226 for (std::size_t i = 0; i < M; ++i) {
228 for (
long k = 1; k <= nsum; ++k) acc += pi[(idx * M + i) * K + static_cast<std::size_t>(k)];
232 if (stabilize) p0 = tiny;
234 pi[(idx * M + i) * K + 0] = p0;
240 for (
long r =
static_cast<long>(R) - 1; r >= 0; --r)
246 bool prefixFull =
true;
247 for (
long r = 0; r < last_nnz; ++r)
252 bool suffixEmpty =
true;
253 for (std::size_t r =
static_cast<std::size_t
>(last_nnz) + 1; r < R; ++r)
258 if (prefixFull && suffixEmpty) {
259 const T& xr = x[
static_cast<std::size_t
>(last_nnz)];
260 if (xr == zero)
throw NumericError(
"pfqn_mvald: zero throughput on the G path");
269 const std::size_t idxN = total - 1;
272 for (std::size_t i = 0; i < M; ++i)
273 for (std::size_t k = 0; k < K; ++k) res.
PI(i, k) = pi[(idxN * M + i) * K + k];
275 for (std::size_t i = 0; i < M; ++i) {
276 for (std::size_t r = 0; r < R; ++r) res.
QN(i, r) = res.
WN(i, r) * res.
XN[r];
277 res.
UN[i] = one - res.
PI(i, 0);
279 for (std::size_t r = 0; r < R; ++r) {
312 const std::vector<int>& N,
const Matrix<T>& Z,
313 const std::vector<int>& mi) {
314 const std::size_t M = D.
rows();
315 const std::size_t R = N.size();
317 throw InputError(
"pfqn_mvamx: demand matrix and population vector disagree on the class count");
318 if (lambda.size() != R)
319 throw InputError(
"pfqn_mvamx: arrival-rate vector and population vector disagree on the class count");
325 res.
XN.assign(R, zero);
330 res.
lG = std::numeric_limits<double>::quiet_NaN();
332 std::vector<std::size_t> openClasses, closedClasses;
333 for (std::size_t r = 0; r < R; ++r) {
335 openClasses.push_back(r);
337 closedClasses.push_back(r);
339 for (std::size_t r = 0; r < R; ++r)
341 throw InputError(
"pfqn_mvamx: arrival rate cannot be specified on a closed class");
343 std::vector<T> Zsum(R, zero);
345 if (Z.
cols() != R)
throw InputError(
"pfqn_mvamx: Z and N disagree on the class count");
346 for (std::size_t k = 0; k < Z.
rows(); ++k)
347 for (std::size_t r = 0; r < R; ++r) Zsum[r] += Z(k, r);
350 for (std::size_t r : openClasses) {
351 for (std::size_t i = 0; i < M; ++i) res.
UN(i, r) = lambda[r] * D(i, r);
352 res.
XN[r] = lambda[r];
355 std::vector<T> UNt(M, zero);
356 for (std::size_t i = 0; i < M; ++i)
357 for (std::size_t r = 0; r < R; ++r) UNt[i] += res.
UN(i, r);
359 const std::size_t nClosed = closedClasses.size();
363 for (std::size_t i = 0; i < M; ++i) {
364 const T slack = one - UNt[i];
366 throw NumericError(
"pfqn_mvamx: open classes saturate a station, no closed solution exists");
367 for (std::size_t c = 0; c < nClosed; ++c) Dc(i, c) = D(i, closedClasses[c]) / slack;
369 std::vector<int> Nc(nClosed);
371 for (std::size_t c = 0; c < nClosed; ++c) {
372 Nc[c] = N[closedClasses[c]];
373 Zc(0, c) = Zsum[closedClasses[c]];
377 for (std::size_t c = 0; c < nClosed; ++c) {
378 const std::size_t r = closedClasses[c];
379 res.
XN[r] = closed.
XN[c];
380 for (std::size_t i = 0; i < M; ++i) {
381 res.
QN(i, r) = closed.
QN(i, c);
382 res.
CN(i, r) = closed.
CN(i, c);
389 for (std::size_t i = 0; i < M; ++i)
390 for (std::size_t c = 0; c < nClosed; ++c) {
391 const std::size_t r = closedClasses[c];
392 res.
UN(i, r) = res.
XN[r] * D(i, r);
395 for (std::size_t i = 0; i < M; ++i) {
397 for (std::size_t c = 0; c < nClosed; ++c) qc += QNc(i, c);
398 const T slack = one - UNt[i];
399 for (std::size_t r : openClasses) {
401 throw NumericError(
"pfqn_mvamx: open classes saturate a station");
402 res.
CN(i, r) = D(i, r) * (one + qc) / slack;
403 res.
QN(i, r) = res.
CN(i, r) * res.
XN[r];
437LdmxEc<T> pfqn_ldmx_ec(
const std::vector<T>& lambda,
const Matrix<T>& D,
const Matrix<T>& mu) {
438 const std::size_t M = mu.rows();
439 const std::size_t Nt = mu.cols();
440 const std::size_t R = D.cols();
442 throw InputError(
"pfqn_ldmx_ec: demand matrix and rate matrix disagree on the station count");
443 if (lambda.size() != R)
444 throw InputError(
"pfqn_ldmx_ec: arrival-rate vector and demand matrix disagree on the class count");
446 const T zero = num_traits<T>::from_int(0);
447 const T one = num_traits<T>::from_int(1);
450 out.Lo.assign(M, zero);
451 for (std::size_t i = 0; i < M; ++i)
452 for (std::size_t r = 0; r < R; ++r) out.Lo[i] += lambda[r] * D(i, r);
455 std::vector<std::size_t> b(M, 1);
456 std::size_t maxb = 1;
457 for (std::size_t i = 0; i < M; ++i) {
458 for (std::size_t j = 1; j <= Nt; ++j) {
459 if (mu(i, j - 1) == mu(i, Nt - 1)) {
464 if (b[i] > maxb) maxb = b[i];
469 const std::size_t Cn = Nt + 1 + maxb;
470 Matrix<T> C(M, Cn, zero);
471 for (std::size_t i = 0; i < M; ++i)
472 for (std::size_t j = 1; j <= Cn; ++j) {
473 const T& m = j <= Nt ? mu(i, j - 1) : mu(i, Nt - 1);
474 if (m == zero)
throw InputError(
"pfqn_ldmx_ec: service rate must be nonzero");
475 C(i, j - 1) = one / m;
482 for (std::size_t i = 0; i < M; ++i) {
483 const std::size_t bi = b[i];
484 const T Cb = C(i, bi - 1);
485 const T slack = one - out.Lo[i] * Cb;
487 throw NumericError(
"pfqn_ldmx_ec: open-class load saturates a station, effective capacity diverges");
488 const T invSlack = one / slack;
492 for (std::size_t j = 1; j + 1 <= bi; ++j) ratioProd *= C(i, j - 1) / Cb;
494 const long nb =
static_cast<long>(bi) - 2;
495 std::vector<T> E1(Nt + 1, zero);
498 for (std::size_t n = 0; n <= Nt; ++n) {
500 out.E(i, n) =
num_pow_int(invSlack,
static_cast<unsigned>(n + 1));
501 out.Eprime(i, n) = Cb * out.E(i, n);
506 E1[0] = invSlack * ratioProd;
508 E1[n] = invSlack * Cb / C(i, n - 1) * E1[n - 1];
510 T E2 = zero, E3 = zero, E2prime = zero;
512 T F2 = zero, F3 = zero, F2p = zero;
513 for (
long n0 = 0; n0 <= nb; ++n0) {
516 F3 = (n == 0) ? ratioProd : Cb / C(i, n - 1) * F3prev0;
519 const T fac = num_traits<T>::from_int(
static_cast<long>(n) + n0) /
520 num_traits<T>::from_int(n0);
521 F2 = fac * out.Lo[i] * C(i,
static_cast<std::size_t
>(
static_cast<long>(n) + n0) - 1) * F2;
522 F3 = fac * out.Lo[i] * Cb * F3;
523 F2p = fac * out.Lo[i] * C(i,
static_cast<std::size_t
>(
static_cast<long>(n) + n0)) * F2p;
525 if (n0 == 0) F3prev0 = F3;
536 out.E(i, n) = E1[n] + E2 - E3;
538 out.Eprime(i, n) = Cb * E1[n] + E2prime - Cb * E3;
540 out.Eprime(i, n) = Cb * out.E(i, n);
543 for (std::size_t n = 1; n <= Nt; ++n) {
544 if (out.E(i, n - 1) == zero)
545 throw NumericError(
"pfqn_ldmx_ec: vanishing effective capacity");
546 out.EC(i, n - 1) = C(i, n - 1) * out.E(i, n) / out.E(i, n - 1);
575 const std::size_t M = D.
rows();
576 const std::size_t R = N.size();
578 throw InputError(
"pfqn_mvaldmx: demand matrix and population vector disagree on the class count");
579 if (lambda.size() != R)
580 throw InputError(
"pfqn_mvaldmx: arrival-rate vector and population vector disagree on the class count");
582 throw InputError(
"pfqn_mvaldmx: rate matrix and demand matrix disagree on the station count");
587 std::vector<std::size_t> openClasses, closedClasses;
589 for (std::size_t r = 0; r < R; ++r) {
591 openClasses.push_back(r);
593 closedClasses.push_back(r);
597 for (std::size_t r = 0; r < R; ++r)
599 throw InputError(
"pfqn_mvaldmx: arrival rate cannot be specified on a closed class");
600 if (
static_cast<long>(mu.
cols()) < Nct)
602 "pfqn_mvaldmx: the load-dependent rates must be given for at least the maximum closed population");
606 for (std::size_t i = 0; i < M; ++i) {
607 for (std::size_t j = 0; j < mu.
cols(); ++j) mux(i, j) = mu(i, j);
608 mux(i, mu.
cols()) = mu(i, mu.
cols() - 1);
610 const detail::LdmxEc<T> ec = detail::pfqn_ldmx_ec(lambda, D, mux);
613 res.
XN.assign(R, zero);
620 const std::size_t Cc = closedClasses.size();
622 std::vector<int> Nc(Cc, 0);
623 std::vector<T> Zc(Cc, zero);
624 for (std::size_t c = 0; c < Cc; ++c) {
625 const std::size_t r = closedClasses[c];
627 for (std::size_t i = 0; i < M; ++i) Dc(i, c) = D(i, r);
630 if (Z.
cols() != R)
throw InputError(
"pfqn_mvaldmx: Z and N disagree on the class count");
631 for (std::size_t k = 0; k < Z.
rows(); ++k)
632 for (std::size_t c = 0; c < Cc; ++c) Zc[c] += Z(k, closedClasses[c]);
635 const std::vector<std::size_t> prods =
plane_sizes(Nc);
637 const std::size_t K =
static_cast<std::size_t
>(Nct) + 1;
640 std::vector<T> Pc(total * M * K, zero);
641 for (std::size_t i = 0; i < M; ++i) Pc[(0 * M + i) * K + 0] = one;
645 std::vector<T> x(Cc, zero);
646 std::vector<int> nvec(Cc, 0);
649 const std::size_t idx =
pop_index(nvec, prods);
651 for (
int v : nvec)
nc += v;
654 std::fill(x.begin(), x.end(), zero);
656 for (std::size_t i = 0; i < M; ++i)
657 for (std::size_t c = 0; c < Cc; ++c) {
658 if (nvec[c] == 0)
continue;
659 const std::size_t idx_c = idx - prods[c];
661 for (
long k = 1; k <=
nc; ++k)
663 ec.EC(i,
static_cast<std::size_t
>(k - 1)) *
664 Pc[(idx_c * M + i) * K +
static_cast<std::size_t
>(k - 1)];
668 for (std::size_t c = 0; c < Cc; ++c) {
669 if (nvec[c] == 0)
continue;
671 for (std::size_t i = 0; i < M; ++i) ctot += w(i, c);
672 if (ctot == zero)
throw NumericError(
"pfqn_mvaldmx: zero total residence time");
676 for (std::size_t i = 0; i < M; ++i) {
677 for (
long k = 1; k <=
nc; ++k) {
679 for (std::size_t c = 0; c < Cc; ++c) {
680 if (nvec[c] == 0)
continue;
681 const std::size_t idx_c = idx - prods[c];
682 acc += Dc(i, c) * ec.EC(i,
static_cast<std::size_t
>(k - 1)) * x[c] *
683 Pc[(idx_c * M + i) * K +
static_cast<std::size_t
>(k - 1)];
685 Pc[(idx * M + i) * K +
static_cast<std::size_t
>(k)] = acc;
688 for (
long k = 1; k <= nc; ++k) acc += Pc[(idx * M + i) * K + static_cast<std::size_t>(k)];
690 if (p0 < tiny) p0 = tiny;
691 Pc[(idx * M + i) * K + 0] = p0;
695 for (
long c =
static_cast<long>(Cc) - 1; c >= 0; --c)
701 bool prefixFull =
true;
702 for (
long c = 0; c < last_nnz; ++c)
703 if (nvec[c] != Nc[c]) {
707 bool suffixEmpty =
true;
708 for (std::size_t c =
static_cast<std::size_t
>(last_nnz) + 1; c < Cc; ++c)
713 if (prefixFull && suffixEmpty) {
714 const T& xr = x[
static_cast<std::size_t
>(last_nnz)];
715 if (xr > zero) res.
G /= xr;
722 const std::size_t idxN = total - 1;
724 for (std::size_t c = 0; c < Cc; ++c) {
725 const std::size_t r = closedClasses[c];
727 for (std::size_t i = 0; i < M; ++i) {
728 res.
CN(i, r) = w(i, c);
729 res.
QN(i, r) = x[c] * w(i, c);
731 const std::size_t idx_c = idxN - prods[c];
733 for (
long k = 1; k <= Nct; ++k) {
734 const std::size_t kk =
static_cast<std::size_t
>(k - 1);
735 if (ec.E(i, kk) == zero)
throw NumericError(
"pfqn_mvaldmx: vanishing effective capacity");
736 u += Dc(i, c) * x[c] * ec.Eprime(i, kk) / ec.E(i, kk) * Pc[(idx_c * M + i) * K + kk];
743 for (std::size_t r : openClasses) {
744 res.
XN[r] = lambda[r];
745 for (std::size_t i = 0; i < M; ++i) {
746 T q = zero, u = zero;
747 for (
long k = 0; k <= Nct; ++k) {
748 const std::size_t kk =
static_cast<std::size_t
>(k);
750 Pc[(idxN * M + i) * K + kk];
751 if (ec.E(i, kk + 1) == zero)
throw NumericError(
"pfqn_mvaldmx: vanishing effective capacity");
752 u += lambda[r] * ec.Eprime(i, kk + 1) / ec.E(i, kk + 1) * Pc[(idxN * M + i) * K + kk];
755 if (lambda[r] == zero)
756 throw NumericError(
"pfqn_mvaldmx: an open class must have a positive arrival rate");
757 res.
CN(i, r) = q / lambda[r];
781 const std::vector<int>& N,
const Matrix<T>& Z,
const std::vector<int>& S) {
782 const std::size_t M = D.
rows();
783 const std::size_t R = N.size();
784 if (S.size() != M)
throw InputError(
"pfqn_mvaldms: server-count vector has the wrong length");
788 for (std::size_t r = 0; r < R; ++r)
792 for (std::size_t i = 0; i < M; ++i)
793 for (
long k = 1; k <= Nct; ++k) {
794 const long c = S[i] ==
INF_SERVERS ? k : (k < S[i] ? k : S[i]);
800 for (std::size_t i = 0; i < M; ++i) {
801 for (std::size_t r = 0; r < R; ++r) {
808 if (rate == zero) res.
UN(i, r) = zero;
841 const std::vector<int>& N,
const Matrix<T>& Z,
const std::vector<int>& mi,
842 const std::vector<int>& S) {
843 const std::size_t M = L.
rows();
844 const std::size_t R = N.size();
846 throw InputError(
"pfqn_mvams: demand matrix and population vector disagree on the class count");
847 if (!mi.empty() && mi.size() != M)
848 throw InputError(
"pfqn_mvams: multiplicity vector has the wrong length");
849 if (!S.empty() && S.size() != M)
850 throw InputError(
"pfqn_mvams: server-count vector has the wrong length");
855 std::vector<int> Sv = S.empty() ? std::vector<int>(M, 1) : S;
856 std::vector<int> miv = mi.empty() ? std::vector<int>(M, 1) : mi;
857 std::vector<T> lam = lambda.empty() ? std::vector<T>(R, zero) : lambda;
859 throw InputError(
"pfqn_mvams: arrival-rate vector and population vector disagree on the class count");
862 bool hasOpenClasses =
false;
863 for (std::size_t r = 0; r < R; ++r) {
865 hasOpenClasses =
true;
870 bool hasMultiServer =
false;
871 for (std::size_t i = 0; i < M; ++i)
873 hasMultiServer =
true;
876 bool hasReplicas =
false;
877 for (std::size_t i = 0; i < M; ++i)
883 if (!hasMultiServer) {
884 if (hasOpenClasses)
return pfqn_mvamx(lam, L, N, Z, miv);
889 throw InputError(
"pfqn_mvams: queue replicas are not available in exact MVA with multiserver stations");
891 if (hasOpenClasses) {
895 res.
lG = std::numeric_limits<double>::quiet_NaN();
899 Matrix<T> mu(M,
static_cast<std::size_t
>(Ntot > 0 ? Ntot : 1), one);
900 for (std::size_t i = 0; i < M; ++i)
901 for (
long k = 1; k <= Ntot; ++k) {
902 const long c = Sv[i] ==
INF_SERVERS ? k : (k < Sv[i] ? k : Sv[i]);
916 for (std::size_t i = 0; i < M; ++i)
917 for (std::size_t r = 0; r < R; ++r) {
919 res.
CN(i, r) = res.
QN(i, r) / res.
XN[r];
921 ? T(res.
XN[r] * L(i, r))
934 const std::vector<int>& N,
const Matrix<T>& Z,
const std::vector<int>& S) {
935 return pfqn_mvams(lambda, L, N, Z, std::vector<int>(), S);
941 const std::vector<int>& N,
const Matrix<T>& Z) {
942 return pfqn_mvams(lambda, L, N, Z, std::vector<int>(), std::vector<int>());
957 const std::vector<int>& N,
const Matrix<T>& Z,
958 const std::vector<int>& mi,
const std::vector<int>& S,
960 const std::size_t M = L.
rows();
961 const std::size_t R = N.size();
963 throw InputError(
"pfqn_mvams_ilock: an interlock matrix is required; use pfqn_mvams for the standard arrival theorem");
965 throw InputError(
"pfqn_mvams_ilock: demand matrix and population vector disagree on the class count");
966 if (!mi.empty() && mi.size() != M)
967 throw InputError(
"pfqn_mvams_ilock: multiplicity vector has the wrong length");
968 if (!S.empty() && S.size() != M)
969 throw InputError(
"pfqn_mvams_ilock: server-count vector has the wrong length");
972 std::vector<int> Sv = S.empty() ? std::vector<int>(M, 1) : S;
973 std::vector<int> miv = mi.empty() ? std::vector<int>(M, 1) : mi;
974 std::vector<T> lam = lambda.empty() ? std::vector<T>(R, zero) : lambda;
976 throw InputError(
"pfqn_mvams_ilock: arrival-rate vector and population vector disagree on the class count");
978 for (std::size_t r = 0; r < R; ++r)
980 throw InputError(
"pfqn_mvams_ilock: the interlock correction is available in exact MVA "
981 "for closed single-server models only; use an AMVA method");
982 for (std::size_t i = 0; i < M; ++i)
984 throw InputError(
"pfqn_mvams_ilock: the interlock correction is available in exact MVA "
985 "for closed single-server models only; use an AMVA method");
NumericError(const std::string &what)
The exception types the port throws.
Dense matrix and non-owning view.
MvaResult< T > pfqn_mva(const Matrix< T > &L, const std::vector< int > &N, const Matrix< T > &Z, const std::vector< int > &mi)
Exact Mean Value Analysis for closed product-form networks (Reiser and Lavenberg 1980).
MvaResult< T > pfqn_mvams(const std::vector< T > &lambda, const Matrix< T > &L, const std::vector< int > &N, const Matrix< T > &Z, const std::vector< int > &mi, const std::vector< int > &S)
General-purpose exact MVA for mixed networks with multiserver stations.
constexpr int OPEN_CLASS
Marks an open (infinite-population) class in a population vector.
MvaResult< T > pfqn_mvamx(const std::vector< T > &lambda, const Matrix< T > &D, const std::vector< int > &N, const Matrix< T > &Z, const std::vector< int > &mi)
Exact MVA for a mixed open/closed network of single-server stations.
MvaLdResult< T > pfqn_mvald(const Matrix< T > &L, const std::vector< int > &N, const Matrix< T > &Z, const Matrix< T > &mu, bool stabilize=true)
Exact MVA for a closed network of load-dependent stations.
MvaResult< T > pfqn_mvaldms(const std::vector< T > &lambda, const Matrix< T > &D, const std::vector< int > &N, const Matrix< T > &Z, const std::vector< int > &S)
Exact MVA for mixed open/closed networks with multiserver stations.
MvaResult< T > pfqn_mvaldmx(const std::vector< T > &lambda, const Matrix< T > &D, const std::vector< int > &N, const Matrix< T > &Z, const Matrix< T > &mu)
Exact MVA for mixed open/closed networks with limited load dependence.
constexpr int INF_SERVERS
Marks an infinite-server station in a server-count vector.
bool is_open_class(int n)
True when the population entry denotes an open class.
MvaResult< T > pfqn_mva_ilock(const Matrix< T > &L, const std::vector< int > &N, const Matrix< T > &Z, const std::vector< int > &mi, const Matrix< T > &IL)
Exact MVA recursion carrying the interlocked-flow correction.
MvaResult< T > pfqn_mvams_ilock(const std::vector< T > &lambda, const Matrix< T > &L, const std::vector< int > &N, const Matrix< T > &Z, const std::vector< int > &mi, const std::vector< int > &S, const Matrix< T > &IL)
MVA entry point for models carrying the interlocked-flow correction.
std::size_t population_count(const std::vector< int > &N)
Number of population vectors n with 0 <= n <= N.
std::vector< std::size_t > plane_sizes(const std::vector< int > &N)
Mixed-radix plane sizes: prods[r] = prod_{s<r} (N[s]+1).
bool next_pop(std::vector< int > &n, const std::vector< int > &N)
Advance n to the next population vector in the lattice 0 <= n <= N, odometer order with the last clas...
T num_pow_int(const T &base, unsigned e)
Integer power, valid in any field (no transcendental requirement).
std::size_t pop_index(const std::vector< int > &n, const std::vector< std::size_t > &prods)
Index of n in the lattice, 0-based (MATLAB hashpop is 1-based).
Number-type abstraction for the templated API port.
Exact Mean Value Analysis for closed product-form networks (Reiser and Lavenberg 1980).
Population-vector enumeration and combinatorics.
Result of pfqn_mvald, mirroring the seven MATLAB outputs.
Matrix< T > WN
(M x R) residence time at the full population
double lG
log of the normalizing constant
Matrix< T > QN
(M x R) mean queue length
std::vector< T > XN
(R) per-class throughput
std::vector< T > UN
(M) utilization, 1 - P(station empty)
std::vector< T > CN
(R) cycle time, exclusive of think time
Matrix< T > PI
(M x (Nt+1)) marginal queue-length distribution at N
bool isNumStable
false once a marginal probability had to be clamped
std::vector< T > XN
(R) per-class throughput
Matrix< T > QN
(M x R) mean queue length
double lG
log of the normalizing constant
Matrix< T > CN
(M x R) residence time
Matrix< T > UN
(M x R) utilization