5#ifndef LINE_API_QSYS_QSYS_MAPG1K_H
6#define LINE_API_QSYS_QSYS_MAPG1K_H
115 std::function<T(
const T&)>
pdf;
177namespace mapg1k_detail {
182 return std::numeric_limits<T>::epsilon();
187T service_mean(
const ServiceLaw<T>& svc) {
190 return svc.shape * svc.scale;
194 const std::size_t p = svc.ph_T.rows();
195 const std::vector<T> e =
ones<T>(p);
196 const std::vector<T> x =
solve(svc.ph_T, e);
198 for (std::size_t i = 0; i < p; ++i) s -= svc.ph_alpha[i] * x[i];
205 return num_traits<T>::from_int(0);
215template <
class T,
class W>
216T density_moment(
const ServiceLaw<T>& svc, W&& w,
const T& reltol) {
217 const T zero = num_traits<T>::from_int(0);
220 std::function<T(
const T&)> g = [&](
const T& u) -> T {
222 const T v = w(x) * svc.pdf(x) * x;
223 return (v == v &&
num_abs(v) < std::numeric_limits<T>::infinity()) ? v : zero;
227 const T hi0 = svc.tmax_finite ? T(log(svc.tmax)) : num_traits<T>::from_int(1);
228 T lo = hi0 - num_traits<T>::from_int(2);
230 const T abstol = num_traits<T>::from_double(1e-300);
231 T total = detail::num_integral<T>(g, lo, hi, reltol, abstol, 40u);
232 const unsigned rounds = 60u;
233 for (
unsigned k = 0; k < rounds; ++k) {
234 const T lonew = lo - num_traits<T>::from_int(4);
235 const T add_lo = detail::num_integral<T>(g, lonew, lo, reltol, abstol, 40u);
238 if (!svc.tmax_finite) {
239 const T hinew = hi + num_traits<T>::from_int(4);
240 add_hi = detail::num_integral<T>(g, hi, hinew, reltol, abstol, 40u);
243 total += add_lo + add_hi;
245 if (added <= reltol *
num_abs(total))
break;
256struct ServiceCoefficients {
263ServiceCoefficients<T> service_coefficients(
const ServiceLaw<T>& svc,
const T& theta,
const T& tol,
264 std::size_t nmaxCap) {
268 const T zero = num_traits<T>::from_int(0), one = num_traits<T>::from_int(1);
269 ServiceCoefficients<T> out;
270 const T qreltol = num_traits<T>::from_double(1e-13);
272 ? density_moment(svc, [](
const T& x) {
return x; }, qreltol)
274 if (out.mean <= zero)
throw InputError(
"qsys_mapg1k: service law has non-positive mean");
280 const double md = num_traits<T>::to_double(T(theta * out.mean));
281 const double g = md + 10.0 * std::sqrt(md > 1.0 ? md : 1.0) + 32.0;
282 n0 =
static_cast<std::size_t
>(g < 32.0 ? 32.0 : std::ceil(g));
283 if (n0 + 1 > nmaxCap) n0 = nmaxCap > 0 ? nmaxCap - 1 : 0;
286 std::vector<T>& cn = out.cn;
289 case ServiceKind::Gamma: {
290 const T th = svc.scale, al = svc.shape;
291 if (al <= zero || th <= zero)
292 throw InputError(
"qsys_mapg1k: gamma shape and scale must be positive");
293 const T q = one + th * theta;
294 const T p = th * theta / q;
295 cn.push_back(exp(-al * log(q)));
296 for (std::size_t n = 1; n <= n0; ++n) {
297 const T nT = num_traits<T>::from_int(static_cast<long>(n));
298 cn.push_back(cn[n - 1] * p * (al + nT - one) / nT);
304 throw InputError(
"qsys_mapg1k: deterministic service time must be positive");
305 const T m = theta * svc.det;
306 cn.push_back(exp(-m));
307 for (std::size_t n = 1; n <= n0; ++n)
308 cn.push_back(cn[n - 1] * m / num_traits<T>::from_int(static_cast<long>(n)));
312 const std::size_t p = svc.ph_T.rows();
313 if (svc.ph_alpha.size() != p || svc.ph_T.cols() != p)
314 throw InputError(
"qsys_mapg1k: PH alpha and T are inconsistent");
316 for (std::size_t i = 0; i < p; ++i)
317 for (std::size_t j = 0; j < p; ++j)
318 ThI(i, j) = (i == j ? theta : zero) - svc.ph_T(i, j);
319 const Matrix<T> Minv = inverse(ThI);
320 const std::vector<T> e = ones<T>(p);
321 std::vector<T> t = mulvec(svc.ph_T, e);
322 for (T& v : t) v = -v;
323 std::vector<T> row = vecmul(svc.ph_alpha, Minv);
324 for (std::size_t n = 0; n <= n0; ++n) {
326 for (std::size_t i = 0; i < p; ++i) v += row[i] * t[i];
328 row = vecmul(row, Minv);
329 for (T& x : row) x *= theta;
334 if (!svc.pdf)
throw InputError(
"qsys_mapg1k: density service law has no pdf");
335 for (std::size_t n = 0; n <= n0; ++n) {
336 const T nT = num_traits<T>::from_int(
static_cast<long>(n));
338 cn.push_back(density_moment(
341 return exp(-theta * x + nT * log(theta * x) - lfact);
350 for (
const T& v : cn) total += v;
351 while (cn.size() < nmaxCap) {
352 if (num_abs(T(one - total)) <= tol) break;
353 const std::size_t first = cn.size();
354 const std::size_t last = (first + 63 < nmaxCap) ? first + 63 : nmaxCap - 1;
357 case ServiceKind::Gamma: {
358 const T q = one + svc.scale * theta;
359 const T p = svc.scale * theta / q;
360 for (std::size_t n = first; n <= last; ++n) {
361 const T nT = num_traits<T>::from_int(static_cast<long>(n));
362 cn.push_back(cn[n - 1] * p * (svc.shape + nT - one) / nT);
367 case ServiceKind::Deterministic: {
368 const T m = theta * svc.det;
369 for (std::size_t n = first; n <= last; ++n) {
370 cn.push_back(cn[n - 1] * m / num_traits<T>::from_int(static_cast<long>(n)));
375 case ServiceKind::PhaseType: {
377 const std::size_t p = svc.ph_T.rows();
379 for (std::size_t i = 0; i < p; ++i)
380 for (std::size_t j = 0; j < p; ++j)
381 ThI(i, j) = (i == j ? theta : zero) - svc.ph_T(i, j);
382 const Matrix<T> Minv = inverse(ThI);
383 const std::vector<T> e = ones<T>(p);
384 std::vector<T> t = mulvec(svc.ph_T, e);
385 for (T& v : t) v = -v;
386 std::vector<T> row = vecmul(svc.ph_alpha, Minv);
387 for (std::size_t n = 0; n < first; ++n) {
388 row = vecmul(row, Minv);
389 for (T& x : row) x *= theta;
391 for (std::size_t n = first; n <= last; ++n) {
393 for (std::size_t i = 0; i < p; ++i) v += row[i] * t[i];
396 row = vecmul(row, Minv);
397 for (T& x : row) x *= theta;
401 case ServiceKind::Density: {
402 for (std::size_t n = first; n <= last; ++n) {
403 const T nT = num_traits<T>::from_int(static_cast<long>(n));
404 const T lfact = log(num_factorial<T>(static_cast<unsigned>(n)));
405 cn.push_back(density_moment(
407 [&](const T& x) { return exp(-theta * x + nT * log(theta * x) - lfact); },
415 if (added <= eps_of<T>() * total)
break;
417 out.residual =
num_abs(T(one - total));
434 const T& tol, std::size_t nmaxCap) {
436 "qsys_mapg1k requires transcendental arithmetic");
438 const std::size_t M = arrival.
D0.rows();
439 if (arrival.
D0.cols() != M || arrival.
D1.rows() != M || arrival.
D1.cols() != M)
440 throw InputError(
"qsys_mapg1k: D0 and D1 must be square matrices of equal size");
441 if (K < 1)
throw InputError(
"qsys_mapg1k: buffer size K must be a positive integer");
446 for (std::size_t i = 0; i < M; ++i) {
447 const T b = -D0(i, i);
449 throw InputError(
"qsys_mapg1k: D0 must have strictly negative diagonal entries");
450 if (b > theta) theta = b;
453 const mapg1k_detail::ServiceCoefficients<T> sc =
454 mapg1k_detail::service_coefficients(svc, theta, tol, nmaxCap);
455 const std::vector<T>& cn = sc.cn;
456 const T Smean = sc.mean;
457 const std::size_t nmax = cn.size() - 1;
460 "qsys_mapg1k: the uniformization series for c_n was truncated with residual " +
464 std::vector<T> dn(nmax + 1, zero);
467 for (std::size_t n = nmax + 1; n-- > 0;) {
468 dn[n] = tail / theta;
474 const std::size_t mmax = K - 1;
475 std::vector<Matrix<T>> A(mmax + 1,
Matrix<T>(M, M, zero));
476 std::vector<Matrix<T>> Q(mmax + 1,
Matrix<T>(M, M, zero));
477 std::vector<Matrix<T>> Sn(mmax + 1,
Matrix<T>(M, M, zero));
479 Matrix<T> B0(M, M, zero), Qtot(M, M, zero);
481 Matrix<T> Pt0(M, M), Pt1(M, M), PD(M, M);
482 for (std::size_t i = 0; i < M; ++i)
483 for (std::size_t j = 0; j < M; ++j) {
484 Pt0(i, j) = (i == j ? one : zero) + D0(i, j) / theta;
485 Pt1(i, j) = D1(i, j) / theta;
486 PD(i, j) = (i == j ? one : zero) + (D0(i, j) + D1(i, j)) / theta;
488 for (std::size_t n = 0; n <= nmax; ++n) {
489 const std::size_t mtop = (n < mmax) ? n : mmax;
490 for (std::size_t m = 0; m <= mtop; ++m)
491 for (std::size_t i = 0; i < M; ++i)
492 for (std::size_t j = 0; j < M; ++j) {
493 A[m](i, j) += Sn[m](i, j) * cn[n];
494 Q[m](i, j) += Sn[m](i, j) * dn[n];
496 for (std::size_t i = 0; i < M; ++i)
497 for (std::size_t j = 0; j < M; ++j) {
498 B0(i, j) += Pn(i, j) * cn[n];
499 Qtot(i, j) += Pn(i, j) * dn[n];
502 std::vector<Matrix<T>> Snew(mmax + 1,
Matrix<T>(M, M, zero));
503 const std::size_t mt = (n + 1 < mmax) ? n + 1 : mmax;
504 for (std::size_t m = 0; m <= mt; ++m) {
506 if (m <= n) acc =
matmul(Sn[m], Pt0);
507 if (m >= 1 && m - 1 <= n) {
509 for (std::size_t i = 0; i < M; ++i)
510 for (std::size_t j = 0; j < M; ++j) acc(i, j) += add(i, j);
519 const std::vector<T> e =
ones<T>(M);
521 for (std::size_t i = 0; i < M; ++i)
522 for (std::size_t j = 0; j < M; ++j) negD0(i, j) = -D0(i, j);
525 const std::vector<T> idle =
mulvec(negD0inv, e);
529 const std::size_t lastblk = (K - 1) * M;
530 for (std::size_t n = 1; n + 1 <= K; ++n) {
532 for (std::size_t m = 0; m + n + 1 <= K; ++m) {
533 const std::size_t col = (n - 1 + m) * M;
534 for (std::size_t i = 0; i < M; ++i)
535 for (std::size_t j = 0; j < M; ++j) {
536 P(n * M + i, col + j) += A[m](i, j);
537 Bacc(i, j) -= A[m](i, j);
540 for (std::size_t i = 0; i < M; ++i)
541 for (std::size_t j = 0; j < M; ++j) P(n * M + i, lastblk + j) += Bacc(i, j);
545 for (std::size_t m = 0; m + 2 <= K; ++m) {
547 for (std::size_t i = 0; i < M; ++i)
548 for (std::size_t j = 0; j < M; ++j) {
549 P(i, m * M + j) += blk(i, j);
550 Bacc(i, j) -= A[m](i, j);
554 for (std::size_t i = 0; i < M; ++i)
555 for (std::size_t j = 0; j < M; ++j) P(i, lastblk + j) += tailblk(i, j);
560 for (std::size_t i = 0; i < K * M; ++i) {
562 for (std::size_t j = 0; j < K * M; ++j) s += P(i, j);
563 const T d =
num_abs(T(s - one));
564 if (d > rowdev) rowdev = d;
568 "qsys_mapg1k: embedded chain rows deviate from 1 by " +
570 "; the uniformization series for A_m has not converged, raise nmax");
574 std::vector<T> sigma0(sigma.begin(), sigma.begin() + M);
578 for (std::size_t j = 0; j < M; ++j) idleTime += sigma0[j] * idle[j];
579 const T Ecyc = Smean + idleTime;
580 const T Tput = one / Ecyc;
581 const T p0 = idleTime / Ecyc;
584 std::vector<Matrix<T>> Qcum(mmax + 1,
Matrix<T>(M, M, zero));
587 for (std::size_t m = 0; m <= mmax; ++m) {
588 for (std::size_t i = 0; i < M; ++i)
589 for (std::size_t j = 0; j < M; ++j) acc(i, j) += Q[m](i, j);
593 std::vector<T> timeKvec(M, zero);
594 for (std::size_t n = 1; n + 1 <= K; ++n) {
595 const std::size_t rr = K - n - 1;
596 std::vector<T> row(sigma.begin() + n * M, sigma.begin() + (n + 1) * M);
598 for (std::size_t i = 0; i < M; ++i)
599 for (std::size_t j = 0; j < M; ++j) D(i, j) -= Qcum[rr](i, j);
600 const std::vector<T> t =
vecmul(row, D);
601 for (std::size_t j = 0; j < M; ++j) timeKvec[j] += t[j];
604 const std::vector<T> s0Psi =
vecmul(sigma0, Psi);
607 for (std::size_t i = 0; i < M; ++i)
608 for (std::size_t j = 0; j < M; ++j) D(i, j) -= Qcum[K - 2](i, j);
609 const std::vector<T> t =
vecmul(s0Psi, D);
610 for (std::size_t j = 0; j < M; ++j) timeKvec[j] += t[j];
612 std::vector<T> pKvec(M, zero);
614 for (std::size_t j = 0; j < M; ++j) {
615 pKvec[j] = timeKvec[j] / Ecyc;
620 std::vector<T> timeL(K + 1, zero);
622 for (std::size_t n = 1; n + 1 <= K; ++n) {
623 std::vector<T> row(sigma.begin() + n * M, sigma.begin() + (n + 1) * M);
624 for (std::size_t l = n; l + 1 <= K; ++l) {
625 const std::vector<T> t =
vecmul(row, Q[l - n]);
626 for (std::size_t j = 0; j < M; ++j) timeL[l] += t[j];
630 const std::vector<T> s0Psi =
vecmul(sigma0, Psi);
631 for (std::size_t l = 1; l + 1 <= K; ++l) {
632 const std::vector<T> t =
vecmul(s0Psi, Q[l - 1]);
633 for (std::size_t j = 0; j < M; ++j) timeL[l] += t[j];
638 for (std::size_t j = 0; j < M; ++j) s += timeKvec[j];
641 std::vector<T> plevel(K + 1, zero);
643 for (std::size_t l = 0; l <= K; ++l) {
644 plevel[l] = timeL[l] / Ecyc;
648 throw NumericError(
"qsys_mapg1k: the level distribution has mass " +
650 "; the Q_m series has not converged, raise nmax");
652 for (std::size_t l = 0; l <= K; ++l)
655 std::vector<T> p0vec =
vecmul(sigma0, negD0inv);
656 for (T& v : p0vec) v /= Ecyc;
668 r.
rho = lambda * Smean;
683 static_cast<std::size_t
>(200000));
The algorithm cannot proceed on this instance (singular matrix, ...).
Equilibrium distribution of a discrete-time Markov chain, and stochastic complementation.
The exception types the port throws.
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.
T map_lambda(const Map< T > &m)
Stationary arrival rate, lambda = pi D1 e.
std::vector< T > dtmc_solve(const Matrix< T > &P)
Stationary distribution of a stochastic matrix P.
ServiceKind
Which family the service law belongs to.
MapG1kResult< T > qsys_mapg1k(const mam::Map< T > &arrival, const ServiceLaw< T > &svc, std::size_t K, const T &tol, std::size_t nmaxCap)
MAP/G/1/K with tail drop.
T num_factorial(unsigned n)
Factorial as a value of T.
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 > solve(const Matrix< T > &A, const std::vector< T > &b)
Convenience: solve Ax = b, leaving A and b untouched.
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.
Adaptive quadrature for the qsys functions whose MATLAB originals call integral(),...
A MAP as the pair of matrices (D0, D1).
Return value of qsys_mapg1k, mirroring the MATLAB result struct.
std::vector< T > plevel
P(level = l), l = 0..K.
T throughput
aggregate departure rate
T rho
offered load lambda S
std::vector< T > pKvec
P(level = K, phase j), summing to pK.
std::size_t nmax
uniformization order used
std::vector< T > p0vec
P(level = 0, phase j), summing to p0.
T lambda
aggregate MAP arrival rate
T countingResidual
|1 - sum_n c_n| at the truncation
T lossProbability
1 - throughput/lambda
T meanQueueLength
E[number in system].
std::vector< T > sigma
stationary law of the embedded chain, K M entries
Service-time descriptor, the C++ form of the MATLAB svc struct.
static ServiceLaw density(std::function< T(const T &)> f, const T &tmax)
static ServiceLaw density(std::function< T(const T &)> f)
Arbitrary density on (0, inf), or on (0, tmax] when tmax is supplied.
Matrix< T > ph_T
PH subgenerator.
T tmax
upper support limit of the density
static ServiceLaw deterministic(const T &d)
Constant service time d.
std::vector< T > ph_alpha
PH initial probability row.
T det
deterministic service time
bool tmax_finite
whether tmax is used
static ServiceLaw phase_type(const std::vector< T > &alpha, const Matrix< T > &Tmat)
Phase type (alpha, Tmat).
std::function< T(const T &)> pdf
density of the service time
static ServiceLaw gamma(const T &shape, const T &scale)
Gamma(shape, scale).