LINE Solver (C++)
Templated C++ port of the LINE queueing solver
Loading...
Searching...
No Matches
cache_sojourn.h
Go to the documentation of this file.
1/**
2 * @file cache_sojourn.h
3 * @brief Grid-independent sojourn-weighted average of a cache mean-field transient.
4 *
5 * Twin of MATLAB `cache_sojourn_ode.m`, JAR `CacheSojourn` and Python
6 * `api/cache/sojourn.py`. For a drift dx/dt = f(x) and a phase-type clock with
7 * row phase vector phi, dphi/dt = phi A, density g(t) = phi(t) c, it returns
8 *
9 * xbar = int_{t0}^{t1} x g dt / int_{t0}^{t1} g dt, wtot = int_{t0}^{t1} g dt,
10 *
11 * by augmenting the state with phi, int g x and int g, so the integrator itself
12 * carries the integral. The value is accurate to the ODE tolerance and does NOT
13 * depend on any output grid: a Riemann sum over the grid did, and that is what
14 * made the adaptive (MATLAB, C++) and fixed-grid (JAR, Python) ENV cache mean
15 * fields differ in the third digit.
16 *
17 * Copyright (c) 2012-2026, Imperial College London
18 * All rights reserved.
19 */
20
21#ifndef LINE_API_CACHE_CACHE_SOJOURN_H
22#define LINE_API_CACHE_CACHE_SOJOURN_H
23
24#include <cstddef>
25#include <vector>
26
27#include "line/num/number.h"
28#include "line/util/matrix.h"
29#include "line/util/ode.h"
30
31namespace line {
32namespace cache {
33
34/** A phase-type clock: dphi/dt = phi A, phi(t0) = phi0, density phi c. Empty = no clock. */
35template <class T>
38 std::vector<T> phi0;
39 std::vector<T> c;
40 bool empty() const { return phi0.empty(); }
41};
42
43/** A sojourn average: `xbar` (empty when `wtot` is not positive), clock mass, per-user miss rate. */
44template <class T>
46 std::vector<T> xbar;
48 std::vector<T> MU; ///< per-user miss rate of xbar, filled by the policy
49 bool empty() const { return xbar.empty(); }
50};
51
52/**
53 * The holding-time clock of a random-environment stage whose holding time has
54 * sub-generator D0 and entry vector pie, in a drift time unit that is `lam` times
55 * real time: A = D0/lam, c = -D0 1/lam, phi0 = pie, so int_0^T g = F(T/lam).
56 */
57template <class T>
58CacheSojournClock<T> cache_sojourn_clock(const Matrix<T>& D0, const std::vector<T>& pie, double lam) {
60 const std::size_t nph = D0.rows();
61 const T L = num_traits<T>::from_double(lam);
62 clk.A = Matrix<T>(nph, nph, num_traits<T>::from_int(0));
63 clk.c.assign(nph, num_traits<T>::from_int(0));
64 clk.phi0 = pie;
65 for (std::size_t i = 0; i < nph; ++i) {
67 for (std::size_t j = 0; j < nph; ++j) {
68 clk.A(i, j) = T(D0(i, j) / L);
69 rs = T(rs + D0(i, j));
70 }
71 clk.c[i] = T(-rs / L);
72 }
73 return clk;
74}
75
76/** Integrates dx/dt = f(t, x) over [t0, t1] from x0 together with `clk`; see the file comment. */
77template <class T, class F>
78CacheSojournResult<T> cache_sojourn_ode(const F& f, const T& t0, const T& t1,
79 const std::vector<T>& x0, const CacheSojournClock<T>& clk) {
80 const std::size_t n = x0.size(), nph = clk.phi0.size();
81 const std::size_t dim = 2 * n + nph + 1;
82 const T zero = num_traits<T>::from_int(0);
83 const auto rhs = [&](const T& t, const std::vector<T>& z) {
84 std::vector<T> x(z.begin(), z.begin() + static_cast<std::ptrdiff_t>(n));
85 const std::vector<T> dx = f(t, x);
86 std::vector<T> dz(dim, zero);
87 for (std::size_t i = 0; i < n; ++i) dz[i] = dx[i];
88 T g = zero;
89 for (std::size_t j = 0; j < nph; ++j) {
90 T s = zero;
91 for (std::size_t i = 0; i < nph; ++i) s = T(s + T(z[n + i] * clk.A(i, j)));
92 dz[n + j] = s;
93 g = T(g + T(z[n + j] * clk.c[j]));
94 }
95 for (std::size_t i = 0; i < n; ++i) dz[n + nph + i] = T(g * x[i]);
96 dz[dim - 1] = g;
97 return dz;
98 };
99 std::vector<T> z0(dim, zero);
100 for (std::size_t i = 0; i < n; ++i) z0[i] = x0[i];
101 for (std::size_t j = 0; j < nph; ++j) z0[n + j] = clk.phi0[j];
103 opt.rtol = num_traits<T>::from_double(1e-8);
104 opt.atol = num_traits<T>::from_double(1e-10);
105 opt.store_trajectory = false;
106 const std::vector<T> z1 = ode_rosenbrock4(rhs, t0, t1, z0, opt).final_state();
108 r.wtot = z1[dim - 1];
109 if (num_traits<T>::to_double(r.wtot) > 0.0) {
110 r.xbar.assign(n, zero);
111 for (std::size_t i = 0; i < n; ++i) r.xbar[i] = T(z1[n + nph + i] / r.wtot);
112 }
113 return r;
114}
115
116} // namespace cache
117} // namespace line
118
119#endif // LINE_API_CACHE_CACHE_SOJOURN_H
std::size_t rows() const
Definition matrix.h:89
Dense matrix and non-owning view.
CacheSojournClock< T > cache_sojourn_clock(const Matrix< T > &D0, const std::vector< T > &pie, double lam)
The holding-time clock of a random-environment stage whose holding time has sub-generator D0 and entr...
CacheSojournResult< T > cache_sojourn_ode(const F &f, const T &t0, const T &t1, const std::vector< T > &x0, const CacheSojournClock< T > &clk)
Integrates dx/dt = f(t, x) over [t0, t1] from x0 together with clk; see the file comment.
Conservation laws of a layered queueing network, enumerated from its structure.
Definition aoi_dist2ph.h:52
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
A phase-type clock: dphi/dt = phi A, phi(t0) = phi0, density phi c.
A sojourn average: xbar (empty when wtot is not positive), clock mass, per-user miss rate.
std::vector< T > MU
per-user miss rate of xbar, filled by the policy