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);
130 if (ctot == zero)
throw NumericError(
"pfqn_mva: zero total residence time");
132 for (std::size_t i = 0; i < M; ++i) {
133 res.
QN(i, s) = res.
XN[s] * res.
CN(i, s);
134 Q[idx * M + i] += res.
QN(i, s);
140 for (
long r =
static_cast<long>(R) - 1; r >= 0; --r)
146 bool prefixFull =
true;
147 for (
long r = 0; r < last_nnz; ++r)
152 bool suffixEmpty =
true;
153 for (std::size_t r =
static_cast<std::size_t
>(last_nnz) + 1; r < R; ++r)
158 if (prefixFull && suffixEmpty) {
159 const T& x = res.
XN[
static_cast<std::size_t
>(last_nnz)];
160 if (x == zero)
throw NumericError(
"pfqn_mva: zero throughput on the G path");
168 for (std::size_t i = 0; i < M; ++i)
169 for (std::size_t r = 0; r < R; ++r) res.
UN(i, r) = res.
XN[r] * L(i, r);
205 const std::vector<int>& mi,
const Matrix<T>& IL) {
206 const std::size_t M = L.
rows();
207 const std::size_t R = N.size();
209 throw InputError(
"pfqn_mva_ilock: demand matrix and population vector disagree on the class count");
210 if (!mi.empty() && mi.size() != M)
211 throw InputError(
"pfqn_mva_ilock: multiplicity vector has the wrong length");
217 res.
XN.assign(R, zero);
224 bool anyPositive =
false;
226 if (v < 0)
throw InputError(
"pfqn_mva_ilock: negative population");
227 if (v > 0) anyPositive =
true;
229 if (!anyPositive || M == 0)
return res;
231 std::vector<T> Zsum(R, zero);
233 if (Z.
cols() != R)
throw InputError(
"pfqn_mva_ilock: Z and N disagree on the class count");
234 for (std::size_t k = 0; k < Z.
rows(); ++k)
235 for (std::size_t r = 0; r < R; ++r) Zsum[r] += Z(k, r);
238 std::vector<T> multi(M, one);
242 throw InputError(
"pfqn_mva_ilock: an interlock matrix is required; use pfqn_mva for the standard arrival theorem");
245 if (IL.
rows() != R || IL.
cols() != R)
246 throw InputError(
"pfqn_mva_ilock: the interlock matrix must be nclasses x nclasses");
248 for (std::size_t r = 0; r < R; ++r)
249 for (std::size_t s = 0; s < R; ++s) {
250 T w = (r == s) ? one : T(one - IL(r, s));
251 if (w < zero) w = zero;
252 if (w > one) w = one;
257 const std::vector<std::size_t> prods =
plane_sizes(N);
261 std::vector<T> Q(total * M, zero);
263 std::vector<T> Qc(total * M * R, zero);
265 std::vector<T> Uc(total * M * R, zero);
267 std::vector<int> n(R, 0);
271 for (
int v : n) npop += v;
273 const std::size_t idx =
pop_index(n, prods);
274 for (std::size_t s = 0; s < R; ++s) {
276 const std::size_t idx_1s = n[s] > 0 ? idx - prods[s] : 0;
278 for (std::size_t i = 0; i < M; ++i) {
280 for (std::size_t r = 0; r < R; ++r) {
284 const T disc = ILw(s, r) * Qc[(idx_1s * M + i) * R + r];
285 const T inSvc = Uc[(idx_1s * M + i) * R + r];
286 qarv += disc > inSvc ? disc : inSvc;
288 res.
CN(i, s) = L(i, s) * (multi[i] + qarv);
289 ctot += res.
CN(i, s);
291 if (ctot == zero)
throw NumericError(
"pfqn_mva_ilock: zero total residence time");
293 for (std::size_t i = 0; i < M; ++i) {
294 res.
QN(i, s) = res.
XN[s] * res.
CN(i, s);
295 Q[idx * M + i] += res.
QN(i, s);
296 Qc[(idx * M + i) * R + s] = res.
QN(i, s);
297 Uc[(idx * M + i) * R + s] = res.
XN[s] * L(i, s);
307 for (std::size_t i = 0; i < M; ++i)
308 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.
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...
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).