LINE Solver (C++)
Templated C++ port of the LINE queueing solver
Loading...
Searching...
No Matches
qsys_mg1k_loss.h
Go to the documentation of this file.
1/*
2 * Copyright (c) 2012-2026, QORE Lab, Imperial College London
3 * All rights reserved.
4 */
5#ifndef LINE_API_QSYS_QSYS_MG1K_LOSS_H
6#define LINE_API_QSYS_QSYS_MG1K_LOSS_H
7
8/**
9 * @file
10 * @ingroup api_qsys
11 * Exact M/G/1/K loss probability, via the chain embedded at service-start
12 * epochs.
13 *
14 * Templated port of matlab/src/api/qsys/qsys_mg1k_loss.m, cross-checked
15 * against jar/src/main/java/jline/api/qsys/Qsys_mg1k_loss.java.
16 *
17 * The state is the number waiting immediately after a service start,
18 * q in {0,...,K-2}, and a_j is the probability of j Poisson arrivals during
19 * one service:
20 *
21 * q = 0 : both a_0 and a_1 lead to q' = 0, because after an empty departure
22 * the next service starts with the next arrival; j >= 2 gives
23 * q' = j-1;
24 * q >= 1: q' = q-1+j, with arrivals past the free capacity lost and
25 * aggregated in the last column.
26 *
27 * The loss probability then follows from renewal-reward,
28 *
29 * E[cycle] = E[S] + sigma_0 a_0/lambda, P_loss = 1 - 1/(rho + sigma_0 a_0),
30 *
31 * with sigma the stationary vector at service-start epochs.
32 *
33 * The service law is supplied as its density. Both the mean service time and
34 * the a_j are obtained by adaptive quadrature on [0, 1e4/lambda], exactly the
35 * truncation and the tolerances MATLAB's integral() is given (RelTol 1e-6,
36 * AbsTol 1e-10), and the a_j series is stopped at 1e-12 or after 1000 terms as
37 * in the reference. For an exponential density the result must reproduce
38 * qsys_mm1k_loss, and that identity is the sharpest available check on the
39 * embedded chain.
40 *
41 * ARITHMETIC. The quadrature and the exp in the Poisson weights make this
42 * transcendental. The chain itself -- row normalization and the stationary
43 * solve -- is finite field arithmetic, but it is fed by the quadrature, so the
44 * gate applies to the whole function.
45 */
46
47#include <cstddef>
48#include <vector>
49
53#include "line/num/number.h"
54#include "line/util/error.h"
55#include "line/util/matrix.h"
56
57namespace line {
58namespace qsys {
59
60template <class T>
63 T utilization; ///< rho = lambda/mu with mu the reciprocal mean service time
64};
65
66namespace detail {
67
68/**
69 * Row-normalize P and repair the diagonal, MATLAB's dtmc_makestochastic. A row
70 * that sums to zero is replaced by a self-loop.
71 */
72template <class T>
73void dtmc_makestochastic_inplace(Matrix<T>& P) {
74 const T zero = num_traits<T>::from_int(0), one = num_traits<T>::from_int(1);
75 for (std::size_t i = 0; i < P.rows(); ++i) {
76 T s = zero;
77 for (std::size_t j = 0; j < P.cols(); ++j) s += P(i, j);
78 if (s > zero) {
79 for (std::size_t j = 0; j < P.cols(); ++j) P(i, j) /= s;
80 T off = zero;
81 for (std::size_t j = 0; j < P.cols(); ++j)
82 if (j != i) off += P(i, j);
83 T d = one - off;
84 if (d < zero) d = zero;
85 if (d > one) d = one;
86 P(i, i) = d;
87 } else {
88 for (std::size_t j = 0; j < P.cols(); ++j) P(i, j) = zero;
89 P(i, i) = one;
90 }
91 }
92}
93
94} // namespace detail
95
96/**
97 * @brief Exact M/G/1/K loss probability, via the chain embedded at
98 * service-start epochs.
99 *
100 * @param lambda Poisson arrival rate
101 * @param density service-time density, callable as density(t) -> T
102 * @param K system capacity, jobs in service included, K >= 2
103 */
104template <class T, class Density>
105Mg1kLossResult<T> qsys_mg1k_loss(const T& lambda, Density&& density, unsigned K) {
107 "qsys_mg1k_loss requires transcendental arithmetic");
108 if (K < 2) throw InputError("qsys_mg1k_loss: K must be at least 2");
109 const T zero = num_traits<T>::from_int(0), one = num_traits<T>::from_int(1);
110 const T reltol = T(num_traits<T>::from_double(1e-6));
111 const T abstol = T(num_traits<T>::from_double(1e-10));
112 const T tmax = T(num_traits<T>::from_double(1e4)) / lambda;
113
114 const T meanS = detail::num_integral<T>([&](const T& t) { return t * density(t); }, zero, tmax,
115 reltol, abstol);
116 if (meanS <= zero) throw NumericError("qsys_mg1k_loss: non-positive mean service time");
117 const T mu = one / meanS;
118
119 // a_j = P(j Poisson arrivals during one service), j = 0, 1, ...
120 std::vector<T> a(K - 1, zero);
121 const T stop = T(num_traits<T>::from_double(1e-12));
122 for (unsigned j = 0; j <= 1000u; ++j) {
123 const T aj = detail::num_integral<T>(
124 [&](const T& t) {
125 return detail::num_exp(T(-lambda * t)) *
126 num_pow_int(T(lambda * t), j) * density(t);
127 },
128 zero, tmax, reltol, abstol) /
130 if (j < a.size())
131 a[j] = aj;
132 else
133 a.push_back(aj);
134 if (aj < stop) break;
135 }
136
137 // Embedded chain at service-start epochs, states q = 0..K-2.
138 const std::size_t n = K - 1;
139 Matrix<T> P(n, n, zero);
140 const std::size_t last = n - 1;
141 P(0, 0) = a[0] + (a.size() > 1 ? a[1] : zero);
142 for (std::size_t i = 1; i + 2 <= n; ++i) P(0, i) = i + 1 < a.size() ? a[i + 1] : zero;
143 if (n >= 2) {
144 for (std::size_t i = 0; i + 1 < n; ++i) P(1, i) = i < a.size() ? a[i] : zero;
145 }
146 for (std::size_t r = 2; r < n; ++r)
147 for (std::size_t col = r - 1; col + 1 < n; ++col) {
148 const std::size_t j = col - r + 1;
149 P(r, col) = j < a.size() ? a[j] : zero;
150 }
151 // Last column absorbs everything the free capacity cannot take.
152 for (std::size_t r = 0; r < n; ++r) {
153 T s = zero;
154 for (std::size_t col = 0; col + 1 < n; ++col) s += P(r, col);
155 P(r, last) = one - s;
156 }
157 detail::dtmc_makestochastic_inplace(P);
158 const std::vector<T> sigma = mc::dtmc_solve(P);
159
161 r.utilization = lambda / mu;
162 const T carried = sigma[0] * a[0] + r.utilization;
163 if (carried == zero) throw NumericError("qsys_mg1k_loss: degenerate renewal cycle");
164 r.lossProbability = one - one / carried;
165 return r;
166}
167
168} // namespace qsys
169} // namespace line
170
171#endif // LINE_API_QSYS_QSYS_MG1K_LOSS_H
InputError(const std::string &what)
Definition error.h:39
std::size_t cols() const
Definition matrix.h:90
std::size_t rows() const
Definition matrix.h:89
NumericError(const std::string &what)
Definition error.h:45
Equilibrium distribution of a discrete-time Markov chain, and stochastic complementation.
The exception types the port throws.
Dense matrix and non-owning view.
std::vector< T > dtmc_solve(const Matrix< T > &P)
Stationary distribution of a stochastic matrix P.
Definition dtmc_solve.h:106
Mg1kLossResult< T > qsys_mg1k_loss(const T &lambda, Density &&density, unsigned K)
Exact M/G/1/K loss probability, via the chain embedded at service-start epochs.
T num_factorial(unsigned n)
Factorial as a value of T.
Definition number.h:184
T num_pow_int(const T &base, unsigned e)
Integer power, valid in any field (no transcendental requirement).
Definition number.h:192
Number-type abstraction for the templated API port.
Adaptive quadrature for the qsys functions whose MATLAB originals call integral(),...
Shared return type and arithmetic helpers for the templated qsys port.
T utilization
rho = lambda/mu with mu the reciprocal mean service time