LINE Solver (C++)
Templated C++ port of the LINE queueing solver
Loading...
Searching...
No Matches
qbd_mapmap1.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_MAM_QBD_MAPMAP1_H
6#define LINE_API_MAM_QBD_MAPMAP1_H
7
8/**
9 * @file
10 * @ingroup api_mam
11 * The MAP/MAP/1 queue solved as a quasi-birth-death process.
12 *
13 * Templated port of matlab/src/api/mam/qbd_mapmap1.m and qbd_rg.m,
14 * cross-checked against jar/src/main/java/jline/api/mam/Qbd_mapmap1.java.
15 *
16 * The level is the number in system and the phase is the pair (arrival phase,
17 * service phase), so the level blocks are
18 *
19 * F = D1^a (x) I_ns an arrival, level up
20 * L = D0^a (+) D0^s no event, level unchanged
21 * B = I_na (x) D1^s a service completion, level down
22 * Lbar = D0^a (x) I_ns level zero: no server is busy
23 *
24 * with (x) the Kronecker product and (+) the Kronecker sum. R and G come from
25 * cyclic reduction (qbd_fundmat), the boundary vector from qbd_pi.
26 *
27 * ARITHMETIC. qbd_mapmap1 itself is gated on transcendental arithmetic because
28 * it calls qbd_fundmat; see qbd_r.h for why the fixed-point iterations cannot
29 * be exact. Everything that consumes R afterwards is un-gated and instantiates
30 * at Rational: qbd_mapmap1_blocks assembles the level blocks with Kronecker
31 * products only, and the moment formulas below are closed forms in R,
32 *
33 * E[N (N-1) ... (N-m+1)] = m! pi_0 R^m (I - R)^-(m+1) e,
34 *
35 * a finite product of exact matrix operations. Those closed forms are also
36 * more accurate than the reference: MATLAB and the JAR sum the truncated level
37 * distribution k pi_k e until the accumulated mass reaches 1 - 1e-10, which
38 * discards the tail mass times its (unbounded) level index, whereas the closed
39 * form sums the whole geometric tail. The difference grows as the caudal
40 * characteristic approaches one; see the note on QN_truncated below.
41 */
42
43#include <cstddef>
44#include <vector>
45
49#include "line/api/mam/qbd_r.h"
50#include "line/num/number.h"
51#include "line/util/error.h"
52#include "line/util/linalg.h"
53#include "line/util/matrix.h"
54
55namespace line {
56namespace mam {
57
58/** The four level blocks of the MAP/MAP/1 QBD. */
59template <class T>
61 Matrix<T> B; ///< A_-1, service completion
62 Matrix<T> L; ///< A_0, local
63 Matrix<T> F; ///< A_1, arrival
64 Matrix<T> Lbar; ///< level-zero local block
65};
66
67/**
68 * Level blocks of the MAP/MAP/1 QBD from the arrival and service MAPs.
69 * Kronecker products only, so exact at Rational.
70 */
71template <class T>
72QbdMapMap1Blocks<T> qbd_mapmap1_blocks(const Map<T>& arrival, const Map<T>& service) {
73 const std::size_t na = arrival.order();
74 const std::size_t ns = service.order();
76 b.F = kron(arrival.D1, eye<T>(ns));
77 b.L = krons(arrival.D0, service.D0);
78 b.B = kron(eye<T>(na), service.D1);
79 b.Lbar = kron(arrival.D0, eye<T>(ns));
80 return b;
81}
82
83/** R, G and the level blocks of a MAP/MAP/1 queue (qbd_rg.m). */
84template <class T>
85struct QbdRg {
89 Matrix<T> U; ///< U = L + R B, the generator of the taboo process at a level
90};
91
92/**
93 * R and G of the MAP/MAP/1 QBD (qbd_rg.m). Gated: qbd_fundmat is iterative.
94 *
95 * @param util if positive, the service MAP is first rescaled to mean
96 * util / lambda_arrival, exactly as the optional third argument of
97 * qbd_rg.m and qbd_mapmap1.m does through map_scale
98 * @param arrival the arrival MAP
99 * @param service_in the service MAP, rescaled to the requested utilization
100 */
101template <class T>
102QbdRg<T> qbd_rg(const Map<T>& arrival, const Map<T>& service_in, const T& util) {
103 static_assert(num_traits<T>::has_transcendental, "qbd_rg requires transcendental arithmetic");
104 Map<T> service = service_in;
105 if (util > num_traits<T>::from_int(0)) service = map_scale(service, T(util / map_lambda(arrival)));
106 const QbdMapMap1Blocks<T> blk = qbd_mapmap1_blocks(arrival, service);
107 const QbdFundMat<T> fm = qbd_fundmat(blk.B, blk.L, blk.F);
108 QbdRg<T> out;
109 out.R = fm.R;
110 out.G = fm.G;
111 out.B = blk.B;
112 out.L = blk.L;
113 out.F = blk.F;
114 out.Lbar = blk.Lbar;
115 out.U = qbd_detail::madd(blk.L, matmul(fm.R, blk.B));
116 return out;
117}
118
119/** qbd_rg without rescaling the service process. */
120template <class T>
121QbdRg<T> qbd_rg(const Map<T>& arrival, const Map<T>& service) {
122 return qbd_rg(arrival, service, T(num_traits<T>::from_int(0)));
123}
124
125/**
126 * Factorial moment of order m of the number in system, computed in closed form
127 * from the boundary vector and R:
128 *
129 * E[N(N-1)...(N-m+1)] = m! pi_0 R^m (I - R)^-(m+1) e.
130 *
131 * Un-gated: given pi_0 and R this is exact matrix algebra. m = 0 returns 1.
132 */
133template <class T>
134T qbd_qlen_factmoment(const std::vector<T>& pi0, const Matrix<T>& R, unsigned m) {
135 const std::size_t n = R.rows();
136 if (pi0.size() != n) throw InputError("qbd_qlen_factmoment: pi0 length mismatch");
137 if (m == 0) return num_traits<T>::from_int(1);
138 const Matrix<T> ImRinv = inverse(qbd_detail::msub(eye<T>(n), R));
139 const Matrix<T> A = matmul(matpow(R, m), matpow(ImRinv, m + 1));
140 const std::vector<T> v = vecmul(pi0, A);
142 for (const T& x : v) s += x;
143 return num_factorial<T>(m) * s;
144}
145
146/**
147 * Raw moment of order m of the number in system, E[N^m], assembled from the
148 * factorial moments with the Stirling numbers of the second kind,
149 * N^m = sum_j S(m,j) N(N-1)...(N-j+1). Integer combinatorics plus exact matrix
150 * algebra, so un-gated and exact at Rational.
151 */
152template <class T>
153T qbd_qlen_moment(const std::vector<T>& pi0, const Matrix<T>& R, unsigned m) {
154 if (m == 0) return num_traits<T>::from_int(1);
155 // S(i,j) by the recurrence S(i,j) = j S(i-1,j) + S(i-1,j-1).
156 std::vector<std::vector<long long>> S(m + 1, std::vector<long long>(m + 1, 0));
157 S[0][0] = 1;
158 for (unsigned i = 1; i <= m; ++i)
159 for (unsigned j = 1; j <= i; ++j)
160 S[i][j] = static_cast<long long>(j) * S[i - 1][j] + S[i - 1][j - 1];
161 T acc = num_traits<T>::from_int(0);
162 for (unsigned j = 1; j <= m; ++j)
163 acc += num_traits<T>::from_int(static_cast<long>(S[m][j])) *
164 qbd_qlen_factmoment(pi0, R, j);
165 return acc;
166}
167
168/** Result of qbd_mapmap1, mirroring the MATLAB return list. */
169template <class T>
171 T XN; ///< throughput, = lambda of the arrival MAP
172 T QN; ///< mean number in system, closed form
173 T UN; ///< utilization, = 1 - sum(pi_0)
174 T RN; ///< mean response time, QN / XN by Little's law
175 T eta; ///< caudal characteristic, sp(R)
176 Matrix<T> pqueue; ///< level distribution, row k = pi_k
177 std::vector<T> pi0; ///< boundary vector, normalized
180 Map<T> service; ///< the service MAP actually used (rescaled if util was given)
181 unsigned iterations = 0;
182};
183
184/**
185 * MAP/MAP/1 queue (qbd_mapmap1.m).
186 *
187 * Differences from the MATLAB reference, both deliberate:
188 * - QN is the closed form pi_0 R (I - R)^-2 e rather than the truncated sum
189 * over the levels that MATLAB and the JAR compute. The truncated value is
190 * available as qbd_mapmap1_qlen_truncated(res) for a like-for-like
191 * comparison.
192 * - eta is bracketed by Collatz-Wielandt on R (qbd_caudal) rather than taken
193 * from an eigendecomposition; the value is the same spectral radius.
194 *
195 * @param util if positive, rescale the service MAP to mean util / lambda_a
196 * @param max_levels how many levels of pqueue to materialize
197 * @param arrival the arrival MAP
198 * @param service_in the service MAP, rescaled to the requested utilization
199 */
200template <class T>
201QbdMapMap1Result<T> qbd_mapmap1(const Map<T>& arrival, const Map<T>& service_in, const T& util,
202 std::size_t max_levels) {
204 "qbd_mapmap1 requires transcendental arithmetic");
205 Map<T> service = service_in;
206 const T lambda_a = map_lambda(arrival);
207 if (util > num_traits<T>::from_int(0)) service = map_scale(service, T(util / lambda_a));
208 const T lambda_s = map_lambda(service);
209 if (lambda_a >= lambda_s)
210 throw NumericError("qbd_mapmap1: the queue is not stable, lambda_a >= lambda_s");
211
212 const QbdMapMap1Blocks<T> blk = qbd_mapmap1_blocks(arrival, service);
213 const QbdFundMat<T> fm = qbd_fundmat(blk.B, blk.L, blk.F);
214
216 res.R = fm.R;
217 res.G = fm.G;
218 res.iterations = fm.iterations;
219 res.B = blk.B;
220 res.L = blk.L;
221 res.F = blk.F;
222 res.Lbar = blk.Lbar;
223 res.U = qbd_detail::madd(blk.L, matmul(fm.R, blk.B));
224 res.service = service;
225
226 res.pqueue = qbd_pi(blk.B, blk.Lbar, fm.R, max_levels, T(num_traits<T>::from_double(1e-10)));
227 res.pi0.assign(res.pqueue.cols(), num_traits<T>::from_int(0));
228 for (std::size_t j = 0; j < res.pqueue.cols(); ++j) res.pi0[j] = res.pqueue(0, j);
229
230 T s0 = num_traits<T>::from_int(0);
231 for (const T& v : res.pi0) s0 += v;
232 res.UN = num_traits<T>::from_int(1) - s0;
233 res.QN = qbd_qlen_factmoment(res.pi0, fm.R, 1);
234 res.XN = lambda_a;
235 res.RN = res.QN / res.XN;
236 res.eta = qbd_caudal(fm.R);
237 return res;
238}
239
240/** qbd_mapmap1 without rescaling and with 20000 materialized levels. */
241template <class T>
242QbdMapMap1Result<T> qbd_mapmap1(const Map<T>& arrival, const Map<T>& service) {
243 return qbd_mapmap1(arrival, service, T(num_traits<T>::from_int(0)),
244 static_cast<std::size_t>(20000));
245}
246
247/**
248 * The mean number in system computed the way MATLAB's qbd_mapmap1 does it, by
249 * summing k over the materialized levels. Provided for a like-for-like
250 * comparison against the reference; qbd_mapmap1's QN field is the closed form
251 * and is the value to use.
252 */
253template <class T>
255 T acc = num_traits<T>::from_int(0);
256 for (std::size_t k = 1; k < res.pqueue.rows(); ++k) {
257 T lvl = num_traits<T>::from_int(0);
258 for (std::size_t j = 0; j < res.pqueue.cols(); ++j) lvl += res.pqueue(k, j);
259 acc += num_traits<T>::from_int(static_cast<long>(k)) * lvl;
260 }
261 return acc;
262}
263
264} // namespace mam
265} // namespace line
266
267#endif // LINE_API_MAM_QBD_MAPMAP1_H
InputError(const std::string &what)
Definition error.h:39
std::size_t rows() const
Definition matrix.h:89
NumericError(const std::string &what)
Definition error.h:45
The exception types the port throws.
Dense linear algebra over the templated number type: products, identity, inverse, and powers.
Markovian arrival process descriptors: stationary vectors, rate, moments, autocorrelation and the ind...
MAP constructors and structural transformations.
Dense matrix and non-owning view.
Marked MAP (MMAP) algebra: per-class rates, class probabilities, superposition, normalization and sca...
QbdMapMap1Blocks< T > qbd_mapmap1_blocks(const Map< T > &arrival, const Map< T > &service)
Level blocks of the MAP/MAP/1 QBD from the arrival and service MAPs.
Definition qbd_mapmap1.h:72
Matrix< T > krons(const Matrix< T > &A, const Matrix< T > &B)
Kronecker sum, MATLAB's krons: kron(A, I_nb) + kron(I_na, B).
Definition mmap_lambda.h:71
T qbd_caudal(const Matrix< T > &R, unsigned iter_max, const T &tol)
Caudal characteristic eta = sp(R), the decay rate of the queue-length tail.
Definition qbd_r.h:355
QbdMapMap1Result< T > qbd_mapmap1(const Map< T > &arrival, const Map< T > &service_in, const T &util, std::size_t max_levels)
MAP/MAP/1 queue (qbd_mapmap1.m).
T qbd_mapmap1_qlen_truncated(const QbdMapMap1Result< T > &res)
The mean number in system computed the way MATLAB's qbd_mapmap1 does it, by summing k over the materi...
T qbd_qlen_moment(const std::vector< T > &pi0, const Matrix< T > &R, unsigned m)
Raw moment of order m of the number in system, E[N^m], assembled from the factorial moments with the ...
Matrix< T > kron(const Matrix< T > &A, const Matrix< T > &B)
Kronecker product.
Definition mmap_lambda.h:57
QbdFundMat< T > qbd_fundmat(const Matrix< T > &B, const Matrix< T > &L, const Matrix< T > &F, unsigned iter_max, const T &tol)
G and R by cyclic reduction (qbd_fundmat.m, the Bini-Meini logarithmic reduction on the raw level blo...
Definition qbd_r.h:280
Map< T > map_scale(const Map< T > &in, const T &new_mean)
Rescale time so that the mean inter-arrival time becomes new_mean.
Matrix< T > qbd_pi(const Matrix< T > &B, const Matrix< T > &Lbar, const Matrix< T > &R, std::size_t max_levels, const T &mass_tol)
Stationary distribution of a QBD given R (QBD_pi.m, continuous-time branch, default boundary).
Definition qbd_r.h:412
T qbd_qlen_factmoment(const std::vector< T > &pi0, const Matrix< T > &R, unsigned m)
Factorial moment of order m of the number in system, computed in closed form from the boundary vector...
QbdRg< T > qbd_rg(const Map< T > &arrival, const Map< T > &service_in, const T &util)
R and G of the MAP/MAP/1 QBD (qbd_rg.m).
T map_lambda(const Map< T > &m)
Stationary arrival rate, lambda = pi D1 e.
Definition map_moment.h:79
T num_factorial(unsigned n)
Factorial as a value of T.
Definition number.h:184
std::vector< T > vecmul(const std::vector< T > &v, const Matrix< T > &A)
Row vector times matrix, v A.
Definition linalg.h:50
Matrix< T > inverse(const Matrix< T > &A)
Inverse by LU with one factorization and n back substitutions.
Definition linalg.h:72
Matrix< T > matmul(const Matrix< T > &A, const Matrix< T > &B)
Matrix product A B.
Definition linalg.h:36
Matrix< T > matpow(const Matrix< T > &A, unsigned k)
Integer matrix power, by repeated squaring.
Definition linalg.h:89
Matrix< T > eye(std::size_t n)
Identity of order n.
Definition linalg.h:28
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).
Definition map_moment.h:53
Matrix< T > D1
Definition map_moment.h:55
Matrix< T > D0
Definition map_moment.h:54
std::size_t order() const
Definition map_moment.h:57
G and R together, as returned by qbd_fundmat.
Definition qbd_r.h:259
Matrix< T > G
Definition qbd_r.h:260
Matrix< T > R
Definition qbd_r.h:261
unsigned iterations
Definition qbd_r.h:262
The four level blocks of the MAP/MAP/1 QBD.
Definition qbd_mapmap1.h:60
Matrix< T > L
A_0, local.
Definition qbd_mapmap1.h:62
Matrix< T > F
A_1, arrival.
Definition qbd_mapmap1.h:63
Matrix< T > B
A_-1, service completion.
Definition qbd_mapmap1.h:61
Matrix< T > Lbar
level-zero local block
Definition qbd_mapmap1.h:64
Result of qbd_mapmap1, mirroring the MATLAB return list.
std::vector< T > pi0
boundary vector, normalized
T QN
mean number in system, closed form
T RN
mean response time, QN / XN by Little's law
T eta
caudal characteristic, sp(R)
Map< T > service
the service MAP actually used (rescaled if util was given)
Matrix< T > pqueue
level distribution, row k = pi_k
T XN
throughput, = lambda of the arrival MAP
T UN
utilization, = 1 - sum(pi_0)
R, G and the level blocks of a MAP/MAP/1 queue (qbd_rg.m).
Definition qbd_mapmap1.h:85
Matrix< T > R
Definition qbd_mapmap1.h:86
Matrix< T > U
U = L + R B, the generator of the taboo process at a level.
Definition qbd_mapmap1.h:89
Matrix< T > L
Definition qbd_mapmap1.h:88
Matrix< T > B
Definition qbd_mapmap1.h:88
Matrix< T > Lbar
Definition qbd_mapmap1.h:88
Matrix< T > F
Definition qbd_mapmap1.h:88
Matrix< T > G
Definition qbd_mapmap1.h:87