5#ifndef LINE_API_QSYS_QSYS_MAPD1_H
6#define LINE_API_QSYS_QSYS_MAPD1_H
170std::vector<Matrix<T>> map_counting_at(
const Matrix<T>& D0,
const Matrix<T>& D1,
const T& s,
172 const std::size_t n = D0.
rows();
173 const std::size_t dim = (
static_cast<std::size_t
>(K) + 1) * n;
175 for (
unsigned b = 0; b <= K; ++b) {
176 for (std::size_t i = 0; i < n; ++i)
177 for (std::size_t j = 0; j < n; ++j) M(b * n + i, b * n + j) = D0(i, j);
179 for (std::size_t i = 0; i < n; ++i)
180 for (std::size_t j = 0; j < n; ++j) M(b * n + i, (b + 1) * n + j) = D1(i, j);
182 const Matrix<T> E = expm(M, s);
183 std::vector<Matrix<T>> P(
static_cast<std::size_t
>(K) + 1);
184 for (
unsigned b = 0; b <= K; ++b) {
186 for (std::size_t i = 0; i < n; ++i)
187 for (std::size_t j = 0; j < n; ++j) P[b](i, j) = E(i, b * n + j);
205std::vector<T> mapd_waiting_time(
const std::vector<std::vector<T>>& pi, std::size_t m,
206 std::size_t c,
const Matrix<T>& D0,
const Matrix<T>& D1,
207 const T& s, std::size_t num_steps,
const std::vector<T>& thetaA,
208 unsigned max_arrivals,
const T& tol) {
209 const T zero = num_traits<T>::from_int(0), one = num_traits<T>::from_int(1);
210 const std::size_t nlev = pi.size();
212 if (nlev == 0 || c == 0)
return w;
213 auto level_mass = [&](std::size_t lvl) {
216 for (std::size_t j = 0; j < m; ++j) acc += pi[lvl][j];
221 for (std::size_t lvl = 0; lvl < c; ++lvl) acc += level_mass(lvl);
224 const T stop = one - num_traits<T>::from_double(1e-10);
226 while (WTacum < stop && i * c < nlev) {
228 for (std::size_t lvl = (i - 1) * c; lvl < i * c; ++lvl) blk += level_mass(lvl);
229 w.push_back(T(w.back() + blk));
233 if (num_steps <= 1)
return w;
235 const std::size_t nw = w.size();
237 std::vector<std::vector<T>>
table(num_steps, std::vector<T>(nw, zero));
239 for (std::size_t step = 1; step < num_steps; ++step) {
240 const T tau = T(s * num_traits<T>::from_int(
static_cast<long>(step)) /
241 num_traits<T>::from_int(
static_cast<long>(num_steps)));
244 for (std::size_t q = 0; q < m; ++q) {
245 const T d = -D0(q, q);
246 if (d > numax) numax = d;
250 const double e = num_traits<T>::to_double(T(numax * tau));
251 const unsigned guess =
static_cast<unsigned>(2.0 * e + 10.0 * std::sqrt(e + 1.0) + 8.0);
252 if (guess > K) K = guess;
254 if (K > max_arrivals) K = max_arrivals;
255 std::vector<Matrix<T>> Ptau = map_counting_at(D0, D1, tau, K);
258 for (std::size_t p = 0; p < m; ++p) {
260 for (std::size_t k = 0; k < Ptau.size(); ++k)
261 for (std::size_t q = 0; q < m; ++q) row += Ptau[k](p, q);
262 const T lack = one - row;
263 if (lack > missing) missing = lack;
265 if (missing <= tol || K >= max_arrivals)
break;
266 K = (2u * K < max_arrivals) ? 2u * K : max_arrivals;
267 Ptau = map_counting_at(D0, D1, tau, K);
269 const std::size_t Kmax = Ptau.size() - 1;
270 const Matrix<T> P0tauInv =
inverse(Ptau[0]);
273 std::vector<std::vector<T>> g(nlev, thetaA);
274 std::vector<T> sumPi = pi[0];
275 g[0] =
vecmul(sumPi, P0tauInv);
277 auto row_sum = [&](
const std::vector<T>& v) {
279 for (std::size_t q = 0; q < v.size(); ++q) r += v[q];
282 while (row_sum(g[ii - 2]) < WTacum && ii <= nlev) {
283 for (std::size_t q = 0; q < m; ++q) sumPi[q] += pi[ii - 1][q];
284 const std::size_t Kstart = (ii - 1 > Kmax) ? (ii - 1 - Kmax) : 0;
285 std::vector<T> sumG =
vecmul(g[Kstart], Ptau[ii - 1 - Kstart]);
286 for (std::size_t k = Kstart + 2; k <= ii - 1; ++k) {
287 const std::vector<T> t =
vecmul(g[k - 1], Ptau[ii - k]);
288 for (std::size_t q = 0; q < m; ++q) sumG[q] += t[q];
290 std::vector<T> rhs(m, zero);
291 for (std::size_t q = 0; q < m; ++q) rhs[q] = sumPi[q] - sumG[q];
292 g[ii - 1] =
vecmul(rhs, P0tauInv);
296 std::vector<T> wt(nw, zero);
298 wt[0] = row_sum(g[c - 1]);
299 for (std::size_t q = 2; q + 1 <= nw; ++q) wt[q - 1] = row_sum(g[q * c - 1]);
300 if (nw == 1) wt[0] = row_sum(g[c - 1]);
305 out.reserve(num_steps * nw);
306 for (std::size_t q = 0; q < nw; ++q)
307 for (std::size_t step = 0; step < num_steps; ++step) out.push_back(table[step][q]);
331 unsigned max_arrivals, std::size_t max_levels,
const T& tol,
332 std::size_t num_steps) {
334 "qsys_mapd1 requires transcendental arithmetic");
336 if (s <= zero)
throw InputError(
"qsys_mapd1: service time s must be positive");
337 if (dist_size == 0)
throw InputError(
"qsys_mapd1: dist_size must be positive");
338 if (max_levels == 0)
throw InputError(
"qsys_mapd1: max_levels must be positive");
339 const std::size_t n = arrival.
D0.rows();
340 if (arrival.
D0.cols() != n || arrival.
D1.rows() != n || arrival.
D1.cols() != n)
341 throw InputError(
"qsys_mapd1: D0 and D1 must be square and of equal order");
346 if (lambda <= zero)
throw InputError(
"qsys_mapd1: non-positive arrival rate");
347 const T rho = lambda * s;
348 if (rho >= one)
throw InputError(
"qsys_mapd1: load rho must be strictly less than 1");
352 for (std::size_t i = 0; i < n; ++i) {
353 const T d = -D0(i, i);
354 if (d > numax) numax = d;
356 const T est = numax * s;
360 const unsigned guess =
static_cast<unsigned>(2.0 * e + 10.0 * std::sqrt(e + 1.0) + 8.0);
361 if (guess > K) K = guess;
363 if (K > max_arrivals) K = max_arrivals;
364 std::vector<Matrix<T>> A = detail::map_counting_at(D0, D1, s, K);
367 for (std::size_t i = 0; i < n; ++i) {
369 for (std::size_t k = 0; k < A.size(); ++k)
370 for (std::size_t j = 0; j < n; ++j) row += A[k](i, j);
371 const T lack = one - row;
372 if (lack > missing) missing = lack;
374 if (missing <= tol || K >= max_arrivals)
break;
375 K = (2u * K < max_arrivals) ? 2u * K : max_arrivals;
376 A = detail::map_counting_at(D0, D1, s, K);
381 std::vector<Matrix<T>> I(A.size());
384 for (std::size_t i = 0; i < n; ++i) C(i, i) -= one;
385 I[0] = detail::rdivide(C, invD0);
386 for (std::size_t k = 1; k < A.size(); ++k) {
389 for (std::size_t i = 0; i < n; ++i)
390 for (std::size_t j = 0; j < n; ++j) Ck(i, j) -= prev(i, j);
391 I[k] = detail::rdivide(Ck, invD0);
397 for (std::size_t i = 0; i < n; ++i)
398 for (std::size_t j = 0; j < n; ++j) negD0inv(i, j) = -invD0(i, j);
400 std::vector<Matrix<T>> B(A.size());
401 for (std::size_t k = 0; k < A.size(); ++k) B[k] =
matmul(Pa, A[k]);
405 const unsigned gmax = 100000u;
406 const std::size_t ktop = A.size() - 1;
407 for (
unsigned it = 0; it < gmax; ++it) {
410 for (std::size_t k = ktop; k-- > 2;) S = mam::qbd_detail::madd(A[k],
matmul(S, G));
415 for (std::size_t i = 0; i < n; ++i)
416 for (std::size_t j = 0; j < n; ++j) {
417 const T d =
num_abs(T(Gn(i, j) - G(i, j)));
418 if (d > gap) gap = d;
421 if (gap <= tol)
break;
425 const std::size_t K1 = A.size() - 1;
426 std::vector<Matrix<T>> Ahat(K1 + 2), Bhat(K1 + 2);
429 for (std::size_t i = K1 + 1; i-- > 0;) {
430 Ahat[i] = mam::qbd_detail::madd(A[i],
matmul(Ahat[i + 1], G));
431 Bhat[i] = mam::qbd_detail::madd(B[i],
matmul(Bhat[i + 1], G));
437 for (std::size_t i = 0; i < n; ++i) M0(i, i) -= one;
438 std::vector<std::vector<T>> x;
439 x.push_back(mam::qbd_detail::statvec(M0));
442 for (
const T& v : x[0]) mass += v;
443 for (std::size_t lvl = 1; lvl <= max_levels; ++lvl) {
444 std::vector<T> acc(n, zero);
446 const std::vector<T> t =
vecmul(x[0], Bhat[lvl]);
447 for (std::size_t j = 0; j < n; ++j) acc[j] += t[j];
449 const std::size_t kmin = (lvl + 1 > K1) ? (lvl + 1 - K1) : 1;
450 for (std::size_t k = kmin; k + 1 <= lvl; ++k) {
451 const std::vector<T> t =
vecmul(x[k], Ahat[lvl - k + 1]);
452 for (std::size_t j = 0; j < n; ++j) acc[j] += t[j];
454 const std::vector<T> xn =
vecmul(acc, W);
456 for (
const T& v : xn) inc += v;
459 if (inc <= tol * mass)
break;
461 for (std::vector<T>& row : x)
462 for (T& v : row) v /= mass;
465 const std::vector<T> e =
ones<T>(n);
466 const std::vector<T> y0 =
vecmul(x[0], Pa);
467 std::vector<std::vector<T>> Ie(I.size());
468 for (std::size_t k = 0; k < I.size(); ++k) Ie[k] =
mulvec(I[k], e);
469 std::vector<T> Isum_e(n, zero), Iw_e(n, zero);
470 for (std::size_t k = 0; k < I.size(); ++k)
471 for (std::size_t j = 0; j < n; ++j) {
472 Isum_e[j] += Ie[k][j];
477 for (std::size_t m = 1; m < x.size(); ++m) {
478 T a = zero, b = zero;
479 for (std::size_t j = 0; j < n; ++j) {
480 a += x[m][j] * Isum_e[j];
481 b += x[m][j] * Iw_e[j];
485 for (std::size_t j = 0; j < n; ++j) L += y0[j] * (Isum_e[j] + Iw_e[j]);
496 const std::vector<T> idle =
mulvec(negD0inv, e);
498 for (std::size_t j = 0; j < n; ++j) p0 += x[0][j] * idle[j];
500 for (std::size_t lvl = 1; lvl < dist_size; ++lvl) {
502 if (lvl - 1 < Ie.size())
503 for (std::size_t j = 0; j < n; ++j) acc += y0[j] * Ie[lvl - 1][j];
504 for (std::size_t m = 1; m <= lvl && m < x.size(); ++m) {
505 const std::size_t kk = lvl - m;
506 if (kk >= Ie.size())
continue;
507 for (std::size_t j = 0; j < n; ++j) acc += x[m][j] * Ie[kk][j];
516 const std::size_t nlev = x.size() + I.size();
517 std::vector<std::vector<T>> pi(nlev, std::vector<T>(n, zero));
519 const std::vector<T> p0v =
vecmul(x[0], negD0inv);
520 for (std::size_t j = 0; j < n; ++j) pi[0][j] = lambda * p0v[j];
522 for (std::size_t lvl = 1; lvl < nlev; ++lvl) {
523 std::vector<T> acc(n, zero);
524 if (lvl - 1 < I.size()) {
525 const std::vector<T> t =
vecmul(y0, I[lvl - 1]);
526 for (std::size_t j = 0; j < n; ++j) acc[j] += t[j];
528 for (std::size_t m = 1; m <= lvl && m < x.size(); ++m) {
529 const std::size_t kk = lvl - m;
530 if (kk >= I.size())
continue;
531 const std::vector<T> t =
vecmul(x[m], I[kk]);
532 for (std::size_t j = 0; j < n; ++j) acc[j] += t[j];
534 for (std::size_t j = 0; j < n; ++j) pi[lvl][j] = lambda * acc[j];
537 r.
waitingTimeDist = detail::mapd_waiting_time(pi, n,
static_cast<std::size_t
>(1), D0, D1,
538 s, num_steps == 0 ? 1 : num_steps, thetaA,
548 unsigned max_arrivals, std::size_t max_levels,
const T& tol) {
549 return qsys_mapd1(arrival, s, dist_size, max_arrivals, max_levels, tol,
550 static_cast<std::size_t
>(1));
559 return qsys_mapd1(arrival, s,
static_cast<std::size_t
>(100), 4096u,
The exception types the port throws.
Matrix exponential by scaling and squaring with a diagonal Pade approximant.
Dense linear algebra over the templated number type: products, identity, inverse, and powers.
LU factorization with partial pivoting, templated on the number type.
Markovian arrival process descriptors: stationary vectors, rate, moments, autocorrelation and the ind...
Dense matrix and non-owning view.
std::vector< T > map_prob(const Map< T > &m)
Stationary distribution of the phase process, pi (D0 + D1) = 0.
T map_lambda(const Map< T > &m)
Stationary arrival rate, lambda = pi D1 e.
const std::map< std::string, std::vector< std::string > > & table()
MapD1Result< T > qsys_mapd1(const mam::Map< T > &arrival, const T &s, std::size_t dist_size, unsigned max_arrivals, std::size_t max_levels, const T &tol, std::size_t num_steps)
MAP/D/1 by the exact embedded M/G/1-type chain.
Conservation laws of a layered queueing network, enumerated from its structure.
std::vector< T > vecmul(const std::vector< T > &v, const Matrix< T > &A)
Row vector times matrix, v A.
Matrix< T > inverse(const Matrix< T > &A)
Inverse by LU with one factorization and n back substitutions.
Matrix< T > matmul(const Matrix< T > &A, const Matrix< T > &B)
Matrix product A B.
std::vector< T > mulvec(const Matrix< T > &A, const std::vector< T > &v)
Matrix times column vector, A v.
std::vector< T > ones(std::size_t n)
Column vector of ones, the ubiquitous e in MAP algebra.
Matrix< T > eye(std::size_t n)
Identity of order n.
Number-type abstraction for the templated API port.
Quasi-birth-death processes: the rate matrix R, the fundamental matrix G, the caudal characteristic,...
A MAP as the pair of matrices (D0, D1).
Return value of qsys_mapd1, mirroring the MATLAB result struct.
T meanSojournTime
W = Wq + s.
T meanWaitingTime
Wq = (L - rho)/lambda.
T meanQueueLength
L, number in system, time-stationary.
std::vector< T > waitingTimeDist
Waiting time CDF at the points {0, s/numSteps, 2 s/numSteps, ...}, the reference's Q-MAM w output (Q_...
std::string analyzer
the reference's 'Q-MAM:MAP/D/1'
std::vector< T > queueLengthDist
P(N = n), n = 0, 1, ...
T utilization
rho = lambda s