5#ifndef LINE_API_CACHE_CACHE_MISS_POS_RMF_H
6#define LINE_API_CACHE_CACHE_MISS_POS_RMF_H
84 std::vector<std::pair<std::size_t, std::size_t> > slots;
85 std::vector<std::vector<std::size_t> > sidx;
89inline SlotMap build_slots(
const std::vector<int>& m) {
91 const std::size_t h = m.size();
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;
100 sm.S = sm.slots.size();
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];
111T pos_out(
const std::vector<T>& x, std::size_t k,
const SlotMap& sm) {
114 for (std::size_t s = 0; s < sm.S; ++s) acc += x[k * sm.S + s];
116 if (o < zero) o = zero;
117 if (o > one) o = one;
123T pos_list_occ(
const std::vector<T>& x, std::size_t k, std::size_t i,
const std::vector<int>& m,
126 for (
int j = 1; j <= m[i - 1]; ++j) acc += x[kidx(k, i,
static_cast<std::size_t
>(j), sm)];
132T pos_deeper(
const Matrix<T>& H, std::size_t i, std::size_t jp,
const std::vector<int>& m,
135 if (i == h)
return 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);
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) {
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;
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]));
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;
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;
176 for (std::size_t k = 0; k < n; ++k) Mrate += p[k] * pos_out(x, k, sm);
181 std::vector<T> Sfull(h + 1, zero);
183 for (std::size_t i = 2; i <= h; ++i) Sfull[i - 1] = Hi[i - 2];
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);
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;
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)];
201 dX[kidx(k, 1, 1, sm)] += p[k] * pos_out(x, k, sm);
203 dX[kidx(k, i, 1, sm)] += p[k] * pos_list_occ(x, k, i - 1, m, sm);
207 dX[kidx(k, i, 1, sm)] +=
208 Hi[i - 1] * x[kidx(k, i + 1,
static_cast<std::size_t
>(m[i]), sm)];
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)];
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) {
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;
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]));
242 std::vector<T> MI(h + 1, zero);
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);
252 std::vector<T> Sin(h + 1, zero);
253 for (std::size_t l = 1; l <= h; ++l) {
255 for (std::size_t s = 1; s + 1 <= l; ++s) Sin[l] += HP(s, l);
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;
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;
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);
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;
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)];
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);
287 for (std::size_t b = i + 1; b <= h; ++b) {
290 dX[kidx(k, i, 1, sm)] +=
291 HP(i, b) * x[kidx(k, b,
static_cast<std::size_t
>(m[b - 1]), sm)];
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)];
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");
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");
324 std::vector<T> lam_i(n, zero);
326 for (std::size_t v = 0; v < u; ++v)
327 for (std::size_t k = 0; k < n; ++k) {
329 lam_i[k] += lambda(v, k);
332 std::vector<T> p(n, zero);
334 for (std::size_t k = 0; k < n; ++k) p[k] = lam_i[k] / tot;
338 const SlotMap sm = build_slots(m);
339 const std::size_t dim = n * sm.S;
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;
349 const std::vector<Matrix<T> > G = rmf_detail::build_item_graphs(accost, lambda, n, h);
350 const bool graph = !G.empty();
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) {
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);
362 opt.store_trajectory =
false;
365 const std::vector<T> 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);
372 for (std::size_t k = 0; k < n; ++k) {
373 res.
MI[k] = lam_i[k] * res.
pi0[k];
376 res.
MU.assign(u, zero);
377 for (std::size_t v = 0; v < u; ++v) {
379 for (std::size_t k = 0; k < n; ++k) {
381 s += lambda(v, k) * res.
pi0[k];
385 if (!want_transient)
return res;
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");
393 const std::size_t nt =
tr.t.size();
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];
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);
402 for (std::size_t v = 0; v < u; ++v)
403 for (std::size_t c = 0; c < nt; ++c) {
405 for (std::size_t k = 0; k < n; ++k) {
407 s += lambda(v, k) * res.
pi0_t(k, c);
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,
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);
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,
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);
Refined mean field (RMF) miss rates of a multi-list RANDOM(m) cache.
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.
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...
bool store_trajectory
keep every accepted point, not just the last
Result of an integration.
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