LINE Solver (C++)
Templated C++ port of the LINE queueing solver
Loading...
Searching...
No Matches
cache_gamma_lp.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_GAMMA_LP_H
6#define LINE_API_CACHE_GAMMA_LP_H
7
8/**
9 * @file
10 * @ingroup api_cache
11 * Access factors of a tree-structured multi-list cache.
12 *
13 * Templated port of matlab/src/api/cache/cache_gamma_lp.m, cross-checked
14 * against jar/src/main/java/jline/api/cache/Cache_gamma_lp.java.
15 *
16 * The lists of the cache form a tree rooted at node 0 ("not cached"), list l
17 * being node l+1. The access factor gamma(i,l) of item i at list l is the
18 * product, along the unique path from the root to node l+1, of the aggregate
19 * request flow crossing each edge:
20 *
21 * gamma(i,l) = prod_{edges (a,b) of the path} sum_v sum_{t<=a} lambda(v,i,t) R{v,i}(a,b)
22 *
23 * The path is recovered by walking up from node l+1 through the parent
24 * relation, the parent of a node being the unique earlier node with a nonzero
25 * routing probability into it; more than one parent means the structure is not
26 * a tree and is an error, as in both reference implementations.
27 *
28 * Only sums and products, so this instantiates at exact arithmetic and the
29 * access factors of a rational model are exact rationals.
30 *
31 * REFERENCE DEFECT (both codebases): the `isempty(Pij)` branch that sets
32 * gamma(i,l) = 0 is unreachable, because Pij is seeded with the node itself
33 * and so is never empty. A list disconnected from the root therefore does not
34 * yield a zero access factor; it yields the empty product 1. Reproduced here
35 * so that the three codebases agree, and flagged rather than fixed.
36 */
37
38#include <cstddef>
39#include <vector>
40
41#include "line/num/number.h"
42#include "line/util/error.h"
43#include "line/util/matrix.h"
44
45namespace line {
46namespace cache {
47
48/** Return value of cache_gamma_lp, mirroring [gamma,u,n,h]. */
49template <class T>
51 Matrix<T> gamma; ///< (n x h) access factors
52 std::size_t u; ///< number of user streams
53 std::size_t n; ///< number of items
54 std::size_t h; ///< number of lists
55 /** Parent list of each list, 0-based, -1 for lists rooted in the miss list. */
56 std::vector<int> parent;
57};
58
59namespace detail {
60
61/** Unique node a < j with R(a,j) != 0, or -1 when j is a root. */
62template <class T>
63int cache_parent(const Matrix<T>& R, std::size_t j) {
64 const T zero = num_traits<T>::from_int(0);
65 int parent = -1;
66 for (std::size_t i = 0; i < j; ++i) {
67 if (R(i, j) != zero) {
68 if (parent >= 0)
69 throw InputError(
70 "cache_gamma_lp: a cache list has more than one parent, but the structure must "
71 "be a tree");
72 parent = static_cast<int>(i);
73 }
74 }
75 return parent;
76}
77
78} // namespace detail
79
80/**
81 * @brief Access factors of a tree-structured multi-list cache.
82 *
83 * @param lambda (u) matrices of size (n x (h+1)): lambda[v](i,t) is the rate at
84 * which user v requests item i while it sits at node t
85 * @param R (u x n) routing matrices of size ((h+1) x (h+1))
86 */
87template <class T>
88CacheGammaResult<T> cache_gamma_lp(const std::vector<Matrix<T>>& lambda,
89 const std::vector<std::vector<Matrix<T>>>& R) {
90 if (lambda.empty()) throw InputError("cache_gamma_lp: no user streams");
91 const std::size_t u = lambda.size();
92 const std::size_t n = lambda[0].rows();
93 if (lambda[0].cols() == 0) throw InputError("cache_gamma_lp: empty lambda");
94 const std::size_t h = lambda[0].cols() - 1;
95 if (R.size() != u) throw InputError("cache_gamma_lp: R and lambda disagree on the user count");
96 for (std::size_t v = 0; v < u; ++v)
97 if (R[v].size() != n)
98 throw InputError("cache_gamma_lp: R and lambda disagree on the item count");
99
100 const T zero = num_traits<T>::from_int(0);
101 const T one = num_traits<T>::from_int(1);
102 Matrix<T> gamma(n, h, zero);
103
104 for (std::size_t i = 0; i < n; ++i) {
105 // Rvi: routing of item i aggregated over users, used only for the tree
106 // structure (the reference sums the matrices before taking parents).
107 Matrix<T> Rvi(R[0][i].rows(), R[0][i].cols(), zero);
108 for (std::size_t v = 0; v < u; ++v)
109 for (std::size_t a = 0; a < Rvi.rows(); ++a)
110 for (std::size_t b = 0; b < Rvi.cols(); ++b) Rvi(a, b) += R[v][i](a, b);
111
112 for (std::size_t l = 0; l < h; ++l) {
113 std::vector<std::size_t> path;
114 path.push_back(l + 1);
115 int pr = detail::cache_parent(Rvi, l + 1);
116 while (pr >= 0) {
117 path.insert(path.begin(), static_cast<std::size_t>(pr));
118 pr = detail::cache_parent(Rvi, static_cast<std::size_t>(pr));
119 }
120
121 T g = one;
122 for (std::size_t li = 1; li < path.size(); ++li) {
123 const std::size_t a = path[li - 1];
124 const std::size_t b = path[li];
125 T y = zero;
126 for (std::size_t v = 0; v < u; ++v)
127 for (std::size_t t = 0; t <= a; ++t) y += lambda[v](i, t) * R[v][i](t, b);
128 g *= y;
129 }
130 gamma(i, l) = g;
131 }
132 }
133
134 // Tree structure of the lists, read off item 0's routing matrix aggregated
135 // over users -- the same matrix the gamma loop walks.
136 Matrix<T> Rtot(R[0][0].rows(), R[0][0].cols(), zero);
137 for (std::size_t v = 0; v < u; ++v)
138 for (std::size_t a = 0; a < Rtot.rows(); ++a)
139 for (std::size_t b = 0; b < Rtot.cols(); ++b) Rtot(a, b) += R[v][0](a, b);
140 std::vector<int> parent(h, -1);
141 for (std::size_t l = 0; l < h; ++l) {
142 const int pr = detail::cache_parent(Rtot, l + 1);
143 parent[l] = (pr < 0) ? -1 : (pr - 1); // list indices, -1 = miss list
144 }
145
147 r.gamma = gamma;
148 r.u = u;
149 r.n = n;
150 r.h = h;
151 r.parent = parent;
152 return r;
153}
154
155} // namespace cache
156} // namespace line
157
158#endif // LINE_API_CACHE_GAMMA_LP_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
The exception types the port throws.
Dense matrix and non-owning view.
CacheGammaResult< T > cache_gamma_lp(const std::vector< Matrix< T > > &lambda, const std::vector< std::vector< Matrix< T > > > &R)
Access factors of a tree-structured multi-list cache.
Number-type abstraction for the templated API port.
Return value of cache_gamma_lp, mirroring [gamma,u,n,h].
std::size_t n
number of items
std::size_t u
number of user streams
std::vector< int > parent
Parent list of each list, 0-based, -1 for lists rooted in the miss list.
Matrix< T > gamma
(n x h) access factors
std::size_t h
number of lists