5#ifndef LINE_API_PFQN_CONWAYMS_H
6#define LINE_API_PFQN_CONWAYMS_H
61void conway_estimate(std::size_t M, std::size_t R,
const std::vector<int>& N_1,
62 const Matrix<T>& Q,
const std::vector<Matrix<T>>& Delta,
const Matrix<T>& W,
63 std::vector<Matrix<T>>& Q1, Matrix<T>& T_1) {
64 const T zero = num_traits<T>::from_int(0);
65 for (std::size_t i = 0; i < M; ++i)
66 for (std::size_t r = 0; r < R; ++r)
67 for (std::size_t s = 1; s <= R; ++s) {
68 const std::vector<int> Ns =
oner(N_1, s);
69 if (N_1[r] <= 0 || Ns[r] <= 0) {
72 Q1[s](i, r) = num_traits<T>::from_int(Ns[r]) *
73 (Q(i, r) / num_traits<T>::from_int(N_1[r]) + Delta[r](i, s - 1));
82 for (std::size_t s = 0; s < R; ++s)
83 for (std::size_t r = 0; r < R; ++r) T_1(s, r) = zero;
84 for (std::size_t r = 1; r <= R; ++r) {
85 const std::vector<int> Nr =
oner(N_1, r);
86 for (std::size_t s = 0; s < R; ++s) {
87 if (N_1[s] <= 0 || Nr[s] <= 0)
continue;
90 for (std::size_t i = 0; i < M; ++i) {
94 num += num_traits<T>::from_int(Nr[s]) *
95 (Q(i, s) / num_traits<T>::from_int(N_1[s]) + Delta[s](i, r - 1));
102 const T t1 = num / den;
103 T_1(s, r - 1) = (t1 < zero) ? zero : t1;
114void conway_conditional_rates(
const Matrix<T>& L, std::size_t M, std::size_t R,
115 const std::vector<int>& N_1,
const std::vector<int>& nservers,
116 const Matrix<T>& T_1, std::vector<T>& XRflat,
117 std::vector<T>& XEflat) {
118 const T zero = num_traits<T>::from_int(0);
119 XRflat.assign(M * R, zero);
120 XEflat.assign(M * R * R, zero);
121 for (std::size_t r = 0; r < R; ++r) {
123 Matrix<T> F(M, R, zero);
124 for (std::size_t i = 0; i < M; ++i) {
126 for (std::size_t c = 0; c < R; ++c) den += L(i, c) * T_1(c, r);
127 if (den == zero)
continue;
128 for (std::size_t c = 0; c < R; ++c) F(i, c) = T_1(c, r) * L(i, c) / den;
130 const std::vector<int> Nr =
oner(N_1, r + 1);
131 for (std::size_t i = 0; i < M; ++i) {
132 if (nservers[i] <= 1)
continue;
133 std::vector<int> n(R, 0);
135 T Csum = zero, XRacc = zero;
136 std::vector<T> Cx(R, zero), XEacc(R, zero);
140 for (std::size_t c = 0; c < R; ++c)
141 if (n[c] > Nr[c]) inB =
false;
145 for (std::size_t c = 0; c < R; ++c)
146 if (n[c] > 0) Ai *=
num_pow_int(F(i, c),
static_cast<unsigned>(n[c]));
150 for (std::size_t c = 0; c < R; ++c) {
151 if (n[c] == 0)
continue;
154 "pfqn_conwayms: a chain occupies a server at a station where it "
155 "has zero demand, so its service rate is unbounded");
156 rate += num_traits<T>::from_int(n[c]) / L(i, c);
161 for (std::size_t c = 0; c < R; ++c)
164 XEacc[c] += Ai / rate;
168 more = next_composition(n);
170 if (Csum != zero) XRflat[i * R + r] = XRacc / Csum;
171 for (std::size_t c = 0; c < R; ++c)
172 if (Cx[c] != zero) XEflat[(i * R + r) * R + c] = XEacc[c] / Cx[c];
178void conway_forward_mva(
const Matrix<T>& L, std::size_t M, std::size_t R,
179 const std::vector<int>& N_1,
const std::vector<T>& Z,
180 const std::vector<int>& nservers,
bool allFCFS,
181 const std::vector<Matrix<T>>& Q1,
const Matrix<T>& P_1,
182 const std::vector<T>& PB_1,
const Matrix<T>& T_1, Matrix<T>& Q,
183 Matrix<T>& W, std::vector<T>& X, Matrix<T>& P, std::vector<T>& PB) {
184 const T zero = num_traits<T>::from_int(0);
185 const T one = num_traits<T>::from_int(1);
186 std::vector<T> XR, XE;
187 conway_conditional_rates(L, M, R, N_1, nservers, T_1, XR, XE);
189 for (std::size_t i = 0; i < M; ++i)
190 for (std::size_t r = 0; r < R; ++r) {
191 if (nservers[i] == 1) {
193 for (std::size_t c = 0; c < R; ++c)
194 W(i, r) += (allFCFS ? L(i, c) : L(i, r)) * Q1[r + 1](i, c);
196 W(i, r) = L(i, r) + PB_1[i] * XR[i * R + r];
197 for (std::size_t c = 0; c < R; ++c)
198 W(i, r) += XE[(i * R + r) * R + c] * (Q1[r + 1](i, c) - L(i, c) * T_1(c, r));
201 for (std::size_t r = 0; r < R; ++r) {
203 for (std::size_t i = 0; i < M; ++i) den += W(i, r);
207 if (den == zero)
throw NumericError(
"pfqn_conwayms: zero total residence time");
208 X[r] = num_traits<T>::from_int(N_1[r]) / den;
210 for (std::size_t i = 0; i < M; ++i) Q(i, r) = X[r] * W(i, r);
220 for (std::size_t i = 0; i < M; ++i) {
221 if (nservers[i] <= 1)
continue;
222 const std::size_t ms =
static_cast<std::size_t
>(nservers[i]);
224 for (std::size_t s = 0; s < R; ++s) A += L(i, s) * X[s];
225 for (std::size_t k = 0; k < P.cols(); ++k) P(i, k) = zero;
226 if (!(A < num_traits<T>::from_int(nservers[i]))) {
233 std::vector<T> alpha(ms, zero);
236 for (std::size_t j = 1; j < ms; ++j) {
237 alpha[j] = A * alpha[j - 1] / num_traits<T>::from_int(
static_cast<int>(j));
238 sumAlpha += alpha[j];
240 const T alphaB = A * alpha[ms - 1] / (num_traits<T>::from_int(nservers[i]) - A);
241 const T p0 = one / (one + sumAlpha + alphaB);
243 for (std::size_t j = 1; j < ms; ++j) P(i, j) = alpha[j] * p0;
249int conway_core(
const Matrix<T>& L, std::size_t M, std::size_t R,
const std::vector<int>& N_1,
250 const std::vector<T>& Z,
const std::vector<int>& nservers,
bool allFCFS,
251 Matrix<T>& Q, Matrix<T>& P, std::vector<T>& PB,
252 const std::vector<Matrix<T>>& Delta,
double tol,
int maxiter, Matrix<T>& W,
254 const T zero = num_traits<T>::from_int(0);
257 bool haveWlast =
false;
260 const Matrix<T> Qlast = Q;
261 const Matrix<T> P_1 = P;
262 const std::vector<T> PB_1 = PB;
263 std::vector<Matrix<T>> Q1(R + 1, Matrix<T>(M, R, zero));
264 Matrix<T> T_1(R, R, zero);
265 conway_estimate(M, R, N_1, Q, Delta, W, Q1, T_1);
266 conway_forward_mva(L, M, R, N_1, Z, nservers, allFCFS, Q1, P_1, PB_1, T_1, Q, W, X, P, PB);
271 double e = std::numeric_limits<double>::infinity();
275 const bool done = e < tol || iter > maxiter;
301 const Matrix<T>& Z,
const std::vector<int>& nservers,
302 const std::vector<SchedStrategy>& type,
double tol,
int maxiter,
305 "pfqn_conwayms requires transcendental arithmetic");
307 const std::size_t M = L.
rows();
308 const std::size_t R = N.size();
311 "pfqn_conwayms: demand matrix and population vector disagree on the class count");
312 if (nservers.size() != M)
313 throw InputError(
"pfqn_conwayms: server-count vector has the wrong station count");
314 for (
int c : nservers)
315 if (c < 1)
throw InputError(
"pfqn_conwayms: server count below one");
316 if (!type.empty() && type.size() != M)
317 throw InputError(
"pfqn_conwayms: scheduling vector has the wrong station count");
318 if (tol <= 0)
throw InputError(
"pfqn_conwayms: tolerance must be positive");
320 if (v < 0)
throw InputError(
"pfqn_conwayms: negative population");
323 const std::vector<T> Zs =
sum_rows(Z, R);
329 res.
C.assign(R, zero);
330 res.
X.assign(R, zero);
332 if (M == 0)
return res;
336 for (std::size_t i = 0; i < type.size(); ++i)
339 std::size_t cmax = 1;
340 for (
int c : nservers) cmax =
static_cast<std::size_t
>(c) > cmax ? c : cmax;
343 std::vector<Matrix<T>> Q(R + 1,
Matrix<T>(M, R, zero));
345 for (std::size_t s = 0; s <= R; ++s) {
346 const std::vector<int> N_1 =
oner(N, s);
347 for (std::size_t i = 0; i < M; ++i)
348 for (std::size_t r = 0; r < R; ++r)
352 throw InputError(
"pfqn_conwayms: initial queue lengths have the wrong shape");
354 std::vector<Matrix<T>> P(R + 1,
Matrix<T>(M, cmax, zero));
355 std::vector<std::vector<T>> PB(R + 1, std::vector<T>(M, zero));
356 detail::linms_init_marginals(M, R, nservers, Q, N, P, PB);
358 std::vector<Matrix<T>> Delta(R,
Matrix<T>(M, R, zero));
360 std::vector<T> X(R, zero);
362 for (
int I = 0; I < 2; ++I) {
363 for (std::size_t s = 0; s <= R; ++s) {
364 const std::vector<int> N_1 =
oner(N, s);
365 bool feasible =
true;
367 if (v < 0) feasible =
false;
368 if (!feasible)
continue;
369 res.
totiter += detail::conway_core(L, M, R, N_1, Zs, nservers, allFCFS, Q[s], P[s],
370 PB[s], Delta, tol, maxiter - res.
totiter, W, X);
373 for (std::size_t i = 0; i < M; ++i)
374 for (std::size_t r = 0; r < R; ++r) {
375 if (N[r] == 0)
continue;
377 for (std::size_t s = 1; s <= R; ++s) {
378 if (N[s - 1] <= 2)
continue;
379 const std::vector<int> Ns =
oner(N, s);
384 Delta[r](i, s - 1) = -Q[0](i, r) / nrT;
392 detail::conway_core(L, M, R, N, Zs, nservers, allFCFS, Q[0], P[0], PB[0], Delta, tol,
397 for (std::size_t i = 0; i < M; ++i)
398 for (std::size_t r = 0; r < R; ++r)
399 res.
U(i, r) = nservers[i] == 1
402 for (std::size_t r = 0; r < R; ++r)
410 const Matrix<T>& Z,
const std::vector<int>& nservers) {
NumericError(const std::string &what)
The exception types the port throws.
Dense matrix and non-owning view.
T num_multinomial(const std::vector< int > &m)
Multinomial coefficient sum(m)!
LinearizerResult< T > pfqn_conwayms(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)
Conway's multiserver Linearizer for chain-dependent FCFS queues (Conway 1989, "Fast Approximate Solut...
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...
void first_composition(std::vector< int > &n, int c)
matlab/src/util/multichoose.m and sprod.m, as an in-place odometer: the compositions of c into R non-...
double enorm_diff(const Matrix< T > &A, const Matrix< T > &B)
Frobenius norm of the difference of two equally shaped matrices, AS A DOUBLE.
T num_pow_int(const T &base, unsigned e)
Integer power, valid in any field (no transcendental requirement).
Number-type abstraction for the templated API port.
Scaffolding shared by the approximate-MVA family.
Extended generalized fixed-point Linearizer (De Souza e Silva and Muntz's generalization of Chandy an...
Multiserver Linearizer (Krzesinski's Linearizer as described in Conway 1989, with De Souza e Silva an...
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