LINE Solver (C++)
Templated C++ port of the LINE queueing solver
Loading...
Searching...
No Matches
aoi_dist2ph.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_AOI_AOI_DIST2PH_H
6#define LINE_API_AOI_AOI_DIST2PH_H
7
8/**
9 * @file
10 * @ingroup api_aoi
11 * MAP to PH conversion for the age-of-information solvers.
12 *
13 * Templated port of matlab/src/api/aoi/aoi_dist2ph.m. The (D0, D1) pair of a
14 * MAP is turned into the (alpha, T) pair the aoi_* algorithms consume:
15 *
16 * T = D0, the sub-generator, unchanged
17 * theta solves theta (D0 + D1) = 0, sum theta = 1
18 * alpha = theta .* (D1 e) normalized, the phase distribution just after a
19 * completion, weighted by where completions actually occur
20 *
21 * alpha is NOT map_pie. map_pie is theta D1 / (theta D1 e), the embedded
22 * arrival chain's stationary vector, which redistributes the mass through the
23 * columns of D1; the reference instead keeps the mass in the phase it left
24 * from, alpha_i proportional to theta_i (D1 e)_i. For a renewal MAP with
25 * D1 = d pi (rank one) the two coincide; for a general MAP they do not, and
26 * this port reproduces the reference rather than substituting map_pie.
27 *
28 * ARITHMETIC. One linear solve, one normalization and sign checks, so this is
29 * exact at T = Rational and is instantiated there. MATLAB obtains theta from
30 * the overdetermined [Q'; e'] \ [0; 1], which its backslash resolves by QR;
31 * on a consistent system that is the exact solution, and the port reaches the
32 * same solution through the normal equations, which is a square solve the
33 * templated LU can carry exactly.
34 *
35 * The generator repair of the reference is kept: when the rows of D0 + D1 do
36 * not sum to zero to within 1e-10, the diagonal is corrected so that they do.
37 * That test is a comparison against a constant and needs no transcendental
38 * function, so it carries over verbatim to the exact instantiation, where it
39 * is essentially never triggered.
40 */
41
42#include <cstddef>
43#include <vector>
44
46#include "line/num/number.h"
47#include "line/util/error.h"
48#include "line/util/linalg.h"
49#include "line/util/lu.h"
50#include "line/util/matrix.h"
51
52namespace line {
53namespace aoi {
54
55/** The (alpha, T) PH pair produced by aoi_dist2ph. */
56template <class T>
57struct AoiPh {
58 std::vector<T> alpha; ///< initial probability vector, sums to one
59 Matrix<T> Tmat; ///< sub-generator, MATLAB's T
60};
61
62/**
63 * Convert a MAP (D0, D1) into the PH pair (alpha, T).
64 *
65 * @param D0 hidden generator
66 * @param D1 arrival matrix
67 */
68template <class T>
69AoiPh<T> aoi_dist2ph(const Matrix<T>& D0, const Matrix<T>& D1) {
70 const std::size_t n = D0.rows();
71 if (n == 0) throw InputError("aoi_dist2ph: empty process");
72 if (D0.cols() != n || D1.rows() != n || D1.cols() != n)
73 throw InputError("aoi_dist2ph: D0 and D1 must be square matrices of the same size");
74
75 const T zero = num_traits<T>::from_int(0), one = num_traits<T>::from_int(1);
76 const T genTol = num_traits<T>::from_double(1e-10);
77
78 AoiPh<T> out;
79 out.Tmat = D0;
80
81 Matrix<T> Q(n, n);
82 for (std::size_t i = 0; i < n; ++i)
83 for (std::size_t j = 0; j < n; ++j) Q(i, j) = D0(i, j) + D1(i, j);
84
85 // Repair the generator when the row sums do not vanish (reference behaviour).
86 T worst = zero;
87 std::vector<T> rowSum(n, zero);
88 for (std::size_t i = 0; i < n; ++i) {
89 for (std::size_t j = 0; j < n; ++j) rowSum[i] += Q(i, j);
90 const T a = rowSum[i] < zero ? T(-rowSum[i]) : rowSum[i];
91 if (a > worst) worst = a;
92 }
93 if (worst > genTol)
94 for (std::size_t i = 0; i < n; ++i) Q(i, i) = Q(i, i) - rowSum[i];
95
96 // theta Q = 0, sum theta = 1, through the normal equations of
97 // A theta' = b with A = [Q'; e'] and b = [0; 1].
98 Matrix<T> AtA(n, n, zero);
99 std::vector<T> Atb(n, one); // (A' b)_i = 1, the last row of A being e'
100 for (std::size_t i = 0; i < n; ++i)
101 for (std::size_t j = 0; j < n; ++j) {
102 T s = one; // contribution of the normalization row
103 for (std::size_t k = 0; k < n; ++k) s += Q(i, k) * Q(j, k);
104 AtA(i, j) = s;
105 }
106 std::vector<T> theta = solve(AtA, Atb);
107
108 T mass = zero;
109 for (std::size_t i = 0; i < n; ++i) {
110 if (theta[i] < zero) theta[i] = zero;
111 mass += theta[i];
112 }
113 if (mass == zero) throw NumericError("aoi_dist2ph: the phase process has no stationary law");
114 for (std::size_t i = 0; i < n; ++i) theta[i] = theta[i] / mass;
115
116 std::vector<T> completion(n, zero);
117 for (std::size_t i = 0; i < n; ++i)
118 for (std::size_t j = 0; j < n; ++j) completion[i] += D1(i, j);
119
120 out.alpha.assign(n, zero);
121 T num = zero;
122 for (std::size_t i = 0; i < n; ++i) {
123 out.alpha[i] = theta[i] * completion[i];
124 num += out.alpha[i];
125 }
126 if (num > zero) {
127 for (std::size_t i = 0; i < n; ++i) out.alpha[i] = out.alpha[i] / num;
128 } else {
129 out.alpha = theta; // no completions anywhere: fall back on theta
130 T s = zero;
131 for (std::size_t i = 0; i < n; ++i) s += out.alpha[i];
132 if (s == zero) throw NumericError("aoi_dist2ph: alpha cannot be normalized");
133 for (std::size_t i = 0; i < n; ++i) out.alpha[i] = out.alpha[i] / s;
134 }
135
136 for (std::size_t i = 0; i < n; ++i)
137 if (out.Tmat(i, i) > zero)
138 throw InputError("aoi_dist2ph: T has a positive diagonal entry, which is not a "
139 "sub-generator");
140 return out;
141}
142
143/** Convenience overload taking the MAP as line::mam::Map. */
144template <class T>
146 return aoi_dist2ph(m.D0, m.D1);
147}
148
149} // namespace aoi
150} // namespace line
151
152#endif // LINE_API_AOI_AOI_DIST2PH_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
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.
AoiPh< T > aoi_dist2ph(const Matrix< T > &D0, const Matrix< T > &D1)
Convert a MAP (D0, D1) into the PH pair (alpha, T).
Definition aoi_dist2ph.h:69
std::vector< T > solve(const Matrix< T > &A, const std::vector< T > &b)
Convenience: solve Ax = b, leaving A and b untouched.
Definition lu.h:158
Number-type abstraction for the templated API port.
The (alpha, T) PH pair produced by aoi_dist2ph.
Definition aoi_dist2ph.h:57
Matrix< T > Tmat
sub-generator, MATLAB's T
Definition aoi_dist2ph.h:59
std::vector< T > alpha
initial probability vector, sums to one
Definition aoi_dist2ph.h:58
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