LINE Solver (C++)
Templated C++ port of the LINE queueing solver
Loading...
Searching...
No Matches
cache_miss.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_MISS_H
6#define LINE_API_CACHE_MISS_H
7
8/**
9 * @file
10 * @ingroup api_cache
11 * Exact cache miss rates from the recursive normalizing constant.
12 *
13 * Templated port of matlab/src/api/cache/cache_miss.m, cross-checked against
14 * jar/src/main/java/jline/api/cache/Cache_miss.java.
15 *
16 * Two quantities are computed, both as ratios of cache_erec constants:
17 * M = E(gamma, m + e_1) / E(gamma, m), the global miss rate; and
18 * pi0(k) = E(gamma without item k, m) / E(gamma, m), the probability that
19 * item k is absent from the cache,
20 * from which the per-user rate MU(v) = sum_k lambda(v,k) pi0(k) and the
21 * per-item rate MI(k) = (sum_v lambda(v,k)) pi0(k) follow.
22 *
23 * Pure field operations throughout, so the exact instantiation returns rates
24 * with no rounding.
25 *
26 * DIVERGENCE, MATLAB vs JAR: MATLAB conditions on the absence of item k by
27 * deleting ROW k of gamma, gamma(setdiff(1:n,k),:) -- gamma is item-by-list,
28 * so a row is an item. The JAR's Cache_miss deletes COLUMN k instead (it
29 * builds gammaWithoutK by iterating over gamma.getNumCols() and skipping
30 * j == k), which removes a cache LIST, not an item, and additionally reads the
31 * item count as lambda.getNumCols() while indexing gamma by it. The JAR's
32 * pi0/MU/MI are therefore wrong whenever h != n, and meaningless in general.
33 * This port follows MATLAB.
34 */
35
36#include <cstddef>
37#include <vector>
38
40#include "line/num/number.h"
41#include "line/util/error.h"
42#include "line/util/matrix.h"
43
44namespace line {
45namespace cache {
46
47/** Return value of cache_miss, mirroring [M,MU,MI,pi0]. */
48template <class T>
50 T M; ///< global miss rate
51 std::vector<T> MU; ///< (u) per-user miss rate; empty when no lambda given
52 std::vector<T> MI; ///< (n) per-item miss rate; empty when no lambda given
53 std::vector<T> pi0; ///< (n) per-item miss probability; empty when no lambda given
54};
55
56/**
57 * @brief Exact cache miss rates from the recursive normalizing constant.
58 *
59 * @param gamma (n x h) access factors
60 * @param m (h) list capacities
61 * @param lambda (u x n) per-user per-item request rates, MATLAB's
62 * lambda(:,:,1); pass an empty matrix for the miss rate alone
63 */
64template <class T>
65CacheMissResult<T> cache_miss(const Matrix<T>& gamma, const std::vector<int>& m,
66 const Matrix<T>& lambda) {
67 if (gamma.cols() != m.size())
68 throw InputError("cache_miss: gamma and m disagree on the number of lists");
69 if (m.empty()) throw InputError("cache_miss: empty capacity vector");
70
71 std::vector<int> ma = m;
72 ma[0] += 1;
73
74 const T Em = cache_erec(gamma, m);
75 if (Em == num_traits<T>::from_int(0))
76 throw NumericError(ErrorKind{"Degenerate"}, "cache_miss: the normalizing constant is zero");
77
79 r.M = cache_erec(gamma, ma) / Em;
80 if (lambda.empty()) return r;
81
82 const std::size_t u = lambda.rows();
83 const std::size_t n = gamma.rows();
84 if (lambda.cols() != n)
85 throw InputError("cache_miss: lambda and gamma disagree on the number of items");
86
87 r.pi0.assign(n, num_traits<T>::from_int(0));
88 for (std::size_t k = 0; k < n; ++k)
89 r.pi0[k] = cache_erec(detail::gamma_without_row(gamma, k), m) / Em;
90
91 r.MU.assign(u, num_traits<T>::from_int(0));
92 for (std::size_t v = 0; v < u; ++v)
93 for (std::size_t k = 0; k < n; ++k) r.MU[v] += lambda(v, k) * r.pi0[k];
94
95 r.MI.assign(n, num_traits<T>::from_int(0));
96 for (std::size_t k = 0; k < n; ++k) {
98 for (std::size_t v = 0; v < u; ++v) s += lambda(v, k);
99 r.MI[k] = s * r.pi0[k];
100 }
101 return r;
102}
103
104/** Overload without request rates: only the global miss rate is defined. */
105template <class T>
106CacheMissResult<T> cache_miss(const Matrix<T>& gamma, const std::vector<int>& m) {
107 return cache_miss(gamma, m, Matrix<T>());
108}
109
110/**
111 * @brief Cost-capped overload, MATLAB's cache_miss(gamma, m, lambda, sigma, cap):
112 * every normalizing constant is the cost-constrained cache_erec(gamma, m,
113 * sigma, cap), and the per-item constants drop item k from sigma as well.
114 * Empty sigma or cap falls back to the unconstrained form, as in the reference.
115 *
116 * @param sigma (n) per-item storage costs
117 * @param cap (h) per-list storage cost caps
118 */
119template <class T>
120CacheMissResult<T> cache_miss(const Matrix<T>& gamma, const std::vector<int>& m,
121 const Matrix<T>& lambda, const std::vector<int>& sigma,
122 const std::vector<int>& cap) {
123 if (sigma.empty() || cap.empty()) return cache_miss(gamma, m, lambda);
124 if (gamma.cols() != m.size())
125 throw InputError("cache_miss: gamma and m disagree on the number of lists");
126 if (m.empty()) throw InputError("cache_miss: empty capacity vector");
127 if (sigma.size() != gamma.rows())
128 throw InputError("cache_miss: sigma and gamma disagree on the number of items");
129
130 std::vector<int> ma = m;
131 ma[0] += 1;
132
133 const T Em = cache_erec(gamma, m, sigma, cap);
134 if (Em == num_traits<T>::from_int(0))
135 throw NumericError(ErrorKind{"Degenerate"}, "cache_miss: the normalizing constant is zero");
136
138 r.M = cache_erec(gamma, ma, sigma, cap) / Em;
139 if (lambda.empty()) return r;
140
141 const std::size_t u = lambda.rows();
142 const std::size_t n = gamma.rows();
143 if (lambda.cols() != n)
144 throw InputError("cache_miss: lambda and gamma disagree on the number of items");
145
146 r.pi0.assign(n, num_traits<T>::from_int(0));
147 for (std::size_t k = 0; k < n; ++k) {
148 std::vector<int> sigma_others;
149 sigma_others.reserve(n - 1);
150 for (std::size_t i = 0; i < n; ++i)
151 if (i != k) sigma_others.push_back(sigma[i]);
152 r.pi0[k] = cache_erec(detail::gamma_without_row(gamma, k), m, sigma_others, cap) / Em;
153 }
154
155 r.MU.assign(u, num_traits<T>::from_int(0));
156 for (std::size_t v = 0; v < u; ++v)
157 for (std::size_t k = 0; k < n; ++k) r.MU[v] += lambda(v, k) * r.pi0[k];
158
159 r.MI.assign(n, num_traits<T>::from_int(0));
160 for (std::size_t k = 0; k < n; ++k) {
162 for (std::size_t v = 0; v < u; ++v) s += lambda(v, k);
163 r.MI[k] = s * r.pi0[k];
164 }
165 return r;
166}
167
168/** Cost-capped overload without request rates. */
169template <class T>
170CacheMissResult<T> cache_miss(const Matrix<T>& gamma, const std::vector<int>& m,
171 const std::vector<int>& sigma, const std::vector<int>& cap) {
172 return cache_miss(gamma, m, Matrix<T>(), sigma, cap);
173}
174
175} // namespace cache
176} // namespace line
177
178#endif // LINE_API_CACHE_MISS_H
Exact recursive normalizing constant of a multi-list cache model.
InputError(const std::string &what)
Definition error.h:105
std::size_t cols() const
Definition matrix.h:90
std::size_t rows() const
Definition matrix.h:89
bool empty() const
Definition matrix.h:92
NumericError(const std::string &what)
Definition error.h:112
The exception types the port throws.
Dense matrix and non-owning view.
CacheMissResult< T > cache_miss(const Matrix< T > &gamma, const std::vector< int > &m, const Matrix< T > &lambda)
Exact cache miss rates from the recursive normalizing constant.
Definition cache_miss.h:65
T cache_erec(const Matrix< T > &gamma, const std::vector< int > &m)
Exact recursive normalizing constant of a multi-list cache model.
Definition cache_erec.h:201
Conservation laws of a layered queueing network, enumerated from its structure.
Definition aoi_dist2ph.h:52
Number-type abstraction for the templated API port.
The <kind> part of a LINE:<category>:<kind> identifier (see _kb/17-error-vocabulary....
Definition error.h:37
Return value of cache_miss, mirroring [M,MU,MI,pi0].
Definition cache_miss.h:49
std::vector< T > MI
(n) per-item miss rate; empty when no lambda given
Definition cache_miss.h:52
std::vector< T > MU
(u) per-user miss rate; empty when no lambda given
Definition cache_miss.h:51
std::vector< T > pi0
(n) per-item miss probability; empty when no lambda given
Definition cache_miss.h:53