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(
"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;
178void linms_estimate(std::size_t M, std::size_t R,
const std::vector<int>& N_1,
const Matrix<T>& Q,
179 const Matrix<T>& P,
const std::vector<T>& PB,
180 const std::vector<Matrix<T>>& Delta,
const std::vector<Matrix<T>>& DeltaP,
181 const Matrix<T>& DeltaPB,
const std::vector<int>& nservers,
182 std::vector<Matrix<T>>& Q1, std::vector<Matrix<T>>& P1,
183 std::vector<std::vector<T>>& PB1) {
184 const T zero = num_traits<T>::from_int(0);
185 Q1.assign(R + 1, Matrix<T>(M, R, zero));
186 P1.assign(R + 1, Matrix<T>(M, P.cols(), zero));
187 PB1.assign(R + 1, std::vector<T>(M, zero));
188 for (std::size_t i = 0; i < M; ++i) {
189 if (nservers[i] > 1) {
190 for (std::size_t j = 0; j < P.cols(); ++j) {
191 P1[0](i, j) = P(i, j);
192 for (std::size_t s = 1; s <= R; ++s)
193 P1[s](i, j) = P(i, j) + DeltaP[s - 1](i, j);
196 for (std::size_t s = 1; s <= R; ++s) PB1[s][i] = PB[i] + DeltaPB(i, s - 1);
198 for (std::size_t r = 0; r < R; ++r)
199 for (std::size_t s = 1; s <= R; ++s) {
200 const std::vector<int> Ns =
oner(N_1, s);
201 if (N_1[r] <= 0 || Ns[r] <= 0) {
204 Q1[s](i, r) = num_traits<T>::from_int(Ns[r]) *
205 (Q(i, r) / num_traits<T>::from_int(N_1[r]) + Delta[r](i, s - 1));
212int linms_core(
const Matrix<T>& L, std::size_t M, std::size_t R,
const std::vector<int>& N_1,
213 const std::vector<T>& Z,
const std::vector<int>& nservers,
bool allFCFS,
214 Matrix<T>& Q, Matrix<T>& P, std::vector<T>& PB,
215 const std::vector<Matrix<T>>& Delta,
const std::vector<Matrix<T>>& DeltaP,
216 const Matrix<T>& DeltaPB,
double tol,
int maxiter, Matrix<T>& W,
219 std::vector<Matrix<T>> Q1, P1;
220 std::vector<std::vector<T>> PB1;
223 const Matrix<T> Qlast = Q;
224 linms_estimate(M, R, N_1, Q, P, PB, Delta, DeltaP, DeltaPB, nservers, Q1, P1, PB1);
225 linms_forward_mva(L, M, R, N_1, Z, nservers, allFCFS, Q1, P1, PB1, Q, W, X, P, PB);
227 if (e < tol || iter > maxiter)
break;
234void linms_init_marginals(std::size_t M, std::size_t R,
const std::vector<int>& nservers,
235 const std::vector<Matrix<T>>& Q,
const std::vector<int>& N,
236 std::vector<Matrix<T>>& P, std::vector<std::vector<T>>& PB) {
237 const T zero = num_traits<T>::from_int(0);
238 const T one = num_traits<T>::from_int(1);
239 for (std::size_t s = 0; s <= R; ++s) {
240 const std::vector<int> N_1 =
oner(N, s);
242 for (
int v : N_1) pop += v;
246 for (std::size_t i = 0; i < M; ++i) {
247 if (nservers[i] <= 1)
continue;
248 for (std::size_t k = 0; k < P[s].cols(); ++k) P[s](i, k) = zero;
254 const T popT = num_traits<T>::from_int(pop);
255 const T pop1T = num_traits<T>::from_int(pop + 1);
256 for (std::size_t i = 0; i < M; ++i) {
257 if (nservers[i] <= 1)
continue;
259 for (std::size_t r = 0; r < R; ++r) qsum += Q[s](i, r);
260 const T two = num_traits<T>::from_int(2);
261 for (
int j = 1; j <= nservers[i] - 1; ++j)
262 P[s](i,
static_cast<std::size_t
>(j)) = two * qsum / (popT * pop1T);
267 if (pop > nservers[i] - 1) {
268 const T slack = num_traits<T>::from_int(pop + 1 - nservers[i]);
269 PB[s][i] = two * qsum / slack / (popT * pop1T);
273 T p0 = one - PB[s][i];
274 for (
int j = 1; j <= nservers[i] - 1; ++j) p0 -= P[s](i,
static_cast<std::size_t
>(j));
299 const Matrix<T>& Z,
const std::vector<int>& nservers,
300 const std::vector<SchedStrategy>& type,
double tol,
304 const std::size_t M = L.
rows();
305 const std::size_t R = N.size();
308 "pfqn_linearizerms: demand matrix and population vector disagree on the class count");
309 if (nservers.size() != M)
310 throw InputError(
"pfqn_linearizerms: server-count vector has the wrong station count");
311 for (
int c : nservers)
312 if (c < 1)
throw InputError(
"pfqn_linearizerms: server count below one");
313 if (!type.empty() && type.size() != M)
314 throw InputError(
"pfqn_linearizerms: scheduling vector has the wrong station count");
315 if (tol <= 0)
throw InputError(
"pfqn_linearizerms: tolerance must be positive");
317 if (v < 0)
throw InputError(
"pfqn_linearizerms: negative population");
320 const std::vector<T> Zs =
sum_rows(Z, R);
326 res.
C.assign(R, zero);
327 res.
X.assign(R, zero);
329 if (M == 0)
return res;
331 bool allFCFS = !type.empty();
332 for (std::size_t i = 0; i < type.size(); ++i)
335 std::size_t cmax = 1;
336 for (
int c : nservers) cmax =
static_cast<std::size_t
>(c) > cmax ? c : cmax;
339 for (std::size_t r = 0; r < R; ++r) Zm(0, r) = Zs[r];
341 std::vector<Matrix<T>> Q(R + 1,
Matrix<T>(M, R, zero));
342 for (std::size_t s = 0; s <= R; ++s) {
343 const std::vector<int> N_1 =
oner(N, s);
344 bool feasible =
true;
346 if (v < 0) feasible =
false;
347 if (!feasible)
continue;
348 std::vector<T> Nt(R, zero);
349 for (std::size_t r = 0; r < R; ++r) Nt[r] = num_traits<T>::from_int(N_1[r]);
355 std::vector<Matrix<T>> P(R + 1,
Matrix<T>(M, cmax, zero));
356 std::vector<std::vector<T>> PB(R + 1, std::vector<T>(M, zero));
357 detail::linms_init_marginals(M, R, nservers, Q, N, P, PB);
359 std::vector<Matrix<T>> Delta(R,
Matrix<T>(M, R, zero));
365 std::vector<Matrix<T>> DeltaP(R,
Matrix<T>(M, cmax, zero));
368 std::vector<T> X(R, zero);
370 for (
int I = 0; I < 2; ++I) {
371 for (std::size_t s = 0; s <= R; ++s) {
372 const std::vector<int> N_1 =
oner(N, s);
373 bool feasible =
true;
375 if (v < 0) feasible =
false;
376 if (!feasible)
continue;
378 detail::linms_core(L, M, R, N_1, Zs, nservers, allFCFS, Q[s], P[s], PB[s], Delta,
379 DeltaP, DeltaPB, tol, maxiter - res.
totiter, W, X);
381 for (std::size_t i = 0; i < M; ++i)
382 for (std::size_t r = 0; r < R; ++r) {
384 for (std::size_t s = 0; s < R; ++s) Delta[r](i, s) = zero;
388 for (std::size_t s = 1; s <= R; ++s) {
389 const std::vector<int> Ns =
oner(N, s);
394 Delta[r](i, s - 1) = -Q[0](i, r) / nrT;
399 for (std::size_t i = 0; i < M; ++i) {
400 if (nservers[i] <= 1)
continue;
401 for (std::size_t s = 1; s <= R; ++s) {
402 for (std::size_t j = 0; j < static_cast<std::size_t>(nservers[i]); ++j)
403 DeltaP[s - 1](i, j) = P[s](i, j) - P[0](i, j);
404 DeltaPB(i, s - 1) = PB[s][i] - PB[0][i];
409 res.
totiter += detail::linms_core(L, M, R, N, Zs, nservers, allFCFS, Q[0], P[0], PB[0], Delta,
410 DeltaP, DeltaPB, tol, maxiter - res.
totiter, W, X);
414 for (std::size_t i = 0; i < M; ++i)
415 for (std::size_t r = 0; r < R; ++r)
416 res.
U(i, r) = nservers[i] == 1
419 for (std::size_t r = 0; r < R; ++r)
427 const Matrix<T>& Z,
const std::vector<int>& nservers) {
428 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.
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