LINE Solver (C++)
Templated C++ port of the LINE queueing solver
Loading...
Searching...
No Matches
cache_miss_pos_rmf.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_CACHE_MISS_POS_RMF_H
6#define LINE_API_CACHE_CACHE_MISS_POS_RMF_H
7
8/**
9 * @file
10 * @ingroup api_cache
11 * Position-resolved mean-field miss rates for FIFO(m) and strict FIFO(m).
12 *
13 * Templated port of `matlab/src/api/cache/cache_miss_fifo_rmf.m`,
14 * `cache_miss_sfifo_rmf.m` and the drift they share,
15 * `cache_pos_drift_graph.m`.
16 *
17 * WHY THESE EXIST AT ALL, GIVEN `cache_miss_rmf`. Gast and Van Houdt
18 * (SIGMETRICS 2015, Thm 1) prove pi_FIFO(m) = pi_RAND(m) EXACTLY, so on the
19 * linear access graph the FIFO STEADY STATE is served from the cheaper refined
20 * mean field of `cache_miss_rmf` and this file is not reached. Two things break
21 * that equality:
22 *
23 * THE TRANSIENT. FIFO evicts the deterministic tail -- residence is exactly
24 * m insertions -- while RANDOM evicts a uniformly drawn victim, so residence
25 * is geometric. H(inf) agrees; H(t) from a cold cache does not, and a
26 * trajectory read off the RANDOM drift would ramp at the wrong rate.
27 *
28 * A NON-LINEAR ACCESS GRAPH. The equality is proved for the linear chain. Once
29 * admission or promotion is item-dependent, the per-item per-list occupancy
30 * that RANDOM(m) tracks no longer determines the dynamics, and FIFO needs its
31 * own position-resolved state.
32 *
33 * Strict FIFO(m) is a THIRD policy, not a spelling of FIFO(m). Gast and Van
34 * Houdt show it differs from RANDOM(m) and give it no mean-field model. The
35 * difference is the within-list age ordering: on a hit at position j of list
36 * i < h the demoted tail of list i+1 is reinserted at position 1 of list i and
37 * positions 1..j-1 shift back (strict), whereas FIFO(m) drops it into the
38 * VACATED position j with no shift. That single choice is the `reinsert`
39 * parameter of the shared drift, and it is why strict FIFO(m) is never served
40 * from `cache_miss_rmf` even on the linear chain -- it degenerates to FIFO(m)
41 * only when m_1 = ... = m_{h-1} = 1, where there is no within-list order to
42 * disagree about.
43 *
44 * THE STATE. x[k,i,j] = P(item k occupies position j of list i), over the
45 * `sum(m)` in-cache slots only; the out-of-cache mass is the complement
46 * 1 - sum_{i,j} x[k,i,j], which is what `pos_out` returns and what the miss
47 * probability is read from. That complement is CLIPPED to [0,1] rather than
48 * asserted, exactly as the reference does: the mean-field trajectory can leave
49 * the simplex by an integration tolerance without the fixed point being wrong.
50 *
51 * THE INITIAL CONDITION DIFFERS BY PATH, and deliberately. The linear drift
52 * starts POPULARITY-ORDERED (the S most requested items pre-loaded, one per
53 * slot), which is near its own fixed point and integrates quickly. The general
54 * graph drift starts COLD (an empty cache), because a graph may make some items
55 * non-admissible and a pre-loaded non-admissible item has no outflow term that
56 * can drain it -- it would sit in the cache forever and report a hit rate the
57 * policy never produces.
58 */
59
60#include <algorithm>
61#include <cmath>
62#include <cstddef>
63#include <numeric>
64#include <string>
65#include <vector>
66
68#include "line/num/number.h"
69#include "line/util/error.h"
70#include "line/util/matrix.h"
71#include "line/util/ode.h"
72
73namespace line {
74namespace cache {
75
76/** Return value of the position-resolved routines, as CacheMissRmfResult. */
77template <class T>
79
80namespace pos_detail {
81
82/** The (list, position) slot map: `slots[s] = (i,j)` with i, j 1-based. */
83struct SlotMap {
84 std::vector<std::pair<std::size_t, std::size_t> > slots; ///< (list, position), 1-based
85 std::vector<std::vector<std::size_t> > sidx; ///< sidx[i-1][j-1] = s, 0-based
86 std::size_t S = 0;
87};
88
89inline SlotMap build_slots(const std::vector<int>& m) {
90 SlotMap sm;
91 const std::size_t h = m.size();
92 std::size_t mmax = 0;
93 for (std::size_t i = 0; i < h; ++i) mmax = std::max(mmax, static_cast<std::size_t>(m[i]));
94 sm.sidx.assign(h, std::vector<std::size_t>(mmax, 0));
95 for (std::size_t i = 1; i <= h; ++i)
96 for (int j = 1; j <= m[i - 1]; ++j) {
97 sm.slots.push_back(std::make_pair(i, static_cast<std::size_t>(j)));
98 sm.sidx[i - 1][static_cast<std::size_t>(j) - 1] = sm.slots.size() - 1;
99 }
100 sm.S = sm.slots.size();
101 return sm;
102}
103
104/** `(k-1)*S + sidx(i,j)` in 0-based form. */
105inline std::size_t kidx(std::size_t k, std::size_t i, std::size_t j, const SlotMap& sm) {
106 return k * sm.S + sm.sidx[i - 1][j - 1];
107}
108
109/** `fifo_out` / `sfifo_out`: out-of-cache occupancy of item k, clipped. */
110template <class T>
111T pos_out(const std::vector<T>& x, std::size_t k, const SlotMap& sm) {
112 const T zero = num_traits<T>::from_int(0), one = num_traits<T>::from_int(1);
113 T acc = zero;
114 for (std::size_t s = 0; s < sm.S; ++s) acc += x[k * sm.S + s];
115 T o = one - acc;
116 if (o < zero) o = zero;
117 if (o > one) o = one;
118 return o;
119}
120
121/** Total in-cache occupancy of item k over the whole of list i. */
122template <class T>
123T pos_list_occ(const std::vector<T>& x, std::size_t k, std::size_t i, const std::vector<int>& m,
124 const SlotMap& sm) {
125 T acc = num_traits<T>::from_int(0);
126 for (int j = 1; j <= m[i - 1]; ++j) acc += x[kidx(k, i, static_cast<std::size_t>(j), sm)];
127 return acc;
128}
129
130/** `sfifo_gi` / `pos_gg`: the aggregate rate at positions DEEPER than jp. */
131template <class T>
132T pos_deeper(const Matrix<T>& H, std::size_t i, std::size_t jp, const std::vector<int>& m,
133 std::size_t h) {
134 const T zero = num_traits<T>::from_int(0);
135 if (i == h) return zero; // the top list never shifts on a hit
136 T g = zero;
137 for (int jj = static_cast<int>(jp) + 1; jj <= m[i - 1]; ++jj)
138 g += H(i - 1, static_cast<std::size_t>(jj) - 1);
139 return g;
140}
141
142/**
143 * `fifo_drift` and `sfifo_drift`, which differ only in the two `strict` arms.
144 *
145 * FIFO(m) shifts a whole list on every insertion into it, so its outflow term
146 * is `Sfull(i) * x`. Strict FIFO(m) ALSO shifts the prefix of a list when a hit
147 * lands deeper in that same list, which is the `pos_deeper` term; and the tail
148 * demoted from list i+1 arrives at position 1 rather than at the position the
149 * promoted job vacated, which is the `j == 1` arm. Everything else is common,
150 * and writing it once is what keeps the two policies' shared terms from
151 * drifting apart.
152 */
153template <class T>
154std::vector<T> pos_drift_linear(const std::vector<T>& x_in, const std::vector<T>& p,
155 const std::vector<int>& m, std::size_t n, std::size_t h,
156 const SlotMap& sm, bool strict) {
157 const T zero = num_traits<T>::from_int(0), one = num_traits<T>::from_int(1);
158 std::vector<T> x = x_in;
159 for (std::size_t a = 0; a < x.size(); ++a) {
160 if (x[a] < zero) x[a] = zero;
161 if (x[a] > one) x[a] = one;
162 }
163 std::size_t mmax = 0;
164 for (std::size_t i = 0; i < h; ++i) mmax = std::max(mmax, static_cast<std::size_t>(m[i]));
165
166 Matrix<T> Hpos(h, mmax, zero);
167 std::vector<T> Hi(h + 1, zero);
168 for (std::size_t s = 0; s < sm.S; ++s) {
169 const std::size_t i = sm.slots[s].first, j = sm.slots[s].second;
170 T acc = zero;
171 for (std::size_t k = 0; k < n; ++k) acc += p[k] * x[kidx(k, i, j, sm)];
172 Hpos(i - 1, j - 1) = acc;
173 Hi[i - 1] += acc;
174 }
175 T Mrate = zero;
176 for (std::size_t k = 0; k < n; ++k) Mrate += p[k] * pos_out(x, k, sm);
177
178 // `Sfull(i)`: the rate at which list i shifts as a whole. List 1 shifts on
179 // every miss; list i > 1 shifts on every hit in list i-1, i.e. on every
180 // promotion INTO it.
181 std::vector<T> Sfull(h + 1, zero);
182 Sfull[0] = Mrate;
183 for (std::size_t i = 2; i <= h; ++i) Sfull[i - 1] = Hi[i - 2];
184
185 std::vector<T> dX(n * sm.S, zero);
186 for (std::size_t k = 0; k < n; ++k)
187 for (std::size_t s = 0; s < sm.S; ++s) {
188 const std::size_t i = sm.slots[s].first, j = sm.slots[s].second;
189 const std::size_t at = kidx(k, i, j, sm);
190 const T xk = x[at];
191 T o = Sfull[i - 1] * xk;
192 if (strict) o += pos_deeper(Hpos, i, j, m, h) * xk;
193 if (i < h) o += p[k] * xk;
194 dX[at] -= o;
195 if (j >= 2) {
196 T rate = Sfull[i - 1];
197 if (strict) rate += pos_deeper(Hpos, i, j - 1, m, h);
198 dX[at] += rate * x[kidx(k, i, j - 1, sm)];
199 } else {
200 if (i == 1)
201 dX[kidx(k, 1, 1, sm)] += p[k] * pos_out(x, k, sm);
202 else
203 dX[kidx(k, i, 1, sm)] += p[k] * pos_list_occ(x, k, i - 1, m, sm);
204 // Strict reinsertion: the demoted tail of list i+1 lands at
205 // position 1 and the prefix shifts back.
206 if (strict && i < h)
207 dX[kidx(k, i, 1, sm)] +=
208 Hi[i - 1] * x[kidx(k, i + 1, static_cast<std::size_t>(m[i]), sm)];
209 }
210 // FIFO reinsertion: the demoted tail lands IN PLACE, at the position
211 // the promoted item just vacated, so the flow is per-position.
212 if (!strict && i < h)
213 dX[at] += Hpos(i - 1, j - 1) *
214 x[kidx(k, i + 1, static_cast<std::size_t>(m[i]), sm)];
215 }
216 return dX;
217}
218
219/**
220 * Port of `cache_pos_drift_graph.m`: the same two policies under a per-item
221 * access graph.
222 *
223 * `strict` is the reference's `reinsert` argument, 'head' (strict FIFO) against
224 * 'pos' (FIFO). Row 0 of G[k] is miss admission (column 0 rejects, column 1+l
225 * admits to list l); row 1+i is a hit in list i, where column 1+i means STAY IN
226 * PLACE -- that diagonal entry is why the outflow carries `1 - G[k](i,i)` and
227 * not `1`, and is the FIFO/SFIFO convention rather than the RANDOM(m) one.
228 */
229template <class T>
230std::vector<T> pos_drift_graph(const std::vector<T>& x_in, const std::vector<T>& p,
231 const std::vector<Matrix<T> >& G, const std::vector<int>& m,
232 std::size_t n, std::size_t h, const SlotMap& sm, bool strict) {
233 const T zero = num_traits<T>::from_int(0), one = num_traits<T>::from_int(1);
234 std::vector<T> x = x_in;
235 for (std::size_t a = 0; a < x.size(); ++a) {
236 if (x[a] < zero) x[a] = zero;
237 if (x[a] > one) x[a] = one;
238 }
239 std::size_t mmax = 0;
240 for (std::size_t i = 0; i < h; ++i) mmax = std::max(mmax, static_cast<std::size_t>(m[i]));
241
242 std::vector<T> MI(h + 1, zero); // MI[l]: miss admission into list l
243 Matrix<T> HP(h + 1, h + 1, zero); // HP(i,b): promotion i -> b, b > i
244 for (std::size_t k = 0; k < n; ++k) {
245 const T ok = pos_out(x, k, sm);
246 for (std::size_t l = 1; l <= h; ++l) MI[l] += p[k] * ok * G[k](0, l);
247 for (std::size_t i = 1; i <= h; ++i) {
248 const T oc = pos_list_occ(x, k, i, m, sm);
249 for (std::size_t b = i + 1; b <= h; ++b) HP(i, b) += p[k] * oc * G[k](i, b);
250 }
251 }
252 std::vector<T> Sin(h + 1, zero);
253 for (std::size_t l = 1; l <= h; ++l) {
254 Sin[l] = MI[l];
255 for (std::size_t s = 1; s + 1 <= l; ++s) Sin[l] += HP(s, l);
256 }
257 // POp(i,j): the rate at which the occupant of (i,j) LEAVES its position on a
258 // hit, i.e. weighted by 1 - G(i,i) rather than by 1.
259 Matrix<T> POp(h, mmax, zero);
260 for (std::size_t s = 0; s < sm.S; ++s) {
261 const std::size_t i = sm.slots[s].first, j = sm.slots[s].second;
262 T acc = zero;
263 for (std::size_t k = 0; k < n; ++k)
264 acc += p[k] * x[kidx(k, i, j, sm)] * T(one - G[k](i, i));
265 POp(i - 1, j - 1) = acc;
266 }
267
268 std::vector<T> dX(n * sm.S, zero);
269 for (std::size_t k = 0; k < n; ++k) {
270 const T ok = pos_out(x, k, sm);
271 for (std::size_t s = 0; s < sm.S; ++s) {
272 const std::size_t i = sm.slots[s].first, j = sm.slots[s].second;
273 const std::size_t at = kidx(k, i, j, sm);
274 const T xk = x[at];
275 T o = p[k] * xk * T(one - G[k](i, i));
276 o += (strict ? T(Sin[i] + pos_deeper(POp, i, j, m, h)) : Sin[i]) * xk;
277 dX[at] -= o;
278 if (j >= 2) {
279 const T rate = strict ? T(Sin[i] + pos_deeper(POp, i, j - 1, m, h)) : Sin[i];
280 dX[at] += rate * x[kidx(k, i, j - 1, sm)];
281 } else {
282 dX[kidx(k, i, 1, sm)] += p[k] * ok * G[k](0, i);
283 for (std::size_t ss = 1; ss + 1 <= i; ++ss)
284 dX[kidx(k, i, 1, sm)] +=
285 p[k] * pos_list_occ(x, k, ss, m, sm) * G[k](ss, i);
286 }
287 for (std::size_t b = i + 1; b <= h; ++b) {
288 if (strict) {
289 if (j == 1)
290 dX[kidx(k, i, 1, sm)] +=
291 HP(i, b) * x[kidx(k, b, static_cast<std::size_t>(m[b - 1]), sm)];
292 } else {
293 T poj = zero;
294 for (std::size_t kk = 0; kk < n; ++kk)
295 poj += p[kk] * x[kidx(kk, i, j, sm)] * G[kk](i, b);
296 dX[at] += poj * x[kidx(k, b, static_cast<std::size_t>(m[b - 1]), sm)];
297 }
298 }
299 }
300 }
301 return dX;
302}
303
304/** The shared body of the two entry points; `strict` selects the policy. */
305template <class T>
306CacheMissPosRmfResult<T> pos_rmf(const std::vector<T>& gamma, const std::vector<int>& m,
307 const Matrix<T>& lambda,
308 const std::vector<std::vector<Matrix<T> > >& accost,
309 bool strict, bool want_transient, const T& t0, const T& t1,
310 const std::vector<T>& x0init) {
312 "cache_miss_fifo_rmf / cache_miss_sfifo_rmf require transcendental arithmetic: "
313 "the fixed point is reached by a tolerance-driven integration");
314 (void)gamma;
315 const T zero = num_traits<T>::from_int(0), one = num_traits<T>::from_int(1);
316 const std::size_t u = lambda.rows(), n = lambda.cols(), h = m.size();
317 const std::string who = strict ? "cache_miss_sfifo_rmf" : "cache_miss_fifo_rmf";
318 if (u == 0 || n == 0) throw InputError(who + ": empty request-rate matrix");
319 if (h == 0) throw InputError(who + ": at least one cache list is required");
320 for (std::size_t i = 0; i < h; ++i)
321 if (m[i] <= 0) throw InputError(who + ": a list has non-positive capacity");
322
323 // non-finite rates are dropped, as the reference's `row(~isfinite(row)) = 0`
324 std::vector<T> lam_i(n, zero);
325 T tot = zero;
326 for (std::size_t v = 0; v < u; ++v)
327 for (std::size_t k = 0; k < n; ++k) {
328 if (!std::isfinite(num_traits<T>::to_double(lambda(v, k)))) continue;
329 lam_i[k] += lambda(v, k);
330 tot += lambda(v, k);
331 }
332 std::vector<T> p(n, zero);
333 if (tot > zero)
334 for (std::size_t k = 0; k < n; ++k) p[k] = lam_i[k] / tot;
335 else
336 for (std::size_t k = 0; k < n; ++k) p[k] = one / num_traits<T>::from_int(static_cast<long>(n));
337
338 const SlotMap sm = build_slots(m);
339 const std::size_t dim = n * sm.S;
340
341 // Popularity-ordered warm start: the S most requested items, one per slot.
342 std::vector<std::size_t> order(n);
343 for (std::size_t k = 0; k < n; ++k) order[k] = k;
344 std::stable_sort(order.begin(), order.end(),
345 [&](std::size_t a, std::size_t b) { return p[a] > p[b]; });
346 std::vector<T> x0(dim, zero);
347 for (std::size_t s = 0; s < sm.S && s < n; ++s) x0[order[s] * sm.S + s] = one;
348
349 const std::vector<Matrix<T> > G = rmf_detail::build_item_graphs(accost, lambda, n, h);
350 const bool graph = !G.empty();
351 // A cold start on the graph path: see the header comment.
352 const std::vector<T> x0s = graph ? std::vector<T>(dim, zero) : x0;
353 const auto f = [&](const T& t, const std::vector<T>& xx) {
354 (void)t;
355 return graph ? pos_drift_graph(xx, p, G, m, n, h, sm, strict)
356 : pos_drift_linear(xx, p, m, n, h, sm, strict);
357 };
358
360 opt.rtol = num_traits<T>::from_double(1e-8);
361 opt.atol = num_traits<T>::from_double(1e-10);
362 opt.store_trajectory = false;
363
365 const std::vector<T> xss =
366 ode_rosenbrock4(f, zero, T(num_traits<T>::from_int(20000)), x0s, opt).final_state();
367 res.xss = xss;
368 res.pi0.assign(n, zero);
369 for (std::size_t k = 0; k < n; ++k) res.pi0[k] = pos_out(xss, k, sm);
370 res.MI.assign(n, zero);
371 res.M = zero;
372 for (std::size_t k = 0; k < n; ++k) {
373 res.MI[k] = lam_i[k] * res.pi0[k];
374 res.M += res.MI[k];
375 }
376 res.MU.assign(u, zero);
377 for (std::size_t v = 0; v < u; ++v) {
378 T s = zero;
379 for (std::size_t k = 0; k < n; ++k) {
380 if (!std::isfinite(num_traits<T>::to_double(lambda(v, k)))) continue;
381 s += lambda(v, k) * res.pi0[k];
382 }
383 res.MU[v] = s;
384 }
385 if (!want_transient) return res;
386
387 OdeOptions<T> topt = opt;
388 topt.store_trajectory = true;
389 const std::vector<T> xt0 = x0init.empty() ? x0s : x0init;
390 if (xt0.size() != dim)
391 throw InputError(who + ": the initial occupancy has the wrong dimension");
392 const OdeSolution<T> tr = ode_rosenbrock4(f, t0, t1, xt0, topt);
393 const std::size_t nt = tr.t.size();
394 res.tout = tr.t;
395 res.xtraj = Matrix<T>(dim, nt, zero);
396 for (std::size_t c = 0; c < nt; ++c)
397 for (std::size_t a = 0; a < dim; ++a) res.xtraj(a, c) = tr.y[c][a];
398 res.pi0_t = Matrix<T>(n, nt, zero);
399 for (std::size_t c = 0; c < nt; ++c)
400 for (std::size_t k = 0; k < n; ++k) res.pi0_t(k, c) = pos_out(tr.y[c], k, sm);
401 res.MU_t = Matrix<T>(u, nt, zero);
402 for (std::size_t v = 0; v < u; ++v)
403 for (std::size_t c = 0; c < nt; ++c) {
404 T s = zero;
405 for (std::size_t k = 0; k < n; ++k) {
406 if (!std::isfinite(num_traits<T>::to_double(lambda(v, k)))) continue;
407 s += lambda(v, k) * res.pi0_t(k, c);
408 }
409 res.MU_t(v, c) = s;
410 }
411 return res;
412}
413
414} // namespace pos_detail
415
416/**
417 * Port of `cache_miss_fifo_rmf.m`: the FIFO(m) position-resolved mean field.
418 *
419 * @param gamma present for signature compatibility; the reference marks it
420 * unused and so is it here
421 * @param m (h) list capacities
422 * @param lambda (u x n) per-user per-item request rates, the reference's
423 * `lambda(:,:,1)` page
424 * @param accost per-(user,item) access graph; empty is the linear chain
425 */
426template <class T>
428 const std::vector<T>& gamma, const std::vector<int>& m, const Matrix<T>& lambda,
429 const std::vector<std::vector<Matrix<T> > >& accost = std::vector<std::vector<Matrix<T> > >()) {
430 return pos_detail::pos_rmf(gamma, m, lambda, accost, false, false,
432 std::vector<T>());
433}
434
435/** `cache_miss_fifo_rmf` with the optional TSPAN/X0INIT transient. */
436template <class T>
438 const std::vector<T>& gamma, const std::vector<int>& m, const Matrix<T>& lambda, const T& t0,
439 const T& t1, const std::vector<T>& x0init,
440 const std::vector<std::vector<Matrix<T> > >& accost = std::vector<std::vector<Matrix<T> > >()) {
441 return pos_detail::pos_rmf(gamma, m, lambda, accost, false, true, t0, t1, x0init);
442}
443
444/**
445 * Port of `cache_miss_sfifo_rmf.m`: the strict FIFO(m) position-resolved mean
446 * field. Same arguments as `cache_miss_fifo_rmf`; the policies differ only in
447 * where a demoted tail is reinserted (see the header comment).
448 */
449template <class T>
451 const std::vector<T>& gamma, const std::vector<int>& m, const Matrix<T>& lambda,
452 const std::vector<std::vector<Matrix<T> > >& accost = std::vector<std::vector<Matrix<T> > >()) {
453 return pos_detail::pos_rmf(gamma, m, lambda, accost, true, false,
455 std::vector<T>());
456}
457
458/** `cache_miss_sfifo_rmf` with the optional TSPAN/X0INIT transient. */
459template <class T>
461 const std::vector<T>& gamma, const std::vector<int>& m, const Matrix<T>& lambda, const T& t0,
462 const T& t1, const std::vector<T>& x0init,
463 const std::vector<std::vector<Matrix<T> > >& accost = std::vector<std::vector<Matrix<T> > >()) {
464 return pos_detail::pos_rmf(gamma, m, lambda, accost, true, true, t0, t1, x0init);
465}
466
467} // namespace cache
468} // namespace line
469
470#endif // LINE_API_CACHE_CACHE_MISS_POS_RMF_H
Refined mean field (RMF) miss rates of a multi-list RANDOM(m) cache.
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.
CacheMissPosRmfResult< T > cache_miss_fifo_rmf_transient(const std::vector< T > &gamma, const std::vector< int > &m, const Matrix< T > &lambda, const T &t0, const T &t1, const std::vector< T > &x0init, const std::vector< std::vector< Matrix< T > > > &accost=std::vector< std::vector< Matrix< T > > >())
cache_miss_fifo_rmf with the optional TSPAN/X0INIT transient.
CacheMissPosRmfResult< T > cache_miss_sfifo_rmf(const std::vector< T > &gamma, const std::vector< int > &m, const Matrix< T > &lambda, const std::vector< std::vector< Matrix< T > > > &accost=std::vector< std::vector< Matrix< T > > >())
Port of cache_miss_sfifo_rmf.m: the strict FIFO(m) position-resolved mean field.
CacheMissPosRmfResult< T > cache_miss_fifo_rmf(const std::vector< T > &gamma, const std::vector< int > &m, const Matrix< T > &lambda, const std::vector< std::vector< Matrix< T > > > &accost=std::vector< std::vector< Matrix< T > > >())
Port of cache_miss_fifo_rmf.m: the FIFO(m) position-resolved mean field.
CacheMissPosRmfResult< T > cache_miss_sfifo_rmf_transient(const std::vector< T > &gamma, const std::vector< int > &m, const Matrix< T > &lambda, const T &t0, const T &t1, const std::vector< T > &x0init, const std::vector< std::vector< Matrix< T > > > &accost=std::vector< std::vector< Matrix< T > > >())
cache_miss_sfifo_rmf with the optional TSPAN/X0INIT transient.
CacheMissRmfResult< T > CacheMissPosRmfResult
Return value of the position-resolved routines, as CacheMissRmfResult.
OdeSolution< T > ode_rosenbrock4(const F &f, const J &jac, const T &t0, const T &t1, const std::vector< T > &y0, const OdeOptions< T > &opt)
Integrate y' = f(t,y) from t0 to t1 with an analytic Jacobian.
Definition ode.h:304
Number-type abstraction for the templated API port.
Adaptive stiff ODE integrator: a four-stage Rosenbrock method of order four with an embedded order-th...
Integration controls.
Definition ode.h:118
bool store_trajectory
keep every accepted point, not just the last
Definition ode.h:125
Result of an integration.
Definition ode.h:143
Return value of cache_miss_rmf, mirroring [M,MU,MI,pi0,tout,pi0_t,MU_t,xtraj].
std::vector< T > pi0
(n_items) per-item miss probability, clipped to [0,1]
Matrix< T > pi0_t
(n_items x nt) transient miss probability
std::vector< T > tout
transient time grid, empty unless tspan was given
Matrix< T > xtraj
(model_dim x nt) transient occupancy
Matrix< T > MU_t
(u x nt) transient per-user miss rate
std::vector< T > MI
(n_items) per-item miss rate
std::vector< T > xss
the occupancy the metrics were read from
std::vector< T > MU
(u) per-user miss rate