72 const std::vector<int>& mi) {
73 const std::size_t M = L.
rows();
74 const std::size_t R = N.size();
76 throw InputError(
"pfqn_mva: demand matrix and population vector disagree on the class count");
77 if (!mi.empty() && mi.size() != M)
78 throw InputError(
"pfqn_mva: multiplicity vector has the wrong length");
84 res.
XN.assign(R, zero);
91 bool anyPositive =
false;
93 if (v < 0)
throw InputError(
"pfqn_mva: negative population");
94 if (v > 0) anyPositive =
true;
96 if (!anyPositive || M == 0)
return res;
98 std::vector<T> Zsum(R, zero);
100 if (Z.
cols() != R)
throw InputError(
"pfqn_mva: Z and N disagree on the class count");
101 for (std::size_t k = 0; k < Z.
rows(); ++k)
102 for (std::size_t r = 0; r < R; ++r) Zsum[r] += Z(k, r);
105 std::vector<T> multi(M, one);
108 const std::vector<std::size_t> prods =
plane_sizes(N);
112 std::vector<T> Q(total * M, zero);
114 std::vector<int> n(R, 0);
118 for (
int v : n) npop += v;
120 const std::size_t idx =
pop_index(n, prods);
121 for (std::size_t s = 0; s < R; ++s) {
123 const std::size_t idx_1s = n[s] > 0 ? idx - prods[s] : 0;
125 for (std::size_t i = 0; i < M; ++i) {
126 const T qarv = Q[idx_1s * M + i];
127 res.
CN(i, s) = L(i, s) * (multi[i] + qarv);
128 ctot += res.
CN(i, s);
132 if (ctot == zero && n[s] > 0)
throw NumericError(
ErrorKind{
"Degenerate"},
"pfqn_mva: zero total residence time");
134 for (std::size_t i = 0; i < M; ++i) {
135 res.
QN(i, s) = res.
XN[s] * res.
CN(i, s);
136 Q[idx * M + i] += res.
QN(i, s);
142 for (
long r =
static_cast<long>(R) - 1; r >= 0; --r)
148 bool prefixFull =
true;
149 for (
long r = 0; r < last_nnz; ++r)
154 bool suffixEmpty =
true;
155 for (std::size_t r =
static_cast<std::size_t
>(last_nnz) + 1; r < R; ++r)
160 if (prefixFull && suffixEmpty) {
161 const T& x = res.
XN[
static_cast<std::size_t
>(last_nnz)];
162 if (x == zero)
throw NumericError(
ErrorKind{
"Degenerate"},
"pfqn_mva: zero throughput on the G path");
170 for (std::size_t i = 0; i < M; ++i)
171 for (std::size_t r = 0; r < R; ++r) res.
UN(i, r) = res.
XN[r] * L(i, r);
208 const std::vector<int>& mi,
const Matrix<T>& IL) {
209 const std::size_t M = L.
rows();
210 const std::size_t R = N.size();
212 throw InputError(
"pfqn_mva_ilock: demand matrix and population vector disagree on the class count");
213 if (!mi.empty() && mi.size() != M)
214 throw InputError(
"pfqn_mva_ilock: multiplicity vector has the wrong length");
220 res.
XN.assign(R, zero);
227 bool anyPositive =
false;
229 if (v < 0)
throw InputError(
"pfqn_mva_ilock: negative population");
230 if (v > 0) anyPositive =
true;
232 if (!anyPositive || M == 0)
return res;
234 std::vector<T> Zsum(R, zero);
236 if (Z.
cols() != R)
throw InputError(
"pfqn_mva_ilock: Z and N disagree on the class count");
237 for (std::size_t k = 0; k < Z.
rows(); ++k)
238 for (std::size_t r = 0; r < R; ++r) Zsum[r] += Z(k, r);
241 std::vector<T> multi(M, one);
245 throw InputError(
"pfqn_mva_ilock: an interlock matrix is required; use pfqn_mva for the standard arrival theorem");
248 if (IL.
rows() != R || IL.
cols() != R)
249 throw InputError(
"pfqn_mva_ilock: the interlock matrix must be nclasses x nclasses");
251 for (std::size_t r = 0; r < R; ++r)
252 for (std::size_t s = 0; s < R; ++s) {
253 T w = (r == s) ? one : T(one - IL(r, s));
254 if (w < zero) w = zero;
255 if (w > one) w = one;
260 const std::vector<std::size_t> prods =
plane_sizes(N);
264 std::vector<T> Q(total * M, zero);
266 std::vector<T> Qc(total * M * R, zero);
268 std::vector<T> Uc(total * M * R, zero);
270 std::vector<int> n(R, 0);
274 for (
int v : n) npop += v;
276 const std::size_t idx =
pop_index(n, prods);
280 Matrix<T> qfull(M, R, zero), qdisc(M, R, zero);
281 for (std::size_t s = 0; s < R; ++s) {
283 const std::size_t idx_1s = n[s] > 0 ? idx - prods[s] : 0;
284 for (std::size_t i = 0; i < M; ++i)
285 for (std::size_t r = 0; r < R; ++r) {
286 const T qc = Qc[(idx_1s * M + i) * R + r];
287 const T inSvc = Uc[(idx_1s * M + i) * R + r];
288 const T wait = T(ILw(s, r) * qc - inSvc);
290 qdisc(i, s) += T((wait > zero ? wait : zero) + ILw(s, r) * inSvc);
298 auto level = [&](
const T& theta, std::vector<T>& X,
Matrix<T>& C) ->
bool {
299 for (std::size_t s = 0; s < R; ++s) {
301 for (std::size_t i = 0; i < M; ++i) {
302 C(i, s) = L(i, s) * (multi[i] + qfull(i, s) - theta * (qfull(i, s) - qdisc(i, s)));
309 for (std::size_t i = 0; i < M; ++i) {
311 for (std::size_t s = 0; s < R; ++s) u += X[s] * L(i, s);
317 std::vector<T> Xl(R, zero);
319 if (!level(one, Xl, Cl)) {
320 double lo = 0.0, hi = 1.0;
321 for (
int it = 0; it < 60; ++it) {
322 const double mid = 0.5 * (lo + hi);
328 for (std::size_t s = 0; s < R; ++s) {
330 for (std::size_t i = 0; i < M; ++i) {
331 res.
CN(i, s) = Cl(i, s);
332 res.
QN(i, s) = res.
XN[s] * res.
CN(i, s);
333 Q[idx * M + i] += res.
QN(i, s);
334 Qc[(idx * M + i) * R + s] = res.
QN(i, s);
335 Uc[(idx * M + i) * R + s] = res.
XN[s] * L(i, s);
345 for (std::size_t i = 0; i < M; ++i)
346 for (std::size_t r = 0; r < R; ++r) res.
UN(i, r) = res.
XN[r] * L(i, r);
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_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.