LINE Solver (C++)
Templated C++ port of the LINE queueing solver
Loading...
Searching...
No Matches
map_moment.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_MAP_MOMENT_H
6#define LINE_API_MAM_MAP_MOMENT_H
7
8/**
9 * @file
10 * @ingroup api_mam
11 * Markovian arrival process descriptors: stationary vectors, rate, moments,
12 * autocorrelation and the index of dispersion.
13 *
14 * Templated port of the kpctoolbox MAP primitives used across LINE
15 * (matlab/lib/kpctoolbox/map/map_prob.m, map_pie.m, map_lambda.m, map_mean.m,
16 * map_moment.m, map_var.m, map_scv.m, map_embedded.m, map_acf.m, map_idc.m,
17 * map_infgen.m).
18 *
19 * A MAP is the pair (D0, D1): D0 carries the hidden transitions and the
20 * negative diagonal, D1 the transitions that emit an arrival. The whole family
21 * is rational in the entries of D0 and D1 -- stationary vectors are linear
22 * solves, moments are i! pie (-D0)^-i e -- so all of it is exact in rational
23 * arithmetic. That matters for fitting work, where the moments of a candidate
24 * MAP are compared against targets and a rounding artifact is easy to mistake
25 * for a fitting error.
26 */
27
28#include <cstddef>
29#include <vector>
30
32#include "line/num/number.h"
33#include "line/util/error.h"
34#include "line/util/linalg.h"
35#include "line/util/matrix.h"
36
37namespace line {
38namespace mam {
39
40/**
41 * Tolerance exponent shared by the KPC feasibility checks (map_feastol.m).
42 *
43 * It lives HERE, in the header every MAP user already includes, because it is
44 * odr-used from `map_transform.h` (map_isfeasible) and from `map_dist.h`. A
45 * declaration in one header and a definition in another that does not include
46 * it links only while some third translation unit happens to emit the weak
47 * symbol; a program built from the declaring header alone would not link.
48 */
49inline int map_feastol() { return 8; }
50
51/** A MAP as the pair of matrices (D0, D1). */
52template <class T>
53struct Map {
56
57 std::size_t order() const { return D0.rows(); }
58};
59
60/** Generator of the underlying phase process, D0 + D1. */
61template <class T>
63 if (m.D0.rows() != m.D1.rows() || m.D0.cols() != m.D1.cols())
64 throw InputError("map_infgen: D0 and D1 have different shapes");
65 Matrix<T> Q(m.D0.rows(), m.D0.cols());
66 for (std::size_t i = 0; i < Q.rows(); ++i)
67 for (std::size_t j = 0; j < Q.cols(); ++j) Q(i, j) = m.D0(i, j) + m.D1(i, j);
68 return Q;
69}
70
71/** Stationary distribution of the phase process, pi (D0 + D1) = 0. */
72template <class T>
73std::vector<T> map_prob(const Map<T>& m) {
74 return mc::ctmc_solve(map_infgen(m));
75}
76
77/** Stationary arrival rate, lambda = pi D1 e. */
78template <class T>
79T map_lambda(const Map<T>& m) {
80 const std::vector<T> p = map_prob(m);
81 const std::vector<T> pD1 = vecmul(p, m.D1);
83 for (const T& v : pD1) s += v;
84 return s;
85}
86
87/** Phase distribution seen by an arriving job, pie = pi D1 / (pi D1 e). */
88template <class T>
89std::vector<T> map_pie(const Map<T>& m) {
90 const std::vector<T> p = map_prob(m);
91 std::vector<T> a = vecmul(p, m.D1);
93 for (const T& v : a) s += v;
94 if (s == num_traits<T>::from_int(0)) throw NumericError("map_pie: the MAP has zero arrival rate");
95 for (T& v : a) v /= s;
96 return a;
97}
98
99/** Mean inter-arrival time, 1/lambda. */
100template <class T>
101T map_mean(const Map<T>& m) {
102 const T lam = map_lambda(m);
103 if (lam == num_traits<T>::from_int(0)) throw NumericError("map_mean: zero arrival rate");
104 return num_traits<T>::from_int(1) / lam;
105}
106
107/** Embedded DTMC at arrival epochs, P = (-D0)^-1 D1. */
108template <class T>
110 Matrix<T> negD0 = m.D0;
111 for (std::size_t i = 0; i < negD0.rows(); ++i)
112 for (std::size_t j = 0; j < negD0.cols(); ++j) negD0(i, j) = -negD0(i, j);
113 return matmul(inverse(negD0), m.D1);
114}
115
116/** Raw moment of order k of the inter-arrival time: k! pie (-D0)^-k e. */
117template <class T>
118T map_moment(const Map<T>& m, unsigned k) {
119 if (k == 0) return num_traits<T>::from_int(1);
120 Matrix<T> negD0 = m.D0;
121 for (std::size_t i = 0; i < negD0.rows(); ++i)
122 for (std::size_t j = 0; j < negD0.cols(); ++j) negD0(i, j) = -negD0(i, j);
123 const Matrix<T> A = matpow(inverse(negD0), k);
124 const std::vector<T> x = map_pie(m);
125 const std::vector<T> xA = vecmul(x, A);
127 for (const T& v : xA) s += v;
128 return num_factorial<T>(k) * s;
129}
130
131/** Variance of the inter-arrival time. */
132template <class T>
133T map_var(const Map<T>& m) {
134 const T mu = map_mean(m);
135 return map_moment(m, 2) - mu * mu;
136}
137
138/** Squared coefficient of variation. */
139template <class T>
140T map_scv(const Map<T>& m) {
141 const T mu = map_mean(m);
142 return map_var(m) / (mu * mu);
143}
144
145/**
146 * Autocorrelation coefficients of the inter-arrival times at the given lags,
147 *
148 * rho_k = (x P^k y - 1) / scv, x = lambda pi, y = (-D0)^-1 e,
149 *
150 * which is matlab/lib/kpctoolbox/map/map_acf.m in full, closing line included.
151 *
152 * The closing normalization USED TO BE MISSING here, and the raw kpctoolbox
153 * quantity x P^k y was returned instead. That is not an autocorrelation
154 * coefficient: it is 1, not 0, for a renewal process, and it is unbounded
155 * above rather than confined to [-1, 1]. On the two-class MMAP of
156 * tests/test_qbd_family.cpp the unnormalized form gave 1.13166 where MATLAB
157 * gives 0.0739911109309197, and every renewal MAP read 1 where MATLAB reads 0
158 * (checked directly: map_acf({[-1]},{[1]}, [1 2]) is [0 0] in MATLAB, and the
159 * raw quantity is 1). The ratio rho_{k+1}/rho_k that amap2_adjust_gamma uses
160 * as its decay characteristic was correspondingly wrong, since scv cancels in
161 * the ratio but the -1 does not: MATLAB's FGAMMA is (raw_4 - 1)/(raw_3 - 1)
162 * and the port was computing raw_4/raw_3.
163 *
164 * Exact in rational arithmetic: the normalization is one subtraction and one
165 * division, so a renewal MAP returns identically zero rather than 1e-16.
166 */
167template <class T>
168std::vector<T> map_acf(const Map<T>& m, const std::vector<unsigned>& lags) {
169 const Matrix<T> P = map_embedded(m);
170 const T lam = map_lambda(m);
171 std::vector<T> x = map_prob(m);
172 for (T& v : x) v *= lam;
173 Matrix<T> negD0 = m.D0;
174 for (std::size_t i = 0; i < negD0.rows(); ++i)
175 for (std::size_t j = 0; j < negD0.cols(); ++j) negD0(i, j) = -negD0(i, j);
176 const std::vector<T> y = mulvec(inverse(negD0), ones<T>(m.order()));
177
178 const T scv = map_scv(m);
179 if (scv == num_traits<T>::from_int(0))
180 throw NumericError("map_acf: the inter-arrival time is deterministic, the autocorrelation "
181 "coefficient is undefined (zero variance)");
182
183 std::vector<T> out;
184 out.reserve(lags.size());
185 for (unsigned lag : lags) {
186 const std::vector<T> xP = vecmul(x, matpow(P, lag));
188 for (std::size_t i = 0; i < xP.size(); ++i) s += xP[i] * y[i];
189 out.push_back(T((s - num_traits<T>::from_int(1)) / scv));
190 }
191 return out;
192}
193
194/** Index of dispersion for counts, I = 1 + 2(lambda - pie (Q + e pi)^-1 D1 e). */
195template <class T>
196T map_idc(const Map<T>& m) {
197 const std::size_t n = m.order();
198 const Matrix<T> Q = map_infgen(m);
199 const std::vector<T> p = map_prob(m);
200 Matrix<T> A(n, n);
201 for (std::size_t i = 0; i < n; ++i)
202 for (std::size_t j = 0; j < n; ++j) A(i, j) = Q(i, j) + p[j]; // Q + e pi
203 const std::vector<T> pie = map_pie(m);
204 const std::vector<T> t1 = vecmul(pie, inverse(A));
205 const std::vector<T> t2 = vecmul(t1, m.D1);
207 for (const T& v : t2) s += v;
209}
210
211/** Two-phase MAP constructor for a Poisson process of rate lambda. */
212template <class T>
213Map<T> map_exponential(const T& lambda) {
214 Map<T> m;
215 m.D0 = Matrix<T>(1, 1);
216 m.D1 = Matrix<T>(1, 1);
217 m.D0(0, 0) = -lambda;
218 m.D1(0, 0) = lambda;
219 return m;
220}
221
222} // namespace mam
223} // namespace line
224
225#endif // LINE_API_MAM_MAP_MOMENT_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
Steady-state distribution of a continuous-time Markov chain.
The exception types the port throws.
Dense linear algebra over the templated number type: products, identity, inverse, and powers.
Dense matrix and non-owning view.
std::vector< T > map_acf(const Map< T > &m, const std::vector< unsigned > &lags)
Autocorrelation coefficients of the inter-arrival times at the given lags,.
Definition map_moment.h:168
T map_idc(const Map< T > &m)
Index of dispersion for counts, I = 1 + 2(lambda - pie (Q + e pi)^-1 D1 e).
Definition map_moment.h:196
Matrix< T > map_infgen(const Map< T > &m)
Generator of the underlying phase process, D0 + D1.
Definition map_moment.h:62
T map_mean(const Map< T > &m)
Mean inter-arrival time, 1/lambda.
Definition map_moment.h:101
T map_var(const Map< T > &m)
Variance of the inter-arrival time.
Definition map_moment.h:133
int map_feastol()
Tolerance exponent shared by the KPC feasibility checks (map_feastol.m).
Definition map_moment.h:49
Map< T > map_exponential(const T &lambda)
Two-phase MAP constructor for a Poisson process of rate lambda.
Definition map_moment.h:213
std::vector< T > map_prob(const Map< T > &m)
Stationary distribution of the phase process, pi (D0 + D1) = 0.
Definition map_moment.h:73
std::vector< T > map_pie(const Map< T > &m)
Phase distribution seen by an arriving job, pie = pi D1 / (pi D1 e).
Definition map_moment.h:89
T map_scv(const Map< T > &m)
Squared coefficient of variation.
Definition map_moment.h:140
Matrix< T > map_embedded(const Map< T > &m)
Embedded DTMC at arrival epochs, P = (-D0)^-1 D1.
Definition map_moment.h:109
T map_moment(const Map< T > &m, unsigned k)
Raw moment of order k of the inter-arrival time: k!
Definition map_moment.h:118
T map_lambda(const Map< T > &m)
Stationary arrival rate, lambda = pi D1 e.
Definition map_moment.h:79
std::vector< T > ctmc_solve(const Matrix< T > &Qin)
Steady-state distribution of a continuous-time Markov chain.
Definition ctmc_solve.h:122
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
std::vector< T > mulvec(const Matrix< T > &A, const std::vector< T > &v)
Matrix times column vector, A v.
Definition linalg.h:62
Matrix< T > matpow(const Matrix< T > &A, unsigned k)
Integer matrix power, by repeated squaring.
Definition linalg.h:89
std::vector< T > ones(std::size_t n)
Column vector of ones, the ubiquitous e in MAP algebra.
Definition linalg.h:104
Number-type abstraction for the templated API port.
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