5#ifndef LINE_API_PFQN_LINEARIZERMS_H
6#define LINE_API_PFQN_LINEARIZERMS_H
78void linms_forward_mva(
const Matrix<T>& L, std::size_t M, std::size_t R,
79 const std::vector<int>& N_1,
const std::vector<T>& Z,
80 const std::vector<int>& nservers,
bool allFCFS,
81 const std::vector<Matrix<T>>& Q1,
const std::vector<Matrix<T>>& P1,
82 const std::vector<std::vector<T>>& PB1, Matrix<T>& Q, Matrix<T>& W,
83 std::vector<T>& X, Matrix<T>& P, std::vector<T>& PB) {
84 const T zero = num_traits<T>::from_int(0);
85 const T one = num_traits<T>::from_int(1);
86 for (std::size_t i = 0; i < M; ++i) {
87 const T c = num_traits<T>::from_int(nservers[i]);
88 for (std::size_t r = 0; r < R; ++r) {
89 W(i, r) = L(i, r) / c;
91 if (L(i, r) == zero)
continue;
92 for (std::size_t s = 0; s < R; ++s)
93 W(i, r) += (allFCFS ? L(i, s) : L(i, r)) / c * Q1[r + 1](i, s);
101 for (
int j = 0; j <= nservers[i] - 2; ++j) {
102 const T wgt = num_traits<T>::from_int(nservers[i] - 1 - j);
103 W(i, r) += L(i, r) / c * wgt * P1[r + 1](i,
static_cast<std::size_t
>(j));
107 for (std::size_t r = 0; r < R; ++r) {
109 for (std::size_t i = 0; i < M; ++i) den += W(i, r);
113 if (den == zero)
throw NumericError(ErrorKind{
"Degenerate"},
"pfqn_linearizerms: zero total residence time");
114 X[r] = num_traits<T>::from_int(N_1[r]) / den;
116 for (std::size_t i = 0; i < M; ++i) Q(i, r) = X[r] * W(i, r);
127 for (std::size_t i = 0; i < M; ++i) {
128 const int ms = nservers[i];
129 if (ms <= 1)
continue;
130 const std::size_t msz =
static_cast<std::size_t
>(ms);
131 T A = zero, dB = zero;
132 std::vector<T> d(msz, zero);
133 for (std::size_t s = 0; s < R; ++s) {
134 const T a_s = L(i, s) * X[s];
136 for (std::size_t j = 0; j < msz; ++j)
137 d[j] += a_s * (P1[s + 1](i, j) - P1[0](i, j));
138 dB += a_s * (PB1[s + 1][i] - PB1[0][i]);
140 const T msT = num_traits<T>::from_int(ms);
143 "pfqn_linearizerms: the station offers as many busy servers as it has, so the "
144 "model is saturated and its queue-length marginals do not exist");
145 std::vector<T> alpha(msz, zero), beta(msz, zero);
147 for (std::size_t j = 1; j < msz; ++j) {
148 const T jT = num_traits<T>::from_int(
static_cast<int>(j));
149 alpha[j] = A * alpha[j - 1] / jT;
150 beta[j] = (A * beta[j - 1] + d[j - 1]) / jT;
152 const T alphaB = A * alpha[msz - 1] / (msT - A);
153 const T betaB = (A * beta[msz - 1] + dB + d[msz - 1]) / (msT - A);
154 T num = one - betaB, den = one + alphaB;
155 for (std::size_t j = 1; j < msz; ++j) {
159 for (std::size_t k = 0; k <
P.cols(); ++k)
P(i, k) = zero;
161 for (std::size_t j = 1; j < msz; ++j)
P(i, j) = alpha[j] *
P(i, 0) + beta[j];
162 PB[i] = alphaB *
P(i, 0) + betaB;
176void linms_estimate(std::size_t M, std::size_t R,
const std::vector<int>& N_1,
const Matrix<T>& Q,
177 const Matrix<T>& P,
const std::vector<T>& PB,
178 const std::vector<Matrix<T>>& Delta,
const std::vector<Matrix<T>>& DeltaP,
179 const Matrix<T>& DeltaPB,
const std::vector<int>& nservers,
180 std::vector<Matrix<T>>& Q1, std::vector<Matrix<T>>& P1,
181 std::vector<std::vector<T>>& PB1) {
182 const T zero = num_traits<T>::from_int(0);
183 Q1.assign(R + 1, Matrix<T>(M, R, zero));
184 P1.assign(R + 1, Matrix<T>(M,
P.cols(), zero));
185 PB1.assign(R + 1, std::vector<T>(M, zero));
186 for (std::size_t i = 0; i < M; ++i) {
187 if (nservers[i] > 1) {
188 for (std::size_t j = 0; j <
P.cols(); ++j) {
189 P1[0](i, j) =
P(i, j);
190 for (std::size_t s = 1; s <= R; ++s)
191 P1[s](i, j) =
P(i, j) + DeltaP[s - 1](i, j);
194 for (std::size_t s = 1; s <= R; ++s) PB1[s][i] = PB[i] + DeltaPB(i, s - 1);
196 for (std::size_t r = 0; r < R; ++r)
197 for (std::size_t s = 1; s <= R; ++s) {
198 const std::vector<int> Ns =
oner(N_1, s);
199 if (N_1[r] <= 0 || Ns[r] <= 0) {
202 Q1[s](i, r) = num_traits<T>::from_int(Ns[r]) *
203 (Q(i, r) / num_traits<T>::from_int(N_1[r]) + Delta[r](i, s - 1));
210int linms_core(
const Matrix<T>& L, std::size_t M, std::size_t R,
const std::vector<int>& N_1,
211 const std::vector<T>& Z,
const std::vector<int>& nservers,
bool allFCFS,
212 Matrix<T>& Q, Matrix<T>& P, std::vector<T>& PB,
213 const std::vector<Matrix<T>>& Delta,
const std::vector<Matrix<T>>& DeltaP,
214 const Matrix<T>& DeltaPB,
double tol,
int maxiter, Matrix<T>& W,
217 std::vector<Matrix<T>> Q1, P1;
218 std::vector<std::vector<T>> PB1;
221 const Matrix<T> Qlast = Q;
222 linms_estimate(M, R, N_1, Q, P, PB, Delta, DeltaP, DeltaPB, nservers, Q1, P1, PB1);
223 linms_forward_mva(L, M, R, N_1, Z, nservers, allFCFS, Q1, P1, PB1, Q, W, X, P, PB);
225 if (e < tol || iter > maxiter)
break;
232void linms_init_marginals(std::size_t M, std::size_t R,
const std::vector<int>& nservers,
233 const std::vector<Matrix<T>>& Q,
const std::vector<int>& N,
234 std::vector<Matrix<T>>& P, std::vector<std::vector<T>>& PB) {
235 const T zero = num_traits<T>::from_int(0);
236 const T one = num_traits<T>::from_int(1);
237 for (std::size_t s = 0; s <= R; ++s) {
238 const std::vector<int> N_1 =
oner(N, s);
240 for (
int v : N_1) pop += v;
244 for (std::size_t i = 0; i < M; ++i) {
245 if (nservers[i] <= 1)
continue;
246 for (std::size_t k = 0; k <
P[s].cols(); ++k) P[s](i, k) = zero;
252 const T popT = num_traits<T>::from_int(pop);
253 const T pop1T = num_traits<T>::from_int(pop + 1);
254 for (std::size_t i = 0; i < M; ++i) {
255 if (nservers[i] <= 1)
continue;
257 for (std::size_t r = 0; r < R; ++r) qsum += Q[s](i, r);
258 const T two = num_traits<T>::from_int(2);
259 for (
int j = 1; j <= nservers[i] - 1; ++j)
260 P[s](i,
static_cast<std::size_t
>(j)) = two * qsum / (popT * pop1T);
265 if (pop > nservers[i] - 1) {
266 const T slack = num_traits<T>::from_int(pop + 1 - nservers[i]);
267 PB[s][i] = two * qsum / slack / (popT * pop1T);
271 T p0 = one - PB[s][i];
272 for (
int j = 1; j <= nservers[i] - 1; ++j) p0 -= P[s](i,
static_cast<std::size_t
>(j));
297 const Matrix<T>& Z,
const std::vector<int>& nservers,
298 const std::vector<SchedStrategy>& type,
double tol,
302 const std::size_t M = L.
rows();
303 const std::size_t R = N.size();
306 "pfqn_linearizerms: demand matrix and population vector disagree on the class count");
307 if (nservers.size() != M)
308 throw InputError(
"pfqn_linearizerms: server-count vector has the wrong station count");
309 for (
int c : nservers)
310 if (c < 1)
throw InputError(
"pfqn_linearizerms: server count below one");
311 if (!type.empty() && type.size() != M)
312 throw InputError(
"pfqn_linearizerms: scheduling vector has the wrong station count");
313 if (tol <= 0)
throw InputError(
"pfqn_linearizerms: tolerance must be positive");
315 if (v < 0)
throw InputError(
"pfqn_linearizerms: negative population");
318 const std::vector<T> Zs =
sum_rows(Z, R);
324 res.
C.assign(R, zero);
325 res.
X.assign(R, zero);
327 if (M == 0)
return res;
329 bool allFCFS = !type.empty();
330 for (std::size_t i = 0; i < type.size(); ++i)
333 std::size_t cmax = 1;
334 for (
int c : nservers) cmax =
static_cast<std::size_t
>(c) > cmax ? c : cmax;
337 for (std::size_t r = 0; r < R; ++r) Zm(0, r) = Zs[r];
339 std::vector<Matrix<T>> Q(R + 1,
Matrix<T>(M, R, zero));
340 for (std::size_t s = 0; s <= R; ++s) {
341 const std::vector<int> N_1 =
oner(N, s);
342 bool feasible =
true;
344 if (v < 0) feasible =
false;
345 if (!feasible)
continue;
346 std::vector<T> Nt(R, zero);
347 for (std::size_t r = 0; r < R; ++r) Nt[r] = num_traits<T>::from_int(N_1[r]);
353 std::vector<Matrix<T>> P(R + 1,
Matrix<T>(M, cmax, zero));
354 std::vector<std::vector<T>> PB(R + 1, std::vector<T>(M, zero));
355 detail::linms_init_marginals(M, R, nservers, Q, N, P, PB);
357 std::vector<Matrix<T>> Delta(R,
Matrix<T>(M, R, zero));
363 std::vector<Matrix<T>> DeltaP(R,
Matrix<T>(M, cmax, zero));
366 std::vector<T> X(R, zero);
368 for (
int I = 0; I < 2; ++I) {
369 for (std::size_t s = 0; s <= R; ++s) {
370 const std::vector<int> N_1 =
oner(N, s);
371 bool feasible =
true;
373 if (v < 0) feasible =
false;
374 if (!feasible)
continue;
376 detail::linms_core(L, M, R, N_1, Zs, nservers, allFCFS, Q[s], P[s], PB[s], Delta,
377 DeltaP, DeltaPB, tol, maxiter - res.
totiter, W, X);
379 for (std::size_t i = 0; i < M; ++i)
380 for (std::size_t r = 0; r < R; ++r) {
382 for (std::size_t s = 0; s < R; ++s) Delta[r](i, s) = zero;
386 for (std::size_t s = 1; s <= R; ++s) {
387 const std::vector<int> Ns =
oner(N, s);
392 Delta[r](i, s - 1) = -Q[0](i, r) / nrT;
397 for (std::size_t i = 0; i < M; ++i) {
398 if (nservers[i] <= 1)
continue;
399 for (std::size_t s = 1; s <= R; ++s) {
400 for (std::size_t j = 0; j < static_cast<std::size_t>(nservers[i]); ++j)
401 DeltaP[s - 1](i, j) = P[s](i, j) - P[0](i, j);
402 DeltaPB(i, s - 1) = PB[s][i] - PB[0][i];
407 res.
totiter += detail::linms_core(L, M, R, N, Zs, nservers, allFCFS, Q[0], P[0], PB[0], Delta,
408 DeltaP, DeltaPB, tol, maxiter - res.
totiter, W, X);
412 for (std::size_t i = 0; i < M; ++i)
413 for (std::size_t r = 0; r < R; ++r)
414 res.
U(i, r) = nservers[i] == 1
417 for (std::size_t r = 0; r < R; ++r)
425 const Matrix<T>& Z,
const std::vector<int>& nservers) {
426 return pfqn_linearizerms(L, N, Z, nservers, std::vector<SchedStrategy>(), 1e-8, 1000,
NumericError(const std::string &what)
The exception types the port throws.
Dense matrix and non-owning view.
std::vector< T > sum_rows(const Matrix< T > &Z, std::size_t R)
Sum the rows of a think-time matrix into a length-R vector, the sum(Z,1) that every AMVA entry point ...
std::vector< int > oner(const std::vector< int > &N, std::size_t r)
matlab/src/util/oner.m: decrement position r of N, with r given 1-based and r == 0 meaning "leave N a...
AmvaResult< T > pfqn_bs(const Matrix< T > &L, const std::vector< T > &N, const std::vector< T > &Z, const std::vector< AmvaSched > &type, double tol=1e-6, std::size_t maxiter=1000, const Matrix< T > &QN0=Matrix< T >())
Bard-Schweitzer approximate MVA.
LinearizerResult< T > pfqn_linearizerms(const Matrix< T > &L, const std::vector< int > &N, const Matrix< T > &Z, const std::vector< int > &nservers, const std::vector< SchedStrategy > &type, double tol, int maxiter, const Matrix< T > &QN0)
Multiserver Linearizer (Krzesinski's Linearizer as described in Conway 1989, with De Souza e Silva an...
double enorm_diff(const Matrix< T > &A, const Matrix< T > &B)
Frobenius norm of the difference of two equally shaped matrices, AS A DOUBLE.
Conservation laws of a layered queueing network, enumerated from its structure.
Number-type abstraction for the templated API port.
Scaffolding shared by the approximate-MVA family.
Bard-Schweitzer approximate MVA.
Extended generalized fixed-point Linearizer (De Souza e Silva and Muntz's generalization of Chandy an...
Matrix< T > QN
(M x R) queue length
Return value of the Linearizer family, mirroring [Q,U,W,C,X,totiter].
Matrix< T > U
(M x R) utilization
std::vector< T > C
(R) cycle time, N_r/X_r - Z_r
std::vector< T > X
(R) per-class throughput
Matrix< T > W
(M x R) per-station residence time
Matrix< T > Q
(M x R) mean queue length
int totiter
total inner iterations across all Core calls