5#ifndef LINE_API_PFQN_MVAMS_H
6#define LINE_API_PFQN_MVAMS_H
130 const Matrix<T>& mu,
bool stabilize =
true) {
131 const std::size_t M = L.
rows();
132 const std::size_t R = N.size();
134 throw InputError(
"pfqn_mvald: demand matrix and population vector disagree on the class count");
140 res.
XN.assign(R, zero);
142 res.
UN.assign(M, zero);
143 res.
CN.assign(R, zero);
155 res.
lG = -std::numeric_limits<double>::infinity();
162 throw InputError(
"pfqn_mvald: rate matrix and demand matrix disagree on the station count");
163 if (
static_cast<long>(mu.
cols()) < Nt)
164 throw InputError(
"pfqn_mvald: rate matrix needs one column per job in the total population");
166 std::vector<T> Zsum(R, zero);
168 if (Z.
cols() != R)
throw InputError(
"pfqn_mvald: Z and N disagree on the class count");
169 for (std::size_t k = 0; k < Z.
rows(); ++k)
170 for (std::size_t r = 0; r < R; ++r) Zsum[r] += Z(k, r);
173 const std::size_t K =
static_cast<std::size_t
>(Nt) + 1;
174 const std::vector<std::size_t> prods =
plane_sizes(N);
178 std::vector<T> pi(total * M * K, zero);
181 std::vector<int> n(R, 0);
182 std::vector<T> x(R, zero);
185 const std::size_t idx =
pop_index(n, prods);
187 for (
int v : n) nsum += v;
190 std::fill(x.begin(), x.end(), zero);
192 for (std::size_t s = 0; s < R; ++s) {
193 if (n[s] == 0)
continue;
194 const std::size_t idx_s = idx - prods[s];
196 for (std::size_t i = 0; i < M; ++i) {
198 for (
long k = 1; k <= nsum; ++k)
199 w += (L(i, s) / mu(i,
static_cast<std::size_t
>(k - 1))) *
201 pi[(idx_s * M + i) * K +
static_cast<std::size_t
>(k - 1)];
205 if (ctot == zero)
throw NumericError(
ErrorKind{
"Degenerate"},
"pfqn_mvald: zero total residence time");
209 for (
long k = 1; k <= nsum; ++k) {
210 for (std::size_t i = 0; i < M; ++i) {
212 for (std::size_t s = 0; s < R; ++s) {
213 if (n[s] == 0)
continue;
214 const std::size_t idx_s = idx - prods[s];
215 acc += (L(i, s) / mu(i,
static_cast<std::size_t
>(k - 1))) * x[s] *
216 pi[(idx_s * M + i) * K +
static_cast<std::size_t
>(k - 1)];
218 pi[(idx * M + i) * K +
static_cast<std::size_t
>(k)] = acc;
222 for (std::size_t i = 0; i < M; ++i) {
224 for (
long k = 1; k <= nsum; ++k) acc += pi[(idx * M + i) * K + static_cast<std::size_t>(k)];
229 if (stabilize) p0 = tiny;
231 pi[(idx * M + i) * K + 0] = p0;
237 for (
long r =
static_cast<long>(R) - 1; r >= 0; --r)
243 bool prefixFull =
true;
244 for (
long r = 0; r < last_nnz; ++r)
249 bool suffixEmpty =
true;
250 for (std::size_t r =
static_cast<std::size_t
>(last_nnz) + 1; r < R; ++r)
255 if (prefixFull && suffixEmpty) {
256 const T& xr = x[
static_cast<std::size_t
>(last_nnz)];
257 if (xr == zero)
throw NumericError(
ErrorKind{
"Degenerate"},
"pfqn_mvald: zero throughput on the G path");
266 const std::size_t idxN = total - 1;
269 for (std::size_t i = 0; i < M; ++i)
270 for (std::size_t k = 0; k < K; ++k) res.
PI(i, k) = pi[(idxN * M + i) * K + k];
272 for (std::size_t i = 0; i < M; ++i) {
273 for (std::size_t r = 0; r < R; ++r) res.
QN(i, r) = res.
WN(i, r) * res.
XN[r];
274 res.
UN[i] = one - res.
PI(i, 0);
276 for (std::size_t r = 0; r < R; ++r) {
309 const std::vector<int>& N,
const Matrix<T>& Z,
310 const std::vector<int>& mi) {
311 const std::size_t M = D.
rows();
312 const std::size_t R = N.size();
314 throw InputError(
"pfqn_mvamx: demand matrix and population vector disagree on the class count");
315 if (lambda.size() != R)
316 throw InputError(
"pfqn_mvamx: arrival-rate vector and population vector disagree on the class count");
322 res.
XN.assign(R, zero);
327 res.
lG = std::numeric_limits<double>::quiet_NaN();
329 std::vector<std::size_t> openClasses, closedClasses;
330 for (std::size_t r = 0; r < R; ++r) {
332 openClasses.push_back(r);
334 closedClasses.push_back(r);
336 for (std::size_t r = 0; r < R; ++r)
338 throw InputError(
"pfqn_mvamx: arrival rate cannot be specified on a closed class");
340 std::vector<T> Zsum(R, zero);
342 if (Z.
cols() != R)
throw InputError(
"pfqn_mvamx: Z and N disagree on the class count");
343 for (std::size_t k = 0; k < Z.
rows(); ++k)
344 for (std::size_t r = 0; r < R; ++r) Zsum[r] += Z(k, r);
347 for (std::size_t r : openClasses) {
348 for (std::size_t i = 0; i < M; ++i) res.
UN(i, r) = lambda[r] * D(i, r);
349 res.
XN[r] = lambda[r];
352 std::vector<T> UNt(M, zero);
353 for (std::size_t i = 0; i < M; ++i)
354 for (std::size_t r = 0; r < R; ++r) UNt[i] += res.
UN(i, r);
356 const std::size_t nClosed = closedClasses.size();
360 for (std::size_t i = 0; i < M; ++i) {
362 const T slack = one - UNt[i];
363 for (std::size_t c = 0; c < nClosed; ++c) Dc(i, c) = D(i, closedClasses[c]) / slack;
365 std::vector<int> Nc(nClosed);
367 for (std::size_t c = 0; c < nClosed; ++c) {
368 Nc[c] = N[closedClasses[c]];
369 Zc(0, c) = Zsum[closedClasses[c]];
373 for (std::size_t c = 0; c < nClosed; ++c) {
374 const std::size_t r = closedClasses[c];
375 res.
XN[r] = closed.
XN[c];
376 for (std::size_t i = 0; i < M; ++i) {
377 res.
QN(i, r) = closed.
QN(i, c);
378 res.
CN(i, r) = closed.
CN(i, c);
385 for (std::size_t i = 0; i < M; ++i)
386 for (std::size_t c = 0; c < nClosed; ++c) {
387 const std::size_t r = closedClasses[c];
388 res.
UN(i, r) = res.
XN[r] * D(i, r);
391 for (std::size_t i = 0; i < M; ++i) {
393 for (std::size_t c = 0; c < nClosed; ++c) qc += QNc(i, c);
394 const T slack = one - UNt[i];
395 for (std::size_t r : openClasses) {
396 res.
CN(i, r) = D(i, r) * (one + qc) / slack;
397 res.
QN(i, r) = res.
CN(i, r) * res.
XN[r];
431LdmxEc<T> pfqn_ldmx_ec(
const std::vector<T>& lambda,
const Matrix<T>& D,
const Matrix<T>& mu) {
432 const std::size_t M = mu.rows();
433 const std::size_t Nt = mu.cols();
434 const std::size_t R = D.cols();
436 throw InputError(
"pfqn_ldmx_ec: demand matrix and rate matrix disagree on the station count");
437 if (lambda.size() != R)
438 throw InputError(
"pfqn_ldmx_ec: arrival-rate vector and demand matrix disagree on the class count");
440 const T zero = num_traits<T>::from_int(0);
441 const T one = num_traits<T>::from_int(1);
444 out.Lo.assign(M, zero);
445 for (std::size_t i = 0; i < M; ++i)
446 for (std::size_t r = 0; r < R; ++r) out.Lo[i] += lambda[r] * D(i, r);
449 std::vector<std::size_t> b(M, 1);
450 std::size_t maxb = 1;
451 for (std::size_t i = 0; i < M; ++i) {
452 for (std::size_t j = 1; j <= Nt; ++j) {
453 if (mu(i, j - 1) == mu(i, Nt - 1)) {
458 if (b[i] > maxb) maxb = b[i];
463 const std::size_t Cn = Nt + 1 + maxb;
464 Matrix<T> C(M, Cn, zero);
465 for (std::size_t i = 0; i < M; ++i)
466 for (std::size_t j = 1; j <= Cn; ++j) {
467 const T& m = j <= Nt ? mu(i, j - 1) : mu(i, Nt - 1);
468 if (m == zero)
throw InputError(
"pfqn_ldmx_ec: service rate must be nonzero");
469 C(i, j - 1) = one / m;
476 for (std::size_t i = 0; i < M; ++i) {
477 const std::size_t bi = b[i];
478 const T Cb = C(i, bi - 1);
479 const T slack = one - out.Lo[i] * Cb;
481 throw NumericError(ErrorKind{
"Instability"},
"pfqn_ldmx_ec: open-class load saturates a station, effective capacity diverges");
482 const T invSlack = one / slack;
486 for (std::size_t j = 1; j + 1 <= bi; ++j) ratioProd *= C(i, j - 1) / Cb;
488 const long nb =
static_cast<long>(bi) - 2;
489 std::vector<T> E1(Nt + 1, zero);
492 for (std::size_t n = 0; n <= Nt; ++n) {
494 out.E(i, n) =
num_pow_int(invSlack,
static_cast<unsigned>(n + 1));
495 out.Eprime(i, n) = Cb * out.E(i, n);
500 E1[0] = invSlack * ratioProd;
502 E1[n] = invSlack * Cb / C(i, n - 1) * E1[n - 1];
504 T E2 = zero, E3 = zero, E2prime = zero;
506 T F2 = zero, F3 = zero, F2p = zero;
507 for (
long n0 = 0; n0 <= nb; ++n0) {
510 F3 = (n == 0) ? ratioProd : Cb / C(i, n - 1) * F3prev0;
513 const T fac = num_traits<T>::from_int(
static_cast<long>(n) + n0) /
514 num_traits<T>::from_int(n0);
515 F2 = fac * out.Lo[i] * C(i,
static_cast<std::size_t
>(
static_cast<long>(n) + n0) - 1) * F2;
516 F3 = fac * out.Lo[i] * Cb * F3;
517 F2p = fac * out.Lo[i] * C(i,
static_cast<std::size_t
>(
static_cast<long>(n) + n0)) * F2p;
519 if (n0 == 0) F3prev0 = F3;
530 out.E(i, n) = E1[n] + E2 - E3;
532 out.Eprime(i, n) = Cb * E1[n] + E2prime - Cb * E3;
534 out.Eprime(i, n) = Cb * out.E(i, n);
541 for (std::size_t n = 1; n <= Nt; ++n) {
542 if (num_traits<T>::is_exact && out.E(i, n - 1) == zero)
543 throw NumericError(ErrorKind{
"Degenerate"},
"pfqn_ldmx_ec: vanishing effective capacity");
544 out.EC(i, n - 1) = C(i, n - 1) * out.E(i, n) / out.E(i, n - 1);
573 const std::size_t M = D.
rows();
574 const std::size_t R = N.size();
576 throw InputError(
"pfqn_mvaldmx: demand matrix and population vector disagree on the class count");
577 if (lambda.size() != R)
578 throw InputError(
"pfqn_mvaldmx: arrival-rate vector and population vector disagree on the class count");
580 throw InputError(
"pfqn_mvaldmx: rate matrix and demand matrix disagree on the station count");
585 std::vector<std::size_t> openClasses, closedClasses;
587 for (std::size_t r = 0; r < R; ++r) {
589 openClasses.push_back(r);
591 closedClasses.push_back(r);
595 for (std::size_t r = 0; r < R; ++r)
597 throw InputError(
"pfqn_mvaldmx: arrival rate cannot be specified on a closed class");
598 if (
static_cast<long>(mu.
cols()) < Nct)
600 "pfqn_mvaldmx: the load-dependent rates must be given for at least the maximum closed population");
604 for (std::size_t i = 0; i < M; ++i) {
605 for (std::size_t j = 0; j < mu.
cols(); ++j) mux(i, j) = mu(i, j);
606 mux(i, mu.
cols()) = mu(i, mu.
cols() - 1);
608 const detail::LdmxEc<T> ec = detail::pfqn_ldmx_ec(lambda, D, mux);
611 res.
XN.assign(R, zero);
618 const std::size_t Cc = closedClasses.size();
620 std::vector<int> Nc(Cc, 0);
621 std::vector<T> Zc(Cc, zero);
622 for (std::size_t c = 0; c < Cc; ++c) {
623 const std::size_t r = closedClasses[c];
625 for (std::size_t i = 0; i < M; ++i) Dc(i, c) = D(i, r);
628 if (Z.
cols() != R)
throw InputError(
"pfqn_mvaldmx: Z and N disagree on the class count");
629 for (std::size_t k = 0; k < Z.
rows(); ++k)
630 for (std::size_t c = 0; c < Cc; ++c) Zc[c] += Z(k, closedClasses[c]);
633 const std::vector<std::size_t> prods =
plane_sizes(Nc);
635 const std::size_t K =
static_cast<std::size_t
>(Nct) + 1;
638 std::vector<T> Pc(total * M * K, zero);
639 for (std::size_t i = 0; i < M; ++i) Pc[(0 * M + i) * K + 0] = one;
643 std::vector<T> x(Cc, zero);
644 std::vector<int> nvec(Cc, 0);
647 const std::size_t idx =
pop_index(nvec, prods);
649 for (
int v : nvec)
nc += v;
652 std::fill(x.begin(), x.end(), zero);
654 for (std::size_t i = 0; i < M; ++i)
655 for (std::size_t c = 0; c < Cc; ++c) {
656 if (nvec[c] == 0)
continue;
657 const std::size_t idx_c = idx - prods[c];
659 for (
long k = 1; k <=
nc; ++k)
661 ec.EC(i,
static_cast<std::size_t
>(k - 1)) *
662 Pc[(idx_c * M + i) * K +
static_cast<std::size_t
>(k - 1)];
666 for (std::size_t c = 0; c < Cc; ++c) {
667 if (nvec[c] == 0)
continue;
669 for (std::size_t i = 0; i < M; ++i) ctot += w(i, c);
670 if (ctot == zero)
throw NumericError(
ErrorKind{
"Degenerate"},
"pfqn_mvaldmx: zero total residence time");
674 for (std::size_t i = 0; i < M; ++i) {
675 for (
long k = 1; k <=
nc; ++k) {
677 for (std::size_t c = 0; c < Cc; ++c) {
678 if (nvec[c] == 0)
continue;
679 const std::size_t idx_c = idx - prods[c];
680 acc += Dc(i, c) * ec.EC(i,
static_cast<std::size_t
>(k - 1)) * x[c] *
681 Pc[(idx_c * M + i) * K +
static_cast<std::size_t
>(k - 1)];
683 Pc[(idx * M + i) * K +
static_cast<std::size_t
>(k)] = acc;
686 for (
long k = 1; k <= nc; ++k) acc += Pc[(idx * M + i) * K + static_cast<std::size_t>(k)];
688 if (p0 < tiny) p0 = tiny;
689 Pc[(idx * M + i) * K + 0] = p0;
693 for (
long c =
static_cast<long>(Cc) - 1; c >= 0; --c)
699 bool prefixFull =
true;
700 for (
long c = 0; c < last_nnz; ++c)
701 if (nvec[c] != Nc[c]) {
705 bool suffixEmpty =
true;
706 for (std::size_t c =
static_cast<std::size_t
>(last_nnz) + 1; c < Cc; ++c)
711 if (prefixFull && suffixEmpty) {
712 const T& xr = x[
static_cast<std::size_t
>(last_nnz)];
713 if (xr > zero) res.
G /= xr;
720 const std::size_t idxN = total - 1;
722 for (std::size_t c = 0; c < Cc; ++c) {
723 const std::size_t r = closedClasses[c];
725 for (std::size_t i = 0; i < M; ++i) {
726 res.
CN(i, r) = w(i, c);
727 res.
QN(i, r) = x[c] * w(i, c);
729 const std::size_t idx_c = idxN - prods[c];
731 for (
long k = 1; k <= Nct; ++k) {
732 const std::size_t kk =
static_cast<std::size_t
>(k - 1);
735 u += Dc(i, c) * x[c] * ec.Eprime(i, kk) / ec.E(i, kk) * Pc[(idx_c * M + i) * K + kk];
742 for (std::size_t r : openClasses) {
743 res.
XN[r] = lambda[r];
744 for (std::size_t i = 0; i < M; ++i) {
745 T q = zero, u = zero;
746 for (
long k = 0; k <= Nct; ++k) {
747 const std::size_t kk =
static_cast<std::size_t
>(k);
749 Pc[(idxN * M + i) * K + kk];
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(
ErrorKind{
"Degenerate"},
"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)
799 long ncol = Nct > 1 ? Nct : 1;
800 for (std::size_t i = 0; i < M; ++i)
801 if (S[i] !=
INF_SERVERS && S[i] > ncol) ncol = S[i];
804 for (std::size_t i = 0; i < M; ++i)
805 for (
long k = 1; k <= ncol; ++k) {
806 const long c = S[i] ==
INF_SERVERS ? k : (k < S[i] ? k : S[i]);
812 for (std::size_t i = 0; i < M; ++i) {
813 for (std::size_t r = 0; r < R; ++r) {
820 if (rate == zero) res.
UN(i, r) = zero;
853 const std::vector<int>& N,
const Matrix<T>& Z,
const std::vector<int>& mi,
854 const std::vector<int>& S) {
855 const std::size_t M = L.
rows();
856 const std::size_t R = N.size();
858 throw InputError(
"pfqn_mvams: demand matrix and population vector disagree on the class count");
859 if (!mi.empty() && mi.size() != M)
860 throw InputError(
"pfqn_mvams: multiplicity vector has the wrong length");
861 if (!S.empty() && S.size() != M)
862 throw InputError(
"pfqn_mvams: server-count vector has the wrong length");
867 std::vector<int> Sv = S.empty() ? std::vector<int>(M, 1) : S;
868 std::vector<int> miv = mi.empty() ? std::vector<int>(M, 1) : mi;
869 std::vector<T> lam = lambda.empty() ? std::vector<T>(R, zero) : lambda;
871 throw InputError(
"pfqn_mvams: arrival-rate vector and population vector disagree on the class count");
874 bool hasOpenClasses =
false;
875 for (std::size_t r = 0; r < R; ++r) {
877 hasOpenClasses =
true;
882 bool hasMultiServer =
false;
883 for (std::size_t i = 0; i < M; ++i)
885 hasMultiServer =
true;
888 bool hasReplicas =
false;
889 for (std::size_t i = 0; i < M; ++i)
895 if (!hasMultiServer) {
896 if (hasOpenClasses)
return pfqn_mvamx(lam, L, N, Z, miv);
900 if (hasOpenClasses) {
905 throw InputError(
ErrorKind{
"Argument"},
"pfqn_mvams: queue replicas are not available in exact MVA for mixed models");
909 res.lG = std::numeric_limits<double>::quiet_NaN();
913 Matrix<T> mu(M,
static_cast<std::size_t
>(Ntot > 0 ? Ntot : 1), one);
914 for (std::size_t i = 0; i < M; ++i)
915 for (
long k = 1; k <= Ntot; ++k) {
916 const long c = Sv[i] ==
INF_SERVERS ? k : (k < Sv[i] ? k : Sv[i]);
928 for (std::size_t i = 0; i < M && i < ld.
UN.size(); ++i) res.
UN(i, 0) = ld.
UN[i];
933 for (std::size_t i = 0; i < M; ++i)
934 for (std::size_t r = 0; r < R; ++r) {
936 res.
CN(i, r) = res.
QN(i, r) / res.
XN[r];
948 const std::vector<int>& N,
const Matrix<T>& Z,
const std::vector<int>& S) {
949 return pfqn_mvams(lambda, L, N, Z, std::vector<int>(), S);
955 const std::vector<int>& N,
const Matrix<T>& Z) {
956 return pfqn_mvams(lambda, L, N, Z, std::vector<int>(), std::vector<int>());
971 const std::vector<int>& N,
const Matrix<T>& Z,
972 const std::vector<int>& mi,
const std::vector<int>& S,
974 const std::size_t M = L.
rows();
975 const std::size_t R = N.size();
977 throw InputError(
"pfqn_mvams_ilock: an interlock matrix is required; use pfqn_mvams for the standard arrival theorem");
979 throw InputError(
"pfqn_mvams_ilock: demand matrix and population vector disagree on the class count");
980 if (!mi.empty() && mi.size() != M)
981 throw InputError(
"pfqn_mvams_ilock: multiplicity vector has the wrong length");
982 if (!S.empty() && S.size() != M)
983 throw InputError(
"pfqn_mvams_ilock: server-count vector has the wrong length");
986 std::vector<int> Sv = S.empty() ? std::vector<int>(M, 1) : S;
987 std::vector<int> miv = mi.empty() ? std::vector<int>(M, 1) : mi;
988 std::vector<T> lam = lambda.empty() ? std::vector<T>(R, zero) : lambda;
990 throw InputError(
"pfqn_mvams_ilock: arrival-rate vector and population vector disagree on the class count");
992 for (std::size_t r = 0; r < R; ++r)
994 throw InputError(
"pfqn_mvams_ilock: the interlock correction is available in exact MVA "
995 "for closed single-server models only; use an AMVA method");
996 for (std::size_t i = 0; i < M; ++i)
998 throw InputError(
"pfqn_mvams_ilock: the interlock correction is available in exact MVA "
999 "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.
Conservation laws of a layered queueing network, enumerated from its structure.
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.
The <kind> part of a LINE:<category>:<kind> identifier (see _kb/17-error-vocabulary....
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