LINE Solver (C++)
Templated C++ port of the LINE queueing solver
Loading...
Searching...
No Matches
cache_lrum_map_levelstats.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_CACHE_LRUM_MAP_LEVELSTATS_H
6#define LINE_API_CACHE_LRUM_MAP_LEVELSTATS_H
7
8/**
9 * @file
10 * @ingroup api_cache
11 * Level statistics of one item's embedded (list, phase) chain in the
12 * LRU(m)-MAP TTL approximation.
13 *
14 * Templated port of matlab/src/api/cache/cache_lrum_map_levelstats.m,
15 * cross-checked against the levelStats method of
16 * jar/src/main/java/jline/api/cache/Cache_t_lrum_map.java. Evaluates eqs.
17 * (5)-(9) of Gast and Van Houdt, Performance Evaluation 2017, for an item
18 * whose request process is the MAP (D0, D1) and whose lists have
19 * characteristic times T(1..h):
20 *
21 * E_l = exp(D0 T_l), N_l = (I - E_l)(-D0)^-1, A_l = N_l D1,
22 * A_0 = (-D0)^-1 D1, N_0 = (-D0)^-1,
23 * R_h = A_{h-1} (I - A_h)^-1,
24 * R_l = A_{l-1} (I - R_{l+1} E_{l+1})^-1, l < h (A_0 for l = 1),
25 * pi_0 = pi_0 R_1 E_1, pi_l = pi_{l-1} R_l,
26 *
27 * with the level-0 boundary vector pi_0 normalized to sum one. The reported
28 * probabilities weight each level by its mean holding time pi_l N_l e, and the
29 * hit fractions divide the hit throughput of each list by the item's
30 * stationary request rate pi D1 e.
31 *
32 * PERRON VECTOR WITHOUT AN EIGENDECOMPOSITION: MATLAB computes pi_0 with
33 * eig(M') and picks the eigenvector of largest real part; the JAR runs power
34 * iteration. Neither is needed. M = R_1 E_1 has Perron root exactly one -- it
35 * is the transition matrix of the embedded chain of returns to level 0, so it
36 * is stochastic in the (list, phase) sense -- and this was verified in MATLAB
37 * on the reference instances, where max(real(eig(M'))) = 1 to 1e-15 at
38 * characteristic times both at and far from the capacity fixed point. So pi_0
39 * is the stationary vector of a stochastic matrix, i.e. dtmc_solve(M), which
40 * the port already solves EXACTLY by LU. The assumption is not taken on faith:
41 * the residual ||pi_0 (M - I)||_inf is checked and a NumericError is raised if
42 * the Perron root is not one, rather than silently returning the stationary
43 * vector of a matrix that does not have one.
44 *
45 * ARITHMETIC: exp(D0 T_l) is a tolerance-controlled approximation, so this
46 * requires transcendental arithmetic. Everything else (the inverses, pi_0, the
47 * stationary phase vector) is a linear solve and is exact.
48 *
49 * DIVERGENCE, MATLAB vs JAR: MATLAB uses one MAP (D0, D1) per item, shared by
50 * every list. The JAR signature takes a MatrixCell per item and reads a
51 * DIFFERENT (D0, D1) per list, D0c.get(l) and D1c.get(l), which is a strictly
52 * larger model and is not what cache_ttl_lrum_map.m builds or what the paper
53 * states. This port follows MATLAB.
54 */
55
56#include <cstddef>
57#include <vector>
58
62#include "line/num/number.h"
63#include "line/util/error.h"
64#include "line/util/expm.h"
65#include "line/util/linalg.h"
66#include "line/util/matrix.h"
67
68namespace line {
69namespace cache {
70
71/** Per-item level statistics, MATLAB's [prob, occ, hitfrac]. */
72template <class T>
74 std::vector<T> prob; ///< (h+1) time-stationary probability of level 0..h
75 std::vector<T> occ; ///< (h) occupancy of lists 1..h, prob(2:end)
76 std::vector<T> hitfrac; ///< (h) fraction of the item's requests hitting in list l
77};
78
79/**
80 * @brief Level statistics of one item's embedded (list, phase) chain in the
81 * LRU(m)-MAP TTL approximation.
82 *
83 * @param D0 (d x d) hidden-transition matrix of the item's request MAP
84 * @param D1 (d x d) arrival matrix of the item's request MAP
85 * @param Tv (h) characteristic times, all positive
86 */
87template <class T>
89 const std::vector<T>& Tv) {
91 "cache_lrum_map_levelstats requires transcendental arithmetic");
92 const std::size_t d = D0.rows();
93 const std::size_t h = Tv.size();
94 if (d == 0 || D0.cols() != d || D1.rows() != d || D1.cols() != d)
95 throw InputError("cache_lrum_map_levelstats: D0 and D1 must be square and of equal size");
96 if (h == 0) throw InputError("cache_lrum_map_levelstats: no characteristic times");
97 const T zero = num_traits<T>::from_int(0);
98 for (std::size_t l = 0; l < h; ++l)
99 if (!(Tv[l] > zero))
100 throw InputError("cache_lrum_map_levelstats: characteristic times must be positive");
101
102 Matrix<T> negD0 = D0;
103 for (std::size_t i = 0; i < d; ++i)
104 for (std::size_t j = 0; j < d; ++j) negD0(i, j) = -D0(i, j);
105 const Matrix<T> iD0 = inverse(negD0);
106 const Matrix<T> I = eye<T>(d);
107
108 std::vector<Matrix<T>> E(h), N(h), A(h);
109 for (std::size_t l = 0; l < h; ++l) {
110 E[l] = expm(D0, Tv[l]);
111 Matrix<T> IE(d, d);
112 for (std::size_t i = 0; i < d; ++i)
113 for (std::size_t j = 0; j < d; ++j) IE(i, j) = I(i, j) - E[l](i, j);
114 N[l] = matmul(IE, iD0);
115 A[l] = matmul(N[l], D1);
116 }
117 const Matrix<T> A0 = matmul(iD0, D1);
118 const Matrix<T>& N0 = iD0;
119
120 // R recursion, eqs. (6)-(7); R[l] is the paper's R_{l+1}.
121 std::vector<Matrix<T>> R(h);
122 for (std::size_t li = h; li-- > 0;) {
123 Matrix<T> lhs; // the numerator, A_{l-1} or A_0
124 Matrix<T> inner(d, d);
125 if (li + 1 == h) {
126 lhs = (h == 1) ? A0 : A[li - 1];
127 for (std::size_t i = 0; i < d; ++i)
128 for (std::size_t j = 0; j < d; ++j) inner(i, j) = I(i, j) - A[li](i, j);
129 } else {
130 lhs = (li == 0) ? A0 : A[li - 1];
131 const Matrix<T> RE = matmul(R[li + 1], E[li + 1]);
132 for (std::size_t i = 0; i < d; ++i)
133 for (std::size_t j = 0; j < d; ++j) inner(i, j) = I(i, j) - RE(i, j);
134 }
135 R[li] = matmul(lhs, inverse(inner));
136 }
137
138 // pi_0: stationary vector of the stochastic matrix M = R_1 E_1.
139 const Matrix<T> M = matmul(R[0], E[0]);
140 const std::vector<T> pi0 = mc::dtmc_solve(M);
141 {
142 const std::vector<T> pM = vecmul(pi0, M);
143 T res = zero;
144 for (std::size_t i = 0; i < d; ++i) {
145 const T e = num_abs(T(pM[i] - pi0[i]));
146 if (e > res) res = e;
147 }
148 if (num_traits<T>::to_double(res) > 1e-8)
149 throw NumericError(
150 "cache_lrum_map_levelstats: R_1 exp(D0 T_1) has no unit Perron root, so the "
151 "level-0 balance equation has no stochastic solution");
152 }
153
154 std::vector<std::vector<T>> pih(h);
155 pih[0] = vecmul(pi0, R[0]);
156 for (std::size_t l = 1; l < h; ++l) pih[l] = vecmul(pih[l - 1], R[l]);
157
158 std::vector<T> holding(h + 1, zero);
159 {
160 const std::vector<T> v = vecmul(pi0, N0);
161 for (std::size_t i = 0; i < d; ++i) holding[0] += v[i];
162 }
163 for (std::size_t l = 0; l < h; ++l) {
164 const std::vector<T> v = vecmul(pih[l], N[l]);
165 for (std::size_t i = 0; i < d; ++i) holding[1 + l] += v[i];
166 }
167 T denom = zero;
168 for (std::size_t l = 0; l <= h; ++l) denom += holding[l];
169 if (denom == zero) throw NumericError("cache_lrum_map_levelstats: zero total holding time");
170
172 out.prob.resize(h + 1);
173 for (std::size_t l = 0; l <= h; ++l) out.prob[l] = holding[l] / denom;
174 out.occ.assign(out.prob.begin() + 1, out.prob.end());
175
176 // Request-weighted hit fractions: hit throughput of list l over the item's
177 // stationary request rate.
178 const mam::Map<T> item{D0, D1};
179 const T lam = mam::map_lambda(item);
180 out.hitfrac.assign(h, zero);
181 if (lam > zero) {
182 for (std::size_t l = 0; l < h; ++l) {
183 const std::vector<T> v = vecmul(vecmul(pih[l], N[l]), D1);
184 T s = zero;
185 for (std::size_t i = 0; i < d; ++i) s += v[i];
186 out.hitfrac[l] = s / denom / lam;
187 }
188 }
189 return out;
190}
191
192} // namespace cache
193} // namespace line
194
195#endif // LINE_API_CACHE_LRUM_MAP_LEVELSTATS_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.
Equilibrium distribution of a discrete-time Markov chain, and stochastic complementation.
The exception types the port throws.
Matrix exponential by scaling and squaring with a diagonal Pade approximant.
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...
Dense matrix and non-owning view.
CacheLrumMapLevelStats< T > cache_lrum_map_levelstats(const Matrix< T > &D0, const Matrix< T > &D1, const std::vector< T > &Tv)
Level statistics of one item's embedded (list, phase) chain in the LRU(m)-MAP TTL approximation.
T map_lambda(const Map< T > &m)
Stationary arrival rate, lambda = pi D1 e.
Definition map_moment.h:79
std::vector< T > dtmc_solve(const Matrix< T > &P)
Stationary distribution of a stochastic matrix P.
Definition dtmc_solve.h:106
T num_abs(const T &v)
Definition number.h:172
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 > eye(std::size_t n)
Identity of order n.
Definition linalg.h:28
Matrix< T > expm(const Matrix< T > &A)
Matrix exponential exp(A).
Definition expm.h:141
Number-type abstraction for the templated API port.
Per-item level statistics, MATLAB's [prob, occ, hitfrac].
std::vector< T > hitfrac
(h) fraction of the item's requests hitting in list l
std::vector< T > occ
(h) occupancy of lists 1..h, prob(2:end)
std::vector< T > prob
(h+1) time-stationary probability of level 0..h
A MAP as the pair of matrices (D0, D1).
Definition map_moment.h:53