5#ifndef LINE_API_PFQN_MVAC_H
6#define LINE_API_PFQN_MVAC_H
91 std::size_t J1 = 0, J = 0, D = 0, S = 0;
94 std::vector<std::size_t> posr;
95 std::vector<std::size_t> grpOfClass;
96 std::vector<std::size_t> gorder;
97 std::vector<std::vector<int>> Vlist;
98 std::vector<int> vsum;
99 std::vector<std::size_t> off, cnt;
104MvacSetup<T> mvac_setup(
const Matrix<T>& A,
const std::vector<int>& N, std::size_t J1,
105 std::size_t J, std::size_t K) {
112 for (std::size_t r = 0; r < N.size(); ++r)
113 if (N[r] > 0) st.posr.push_back(r);
116 std::vector<std::vector<T>> Adist;
117 st.grpOfClass.assign(st.posr.size(), 0);
118 for (std::size_t p = 0; p < st.posr.size(); ++p) {
119 std::vector<T> colv(J, zero);
120 for (std::size_t j = 0; j < J; ++j) colv[j] = A(j, st.posr[p]);
121 std::size_t g = Adist.size();
122 for (std::size_t q = 0; q < Adist.size(); ++q)
123 if (Adist[q] == colv) {
127 if (g == Adist.size()) Adist.push_back(colv);
128 st.grpOfClass[p] = g;
130 const std::size_t Dall = Adist.size();
132 std::vector<bool> visitsIS(Dall,
false);
133 for (std::size_t g = 0; g < Dall; ++g)
134 for (std::size_t j = J1; j < J; ++j)
135 if (Adist[g][j] > zero) {
139 for (std::size_t g = 0; g < Dall; ++g)
140 if (visitsIS[g]) st.gorder.push_back(g);
141 for (std::size_t g = 0; g < Dall; ++g)
142 if (!visitsIS[g]) st.gorder.push_back(g);
145 for (std::size_t g = 0; g < Dall; ++g)
146 if (!visitsIS[g]) ++st.S;
149 std::vector<std::size_t> mult(st.D, 0);
150 for (std::size_t g = 0; g < st.D; ++g)
151 for (std::size_t p = 0; p < st.posr.size(); ++p)
152 if (st.grpOfClass[p] == st.gorder[g])
153 mult[g] +=
static_cast<std::size_t
>(N[st.posr[p]]);
154 std::vector<std::size_t> chainGroup(K, 0);
155 for (std::size_t g = 0; g < st.D; ++g) chainGroup[K - st.D + g] = g;
157 for (std::size_t g = 0; g < st.D; ++g)
158 for (std::size_t cc = 0; cc + 1 < mult[g]; ++cc) chainGroup[p++] = g;
161 for (std::size_t k = 0; k < K; ++k)
162 for (std::size_t j = 0; j < J; ++j) st.a(j, k) = Adist[st.gorder[chainGroup[k]]][j];
165 st.off.assign(K + 1, 0);
166 st.cnt.assign(K + 1, 0);
167 for (std::size_t t = 0; t <= K; ++t) {
168 const std::vector<std::vector<int>> Vt =
170 st.off[t] = st.Vlist.size();
171 st.cnt[t] = Vt.size();
172 for (std::size_t i = 0; i < Vt.size(); ++i) {
173 st.Vlist.push_back(Vt[i]);
174 st.vsum.push_back(
static_cast<int>(t));
177 const std::size_t nv = st.Vlist.size();
178 st.succ = Matrix<long>(nv, J, -1);
179 for (std::size_t vi = 0; vi < nv; ++vi) {
180 const int t = st.vsum[vi];
181 if (
static_cast<std::size_t
>(t) + 1 > K)
continue;
182 for (std::size_t j = 0; j < J; ++j) {
183 std::vector<int> w = st.Vlist[vi];
185 for (std::size_t q = 0; q < st.cnt[t + 1]; ++q)
186 if (st.Vlist[st.off[t + 1] + q] == w) {
187 st.succ(vi, j) =
static_cast<long>(st.off[t + 1] + q);
197void mvac_part1(std::size_t k0,
const MvacSetup<T>& st, std::vector<Matrix<T>>& Lall,
198 std::vector<Matrix<T>>& Ljkall, std::vector<std::vector<T>>& lamall) {
199 const T zero = num_traits<T>::from_int(0), one = num_traits<T>::from_int(1);
200 const std::size_t J = st.J, J1 = st.J1, K = st.K, nv = st.Vlist.size();
201 for (std::size_t k = k0; k <= K; ++k) {
202 const Matrix<T>& Lp = Lall[k - 1];
203 Matrix<T> Lk(J, nv, zero), Ljk(J, nv, zero);
204 std::vector<T> lamv(nv, zero);
205 for (std::size_t vi = 0; vi < nv; ++vi) {
206 if (
static_cast<std::size_t
>(st.vsum[vi]) > K - k)
continue;
208 for (std::size_t j = 0; j < J; ++j) den += st.a(j, k - 1);
209 for (std::size_t j = 0; j < J1; ++j)
210 den += (Lp(j, vi) + num_traits<T>::from_int(st.Vlist[vi][j])) * st.a(j, k - 1);
211 if (den <= zero)
throw NumericError(
"pfqn_mvac: a chain has zero total demand");
212 const T lam = one / den;
213 for (std::size_t j = 0; j < J1; ++j)
214 Ljk(j, vi) = lam * (one + Lp(j, vi) + num_traits<T>::from_int(st.Vlist[vi][j])) *
216 for (std::size_t j = J1; j < J; ++j) Ljk(j, vi) = lam * st.a(j, k - 1);
218 for (std::size_t i = 0; i < J; ++i) {
220 for (std::size_t j = 0; j < J; ++j) {
221 const long sj = st.succ(vi, j);
222 if (sj < 0)
throw NumericError(
"pfqn_mvac: multiplicity vector out of range");
223 s += Ljk(j, vi) * Lp(i,
static_cast<std::size_t
>(sj));
247 const std::size_t M = L.
rows(), R = L.
cols();
248 if (M == 0 || R == 0)
throw InputError(
"pfqn_mvac: empty demand matrix");
249 if (N.size() != R)
throw InputError(
"pfqn_mvac: L and N disagree on the class count");
251 throw InputError(
"pfqn_mvac: the think time matrix and the demand matrix disagree");
255 res.
X.assign(R, zero);
261 for (std::size_t r = 0; r < R; ++r) {
262 if (N[r] < 0)
throw InputError(
"pfqn_mvac: the population vector must be nonnegative");
263 K +=
static_cast<std::size_t
>(N[r]);
265 if (K == 0)
return res;
268 std::vector<std::size_t> ssfrIdx, isIdx;
269 for (std::size_t i = 0; i < M; ++i)
270 for (std::size_t r = 0; r < R; ++r)
271 if (L(i, r) > zero) {
272 ssfrIdx.push_back(i);
275 for (std::size_t i = 0; i < Z.
rows(); ++i)
276 for (std::size_t r = 0; r < R; ++r)
277 if (Z(i, r) > zero) {
281 const std::size_t J1 = ssfrIdx.size(), J = J1 + isIdx.size();
282 if (J == 0)
throw InputError(
"pfqn_mvac: all service demands are zero, the throughput is unbounded");
285 for (std::size_t j = 0; j < J1; ++j)
286 for (std::size_t r = 0; r < R; ++r) A(j, r) = L(ssfrIdx[j], r);
287 for (std::size_t j = J1; j < J; ++j)
288 for (std::size_t r = 0; r < R; ++r) A(j, r) = Z(isIdx[j - J1], r);
290 detail::MvacSetup<T> st = detail::mvac_setup(A, N, J1, J, K);
291 const std::size_t nv = st.Vlist.size();
292 const std::size_t z0 = 0;
294 std::vector<Matrix<T>> Lall(K + 1), Ljkall(K + 1);
295 std::vector<std::vector<T>> lamall(K + 1);
298 std::vector<T> lamChain(K, zero);
301 detail::mvac_part1(1, st, Lall, Ljkall, lamall);
302 lamChain[K - 1] = lamall[K][z0];
303 for (std::size_t j = 0; j < J; ++j) Lchain(j, K - 1) = Ljkall[K](j, z0);
306 const std::size_t D = st.D, S = st.S;
307 const long lmaxK = std::min(
static_cast<long>(K) - 1,
static_cast<long>(K - S));
308 if (D >= 2 && lmaxK >=
static_cast<long>(K - D + 1)) {
309 std::vector<Matrix<T>> L2prev(K + 1), L2cur(K + 1);
310 for (std::size_t k = K - D + 2; k <= K; ++k) {
312 const long lhi = std::min(
static_cast<long>(k) - 1,
static_cast<long>(K - S));
313 for (
long l =
static_cast<long>(K - D + 1); l <= lhi; ++l) {
315 for (std::size_t vi = 0; vi < nv; ++vi) {
316 if (
static_cast<std::size_t
>(st.vsum[vi]) != K - k)
continue;
317 for (std::size_t j = 0; j < J; ++j) {
318 const long sj = st.succ(vi, j);
319 if (sj < 0)
throw NumericError(
"pfqn_mvac: multiplicity vector out of range");
321 (l ==
static_cast<long>(k) - 1) ? Ljkall[k - 1] : L2prev[l];
322 for (std::size_t i = 0; i < J; ++i)
323 acc(i, vi) += Ljkall[k](j, vi) * prev(i,
static_cast<std::size_t
>(sj));
329 for (
long l =
static_cast<long>(K - D + 1); l <= lmaxK; ++l) {
330 for (std::size_t j = 0; j < J; ++j)
331 Lchain(j,
static_cast<std::size_t
>(l) - 1) = L2cur[l](j, z0);
333 for (std::size_t j = J1; j < J; ++j)
334 if (st.a(j,
static_cast<std::size_t
>(l) - 1) > zero) {
338 if (jIS == J)
throw NumericError(
"pfqn_mvac: chain has no IS center");
339 lamChain[
static_cast<std::size_t
>(l) - 1] =
340 Lchain(jIS,
static_cast<std::size_t
>(l) - 1) /
341 st.a(jIS,
static_cast<std::size_t
>(l) - 1);
349 std::vector<std::size_t>
perm(K);
350 for (std::size_t k = 0; k < K; ++k)
perm[k] = k;
351 for (std::size_t l = 1; l + 1 <= S && S >= 1 && l < S; ++l) {
352 std::swap(
perm[K - l - 1],
perm[K - 1]);
353 for (std::size_t j = 0; j < J; ++j) {
354 const T tmp = st.a(j, K - l - 1);
355 st.a(j, K - l - 1) = st.a(j, K - 1);
356 st.a(j, K - 1) = tmp;
358 detail::mvac_part1(K - l, st, Lall, Ljkall, lamall);
359 lamChain[
perm[K - 1]] = lamall[K][z0];
360 for (std::size_t j = 0; j < J; ++j) Lchain(j,
perm[K - 1]) = Ljkall[K](j, z0);
364 for (std::size_t g = 0; g < D; ++g) {
365 const std::size_t kg = K - D + g;
366 for (std::size_t p = 0; p < st.posr.size(); ++p) {
367 if (st.grpOfClass[p] != st.gorder[g])
continue;
368 const std::size_t r = st.posr[p];
370 res.
X[r] = nr * lamChain[kg];
371 for (std::size_t j = 0; j < J1; ++j) {
372 res.
Q(ssfrIdx[j], r) = nr * Lchain(j, kg);
373 res.
U(ssfrIdx[j], r) = res.
X[r] * L(ssfrIdx[j], r);
374 res.
C(ssfrIdx[j], r) = res.
Q(ssfrIdx[j], r) / res.
X[r];
379 for (std::size_t r = 0; r < R; ++r)
381 for (std::size_t i = 0; i < M; ++i) res.
C(i, r) = L(i, r);
NumericError(const std::string &what)
The exception types the port throws.
Dense matrix and non-owning view.
std::vector< std::vector< int > > multichoose_rows(int n, int k)
All n-vectors of nonnegative integers summing to k, in MATLAB multichoose(n,k) order.
MvacResult< T > pfqn_mvac(const Matrix< T > &L, const std::vector< int > &N, const Matrix< T > &Z)
MVAC: exact mean value analysis BY CHAIN of a closed multichain product-form network (Conway,...
Number-type abstraction for the templated API port.
Integer-composition enumeration shared by the CoMoM and MVAC ports.
Return value of pfqn_mvac, mirroring [XN, QN, UN, CN].
Matrix< T > C
(M x R) per-class residence time
Matrix< T > U
(M x R) per-class utilization
std::vector< T > X
(R) per-class throughput
Matrix< T > Q
(M x R) per-class queue length at the SSFR queues