5#ifndef LINE_API_CACHE_CACHE_MISS_RMF_H
6#define LINE_API_CACHE_CACHE_MISS_RMF_H
107#include <type_traits>
124 return i + k * n_items;
142namespace rmf_detail {
146T hit_rate(
const std::vector<T>& x,
const std::vector<T>& p, std::size_t level,
147 std::size_t n_items) {
149 for (std::size_t i = 0; i < n_items; ++i) hr += p[i] * x[
cache_miss_rmf_index(i, level, n_items)];
155std::vector<T> drift(
const std::vector<T>& x,
const std::vector<T>& p,
const std::vector<T>& m,
156 std::size_t n_items, std::size_t h) {
158 const std::size_t model_dim = n_items * (h + 1);
159 std::vector<T> hr(h + 1, zero);
160 for (std::size_t k = 0; k <= h; ++k) hr[k] = hit_rate(x, p, k, n_items);
161 std::vector<T> dX(model_dim, zero);
162 for (std::size_t i = 0; i < n_items; ++i) {
163 for (std::size_t k = 0; k + 1 <= h; ++k) {
166 const T flow = p[i] * x[ik] - hr[k] * x[ik1] / m[k];
198Matrix<T> jacobian(
const std::vector<T>& x,
const std::vector<T>& p,
const std::vector<T>& m,
199 std::size_t n_items, std::size_t h) {
200 const T zero = num_traits<T>::from_int(0);
201 const std::size_t model_dim = n_items * (h + 1);
202 std::vector<T> hr(h + 1, zero);
203 for (std::size_t k = 0; k <= h; ++k) hr[k] = hit_rate(x, p, k, n_items);
204 Matrix<T> Fp(model_dim, model_dim, zero);
205 for (std::size_t i = 0; i < n_items; ++i) {
206 for (std::size_t k = 0; k + 1 <= h; ++k) {
211 Fp(ik, ik1) += hr[k] / m[k];
212 Fp(ik1, ik1) -= hr[k] / m[k];
213 for (std::size_t j = 0; j < n_items; ++j) {
216 Fp(ik, jk1) -= p[i] * x[ik] / m[k];
217 Fp(ik1, jk1) += p[i] * x[ik] / m[k];
218 Fp(ik, jk) += p[j] * x[ik1] / m[k];
219 Fp(ik1, jk) -= p[j] * x[ik1] / m[k];
232std::vector<T> hessian(
const std::vector<T>& p,
const std::vector<T>& m, std::size_t n_items,
234 const T zero = num_traits<T>::from_int(0);
235 const std::size_t md = n_items * (h + 1);
236 std::vector<T> H(md * md * md, zero);
237 const auto at = [md](std::size_t a, std::size_t b, std::size_t c) {
238 return (a * md + b) * md + c;
240 for (std::size_t i = 0; i < n_items; ++i) {
241 for (std::size_t k = 0; k + 1 <= h; ++k) {
244 for (std::size_t j = 0; j < n_items; ++j) {
245 if (j == i)
continue;
248 H[at(ik, jk, ik1)] += p[j] / m[k];
249 H[at(ik, ik1, jk)] += p[j] / m[k];
250 H[at(ik, jk1, ik)] -= p[i] / m[k];
251 H[at(ik, ik, jk1)] -= p[i] / m[k];
252 H[at(ik1, jk, ik1)] -= p[j] / m[k];
253 H[at(ik1, ik1, jk)] -= p[j] / m[k];
254 H[at(ik1, jk1, ik)] += p[i] / m[k];
255 H[at(ik1, ik, jk1)] += p[i] / m[k];
264Matrix<T> noise_matrix(
const std::vector<T>& x,
const std::vector<T>& p,
const std::vector<T>& m,
265 std::size_t n_items, std::size_t h) {
266 const T zero = num_traits<T>::from_int(0);
267 const std::size_t md = n_items * (h + 1);
268 Matrix<T> Q(md, md, zero);
269 const int signs[4] = {-1, 1, 1, -1};
270 for (std::size_t i = 0; i < n_items; ++i) {
271 for (std::size_t k = 0; k + 1 <= h; ++k) {
272 for (std::size_t j = 0; j < n_items; ++j) {
279 for (
int ia = 0; ia < 4; ++ia)
280 for (
int ib = 0; ib < 4; ++ib)
281 Q(idx[ia], idx[ib]) +=
282 rate * num_traits<T>::from_int(signs[ia] * signs[ib]);
296std::vector<T> fixed_point(
const std::vector<T>& x0,
const std::vector<T>& p,
297 const std::vector<T>& m, std::size_t n_items, std::size_t h,
298 const T& tmax,
const T& rtol,
const T& atol) {
302 opt.store_trajectory =
false;
303 const auto f = [&](
const T& t,
const std::vector<T>& x) {
305 return drift(x, p, m, n_items, h);
308 return ode_rosenbrock4(f, T(num_traits<T>::from_int(0)), tmax, x0, opt).final_state();
313std::vector<T> fixed_point(
const std::vector<T>& x0,
const std::vector<T>& p,
314 const std::vector<T>& m, std::size_t n_items, std::size_t h,
316 return fixed_point(x0, p, m, n_items, h, tmax, T(num_traits<T>::from_double(1e-8)),
317 T(num_traits<T>::from_double(1e-10)));
335Matrix<T> lyapunov(
const Matrix<T>& F,
const Matrix<T>& Q) {
336 const std::size_t r = F.rows();
337 const T zero = num_traits<T>::from_int(0);
338 Matrix<T> A(r * r, r * r, zero);
339 std::vector<T> rhs(r * r, zero);
340 for (std::size_t a = 0; a < r; ++a)
341 for (std::size_t b = 0; b < r; ++b) {
342 const std::size_t row = a * r + b;
343 for (std::size_t c = 0; c < r; ++c) {
344 A(row, c * r + b) += F(a, c);
345 A(row, a * r + c) += F(b, c);
350 Matrix<T> W(r, r, zero);
351 for (std::size_t a = 0; a < r; ++a)
352 for (std::size_t b = 0; b < r; ++b) W(a, b) = w[a * r + b];
377 std::size_t rank = 0;
378 std::vector<std::vector<T>> basis;
382NullSpace<T> left_null_space(
const Matrix<T>& Fp) {
383 const std::size_t n = Fp.rows();
384 const T zero = num_traits<T>::from_int(0);
386 Matrix<T> M(n, n, zero);
388 for (std::size_t i = 0; i < n; ++i)
389 for (std::size_t j = 0; j < n; ++j) {
392 if (a > scale) scale = a;
394 if (scale == zero) scale = num_traits<T>::from_int(1);
397 const T tol = scale * num_traits<T>::from_double(1e-8);
399 std::vector<std::size_t> col_of_pivot;
400 std::vector<std::size_t> perm(n);
401 for (std::size_t i = 0; i < n; ++i) perm[i] = i;
403 for (std::size_t col = 0; col < n && row < n; ++col) {
406 for (std::size_t i = row + 1; i < n; ++i) {
407 const T a =
num_abs(M(i, col));
413 if (best <= tol)
continue;
415 for (std::size_t j = 0; j < n; ++j) std::swap(M(row, j), M(p, j));
416 const T d = M(row, col);
417 for (std::size_t j = 0; j < n; ++j) M(row, j) = M(row, j) / d;
418 for (std::size_t i = 0; i < n; ++i) {
419 if (i == row)
continue;
420 const T f = M(i, col);
421 if (f == zero)
continue;
422 for (std::size_t j = 0; j < n; ++j) M(i, j) -= f * M(row, j);
424 col_of_pivot.push_back(col);
429 ns.rank = col_of_pivot.size();
430 std::vector<bool> is_pivot(n,
false);
431 for (std::size_t c : col_of_pivot) is_pivot[c] =
true;
433 for (std::size_t free_col = 0; free_col < n; ++free_col) {
434 if (is_pivot[free_col])
continue;
435 std::vector<T> v(n, zero);
436 v[free_col] = num_traits<T>::from_int(1);
437 for (std::size_t r = 0; r < col_of_pivot.size(); ++r)
438 v[col_of_pivot[r]] = -M(r, free_col);
439 ns.basis.push_back(v);
443 for (std::size_t i = 0; i < ns.basis.size(); ++i) {
444 for (std::size_t k = 0; k < i; ++k) {
446 for (std::size_t j = 0; j < n; ++j) dot += ns.basis[i][j] * ns.basis[k][j];
447 for (std::size_t j = 0; j < n; ++j) ns.basis[i][j] -= dot * ns.basis[k][j];
450 for (std::size_t j = 0; j < n; ++j) nrm2 += ns.basis[i][j] * ns.basis[i][j];
451 const T nrm = sqrt(nrm2);
452 if (nrm <= tol)
throw NumericError(
"cache_miss_rmf: the null-space basis degenerated");
453 for (std::size_t j = 0; j < n; ++j) ns.basis[i][j] = ns.basis[i][j] / nrm;
475inline NullSpace<double> left_null_space_svd(
const Matrix<double>& Fp) {
476#ifndef LINE_MP_HAVE_LAPACK
477 return left_null_space(Fp);
479 const std::size_t n = Fp.rows();
480 std::vector<double> a(n * n);
481 for (std::size_t i = 0; i < n; ++i)
482 for (std::size_t j = 0; j < n; ++j) a[j * n + i] = Fp(i, j);
483 const int ni =
static_cast<int>(n);
484 std::vector<double> s(n), u(n * n), vt(1);
485 int info = 0, lwork = -1;
488 dgesvd_(
"A",
"N", &ni, &ni, a.data(), &ni, s.data(), u.data(), &ni, vt.data(), &one, &wopt,
490 if (info != 0)
throw NumericError(
"cache_miss_rmf: LAPACK workspace query failed");
491 lwork =
static_cast<int>(wopt);
492 std::vector<double> work(
static_cast<std::size_t
>(lwork));
493 dgesvd_(
"A",
"N", &ni, &ni, a.data(), &ni, s.data(), u.data(), &ni, vt.data(), &one,
494 work.data(), &lwork, &info);
495 if (info != 0)
throw NumericError(
"cache_miss_rmf: LAPACK dgesvd failed to converge");
497 const double tol = 1e-8 * s[0];
498 NullSpace<double> ns;
501 if (v > tol) ++ns.rank;
502 for (std::size_t c = ns.rank; c < n; ++c) {
503 std::vector<double> row(n, 0.0);
504 for (std::size_t i = 0; i < n; ++i) row[i] = u[c * n + i];
505 ns.basis.push_back(row);
513NullSpace<T> left_null_space_for(
const Matrix<T>& Fp) {
514 return left_null_space(Fp);
518inline NullSpace<double> left_null_space_for<double>(
const Matrix<double>& Fp) {
519 return left_null_space_svd(Fp);
531Matrix<T> linear_graph(std::size_t h) {
532 const T zero = num_traits<T>::from_int(0), one = num_traits<T>::from_int(1);
533 Matrix<T> g(h + 1, h + 1, zero);
535 for (std::size_t a = 1; a + 1 <= h; ++a) g(a, a + 1) = one;
552std::vector<Matrix<T> > build_item_graphs(
const std::vector<std::vector<Matrix<T> > >& accost,
553 const Matrix<T>& lambda, std::size_t n, std::size_t h) {
554 std::vector<Matrix<T> > G;
555 if (accost.empty())
return G;
556 const T zero = num_traits<T>::from_int(0);
557 const Matrix<T> lin = linear_graph<T>(h);
558 const std::size_t u = accost.size();
559 std::vector<Matrix<T> > Gc(n, lin);
560 bool is_linear =
true;
561 for (std::size_t k = 0; k < n; ++k) {
562 Matrix<T> num(h + 1, h + 1, zero);
564 for (std::size_t v = 0; v < u; ++v) {
565 if (k >= accost[v].size())
continue;
566 const Matrix<T>& gvk = accost[v][k];
567 if (gvk.rows() == 0)
continue;
568 if (gvk.rows() != h + 1 || gvk.cols() != h + 1)
569 throw InputError(
"cache_miss_rmf: an access graph is not (h+1)x(h+1)");
570 double w = (v < lambda.rows() && k < lambda.cols())
571 ? num_traits<T>::to_double(lambda(v, k))
573 if (!std::isfinite(w)) w = 0.0;
574 const T wv = num_traits<T>::from_double(w);
575 for (std::size_t a = 0; a <= h; ++a)
576 for (std::size_t b = 0; b <= h; ++b) num(a, b) += wv * gvk(a, b);
581 for (std::size_t a = 0; a <= h; ++a)
582 for (std::size_t b = 0; b <= h; ++b) gk(a, b) = num(a, b) / den;
583 }
else if (!accost.empty() && k < accost[0].size() && accost[0][k].rows() == h + 1) {
586 for (std::size_t a = 0; a <= h; ++a) {
588 for (std::size_t b = 0; b <= h; ++b) srow += gk(a, b);
590 for (std::size_t b = 0; b <= h; ++b) gk(a, b) = gk(a, b) / srow;
593 for (std::size_t a = 0; a <= h && is_linear; ++a)
594 for (std::size_t b = 0; b <= h && is_linear; ++b)
595 if (std::fabs(num_traits<T>::to_double(gk(a, b)) -
596 num_traits<T>::to_double(lin(a, b))) >= 1e-9)
599 if (!is_linear) G = Gc;
614std::vector<T> drift_graph(
const std::vector<T>& x_in,
const std::vector<T>& p,
615 const std::vector<Matrix<T> >& G,
const std::vector<T>& m,
616 std::size_t n, std::size_t h) {
617 const T zero = num_traits<T>::from_int(0), one = num_traits<T>::from_int(1);
618 const std::size_t model_dim = n * (h + 1);
619 std::vector<T> x = x_in;
620 for (std::size_t a = 0; a < x.size(); ++a) {
621 if (x[a] < zero) x[a] = zero;
622 if (x[a] > one) x[a] = one;
625 Matrix<T> A(h + 1, h + 1, zero);
626 for (std::size_t s = 0; s <= h; ++s)
627 for (std::size_t j = 0; j < n; ++j) {
629 if (xjs == zero)
continue;
630 for (std::size_t i = 1; i <= h; ++i) A(s, i) += p[j] * xjs * G[j](s, i);
632 std::vector<T> dX(model_dim, zero);
633 for (std::size_t k = 0; k < n; ++k) {
635 for (std::size_t i = 1; i <= h; ++i) {
637 T infl = p[k] * outk * G[k](0, i);
638 for (std::size_t s = 1; s + 1 <= i; ++s)
640 for (std::size_t b = i + 1; b <= h; ++b)
642 T outfl = p[k] * xki * T(one - G[k](i, i));
644 for (std::size_t s = 0; s + 1 <= i; ++s) disp += A(s, i);
645 outfl += disp * xki / m[i - 1];
661std::vector<T> fixed_point_graph(
const std::vector<T>& x0,
const std::vector<T>& p,
662 const std::vector<Matrix<T> >& G,
const std::vector<T>& m,
663 std::size_t n, std::size_t h) {
665 opt.rtol = num_traits<T>::from_double(1e-8);
666 opt.atol = num_traits<T>::from_double(1e-10);
667 opt.store_trajectory =
false;
668 const auto f = [&](
const T& t,
const std::vector<T>& x) {
670 return drift_graph(x, p, G, m, n, h);
673 T(num_traits<T>::from_int(20000)), x0, opt)
700 const std::vector<std::vector<
Matrix<T> > >& accost) {
702 "cache_miss_rmf requires transcendental arithmetic: its fixed point is reached "
703 "by a tolerance-driven integration of the mean-field drift");
707 const std::size_t u = lambda.
rows();
708 const std::size_t n_items = lambda.
cols();
709 const std::size_t h = m_in.size();
710 if (u == 0 || n_items == 0)
throw InputError(
"cache_miss_rmf: empty request-rate matrix");
711 if (h == 0)
throw InputError(
"cache_miss_rmf: at least one cache list is required");
713 std::vector<T> m(h, zero);
714 for (std::size_t k = 0; k < h; ++k) {
715 if (m_in[k] <= 0)
throw InputError(
"cache_miss_rmf: a list has non-positive capacity");
720 std::vector<T> lam_i(n_items, zero);
722 for (std::size_t v = 0; v < u; ++v)
723 for (std::size_t i = 0; i < n_items; ++i) {
724 lam_i[i] += lambda(v, i);
725 lam_tot += lambda(v, i);
727 if (lam_tot == zero)
throw InputError(
"cache_miss_rmf: all request rates are zero");
728 std::vector<T> p(n_items, zero);
729 for (std::size_t i = 0; i < n_items; ++i) p[i] = lam_i[i] / lam_tot;
731 const std::size_t model_dim = n_items * (h + 1);
735 std::vector<T> x0(model_dim, zero);
737 for (std::size_t k = 1; k <= h; ++k)
738 for (
int jj = 0; jj < m_in[k - 1]; ++jj) {
745 const std::vector<Matrix<T> > G = rmf_detail::build_item_graphs(accost, lambda, n_items, h);
749 res.
xss = rmf_detail::fixed_point_graph(x0, p, G, m, n_items, h);
750 res.
pi0.assign(n_items, zero);
751 for (std::size_t i = 0; i < n_items; ++i) {
753 if (v < zero) v = zero;
754 if (v > one) v = one;
757 res.
MI.assign(n_items, zero);
759 for (std::size_t i = 0; i < n_items; ++i) {
760 res.
MI[i] = lam_i[i] * res.
pi0[i];
763 res.
MU.assign(u, zero);
764 for (std::size_t v = 0; v < u; ++v) {
766 for (std::size_t i = 0; i < n_items; ++i) s += lambda(v, i) * res.
pi0[i];
772 std::vector<T> xss = rmf_detail::fixed_point(x0, p, m, n_items, h, tmax);
773 xss = rmf_detail::fixed_point(xss, p, m, n_items, h, tmax,
779 const Matrix<T> Fp = rmf_detail::jacobian(xss, p, m, n_items, h);
780 const rmf_detail::NullSpace<T> ns = rmf_detail::left_null_space_for(Fp);
781 const std::size_t rk = ns.rank;
782 if (rk == 0 || rk >= model_dim)
783 throw NumericError(
"cache_miss_rmf: the reduction is degenerate");
784 const std::vector<T> Fpp = rmf_detail::hessian(p, m, n_items, h);
785 const Matrix<T> Q = rmf_detail::noise_matrix(xss, p, m, n_items, h);
791 for (std::size_t l = 0; l <= h && d < rk; ++l)
792 for (std::size_t i = 0; i + 1 < n_items && d < rk; ++i, ++d)
794 if (d != rk)
throw NumericError(
"cache_miss_rmf: the reduction basis is too small");
796 if (ns.basis.size() != model_dim - rk)
797 throw NumericError(
"cache_miss_rmf: the null-space basis has the wrong size");
798 for (std::size_t i = 0; i < ns.basis.size(); ++i)
799 for (std::size_t j = 0; j < model_dim; ++j) C(rk + i, j) = ns.basis[i][j];
805 for (std::size_t a = 0; a < rk; ++a)
806 for (std::size_t b = 0; b < rk; ++b) Fp_r(a, b) = tmp(a, b);
810 Matrix<T> Ct(model_dim, model_dim, zero);
811 for (std::size_t a = 0; a < model_dim; ++a)
812 for (std::size_t b = 0; b < model_dim; ++b) Ct(a, b) = C(b, a);
814 for (std::size_t a = 0; a < rk; ++a)
815 for (std::size_t b = 0; b < rk; ++b) Q_r(a, b) = tmp(a, b);
819 const auto Hat = [model_dim](std::size_t a, std::size_t b, std::size_t c) {
820 return (a * model_dim + b) * model_dim + c;
822 std::vector<T> tmp1(rk * model_dim * model_dim, zero);
823 for (std::size_t a = 0; a < rk; ++a)
824 for (std::size_t j = 0; j < model_dim; ++j)
825 for (std::size_t k = 0; k < model_dim; ++k) {
827 for (std::size_t i = 0; i < model_dim; ++i) s += C(a, i) * Fpp[Hat(i, j, k)];
828 tmp1[(a * model_dim + j) * model_dim + k] = s;
830 std::vector<T> tmp2(rk * rk * model_dim, zero);
831 for (std::size_t a = 0; a < rk; ++a)
832 for (std::size_t b = 0; b < rk; ++b)
833 for (std::size_t k = 0; k < model_dim; ++k) {
835 for (std::size_t j = 0; j < model_dim; ++j)
836 s += tmp1[(a * model_dim + j) * model_dim + k] * Cinv(j, b);
837 tmp2[(a * rk + b) * model_dim + k] = s;
839 std::vector<T> Fpp_r(rk * rk * rk, zero);
840 for (std::size_t a = 0; a < rk; ++a)
841 for (std::size_t b = 0; b < rk; ++b)
842 for (std::size_t c = 0; c < rk; ++c) {
844 for (std::size_t k = 0; k < model_dim; ++k)
845 s += tmp2[(a * rk + b) * model_dim + k] * Cinv(k, c);
846 Fpp_r[(a * rk + b) * rk + c] = s;
849 const Matrix<T> W_r = rmf_detail::lyapunov(Fp_r, Q_r);
851 std::vector<T> C_r(rk, zero);
852 for (std::size_t a = 0; a < rk; ++a) {
854 for (std::size_t b = 0; b < rk; ++b)
855 for (std::size_t c = 0; c < rk; ++c) s += Fpp_r[(a * rk + b) * rk + c] * W_r(b, c);
858 std::vector<T> rhs(rk, zero);
859 for (std::size_t a = 0; a < rk; ++a)
863 std::vector<T> xref(model_dim, zero);
864 for (std::size_t i = 0; i < model_dim; ++i) {
866 for (std::size_t a = 0; a < rk; ++a) s += Cinv(i, a) * V_r[a];
878 res.
pi0.assign(n_items, zero);
879 for (std::size_t i = 0; i < n_items; ++i) {
881 if (v < zero) v = zero;
882 if (v > one) v = one;
885 res.
MI.assign(n_items, zero);
887 for (std::size_t i = 0; i < n_items; ++i) {
888 res.
MI[i] = lam_i[i] * res.
pi0[i];
891 res.
MU.assign(u, zero);
892 for (std::size_t v = 0; v < u; ++v) {
894 for (std::size_t i = 0; i < n_items; ++i) s += lambda(v, i) * res.
pi0[i];
903 const Matrix<T>& lambda,
const T& tmax) {
922 const Matrix<T>& lambda,
const T& t0,
const T& t1,
923 const std::vector<T>& x0init) {
925 "cache_miss_rmf_transient requires transcendental arithmetic");
928 const std::size_t u = lambda.
rows();
929 const std::size_t n_items = lambda.
cols();
930 const std::size_t h = m_in.size();
931 const std::size_t model_dim = n_items * (h + 1);
932 if (x0init.size() != model_dim)
933 throw InputError(
"cache_miss_rmf_transient: initial occupancy has the wrong length");
935 std::vector<T> m(h, zero);
936 for (std::size_t k = 0; k < h; ++k) m[k] = num_traits<T>::from_int(
static_cast<long>(m_in[k]));
938 std::vector<T> lam_i(n_items, zero);
940 for (std::size_t v = 0; v < u; ++v)
941 for (std::size_t i = 0; i < n_items; ++i) {
942 lam_i[i] += lambda(v, i);
943 lam_tot += lambda(v, i);
945 std::vector<T> p(n_items, zero);
946 for (std::size_t i = 0; i < n_items; ++i) p[i] = lam_i[i] / lam_tot;
951 const auto f = [&](
const T& t,
const std::vector<T>& x) {
953 return rmf_detail::drift(x, p, m, n_items, h);
958 const std::size_t nt = s.
t.size();
961 for (std::size_t j = 0; j < nt; ++j)
962 for (std::size_t i = 0; i < model_dim; ++i) res.
xtraj(i, j) = s.
y[j][i];
964 for (std::size_t i = 0; i < n_items; ++i)
965 for (std::size_t j = 0; j < nt; ++j) {
967 if (v < zero) v = zero;
968 if (v > one) v = one;
972 for (std::size_t v = 0; v < u; ++v)
973 for (std::size_t j = 0; j < nt; ++j) {
975 for (std::size_t i = 0; i < n_items; ++i) acc += lambda(v, i) * res.
pi0_t(i, j);
976 res.
MU_t(v, j) = acc;
988 std::vector<Matrix<T> >
W;
1021 const std::vector<int>& m_in,
const Matrix<T>& lambda,
const T& time, std::size_t n_points,
1023 if (!std::is_same<T, double>::value)
1025 "cache_miss_rmf_expansion_transient: the coupled (X,V,W) system is integrated with "
1026 "LSODA, whose coefficients assume double precision; rerun with --arith double");
1028 const std::size_t u = lambda.
rows();
1029 const std::size_t n_items = lambda.
cols();
1030 const std::size_t h = m_in.size();
1031 if (u == 0 || n_items == 0)
1032 throw InputError(
"cache_miss_rmf_expansion_transient: empty request-rate matrix");
1034 throw InputError(
"cache_miss_rmf_expansion_transient: at least one cache list is required");
1036 throw InputError(
"cache_miss_rmf_expansion_transient: at least two output points are "
1037 "required to describe a trajectory");
1039 throw InputError(
"cache_miss_rmf_expansion_transient: the horizon must be positive");
1041 std::vector<T> m(h, zero);
1042 for (std::size_t k = 0; k < h; ++k) {
1044 throw InputError(
"cache_miss_rmf_expansion_transient: a list has non-positive capacity");
1048 std::vector<T> lam_i(n_items, zero);
1050 for (std::size_t v = 0; v < u; ++v)
1051 for (std::size_t i = 0; i < n_items; ++i) {
1052 lam_i[i] += lambda(v, i);
1053 lam_tot += lambda(v, i);
1055 if (lam_tot == zero)
1056 throw InputError(
"cache_miss_rmf_expansion_transient: all request rates are zero");
1057 std::vector<T> p(n_items, zero);
1058 for (std::size_t i = 0; i < n_items; ++i) p[i] = lam_i[i] / lam_tot;
1060 const std::size_t d = n_items * (h + 1);
1063 std::vector<T> x0(d, zero);
1065 std::size_t obj = 0;
1066 for (std::size_t k = 1; k <= h; ++k)
1067 for (
int jj = 0; jj < m_in[k - 1]; ++jj) {
1072 for (std::size_t i = obj; i < n_items; ++i)
1076 std::vector<double> grid(n_points, 0.0);
1078 for (std::size_t j = 0; j < n_points; ++j)
1079 grid[j] = tend *
static_cast<double>(j) /
static_cast<double>(n_points - 1);
1081 const std::size_t total = order == 0 ? d : d + d + d * d;
1082 std::vector<double> y0(total, 0.0);
1083 for (std::size_t i = 0; i < d; ++i) y0[i] = num_traits<T>::to_double(x0[i]);
1085 const std::vector<T> Fpp = order == 0 ? std::vector<T>() : rmf_detail::hessian(p, m, n_items, h);
1087 const auto rhs = [&](
double t,
const double* y,
double* dy) {
1089 std::vector<T> x(d, zero);
1090 for (std::size_t i = 0; i < d; ++i) x[i] = num_traits<T>::from_double(y[i]);
1091 const std::vector<T> F = rmf_detail::drift(x, p, m, n_items, h);
1092 for (std::size_t i = 0; i < d; ++i) dy[i] = num_traits<T>::to_double(F[i]);
1093 if (order == 0)
return;
1094 const Matrix<T> Fp = rmf_detail::jacobian(x, p, m, n_items, h);
1095 const Matrix<T> Q = rmf_detail::noise_matrix(x, p, m, n_items, h);
1096 for (std::size_t a = 0; a < d; ++a) {
1101 for (std::size_t b = 0; b < d; ++b)
1102 for (std::size_t c = 0; c < d; ++c) {
1103 const T hv = Fpp[(a * d + b) * d + c];
1104 if (hv == zero)
continue;
1110 for (std::size_t a = 0; a < d; ++a)
1111 for (std::size_t b = 0; b < d; ++b) {
1113 for (std::size_t c = 0; c < d; ++c)
1124 if (!s.
success || s.
y.size() != n_points)
1125 throw NumericError(
"cache_miss_rmf_expansion_transient: the coupled (X,V,W) system could "
1126 "not be integrated over the requested horizon");
1129 out.
t.assign(n_points, zero);
1132 out.
W.assign(n_points,
Matrix<T>(d, d, zero));
1133 for (std::size_t j = 0; j < n_points; ++j) {
1135 for (std::size_t i = 0; i < d; ++i) {
1139 if (order == 0)
continue;
1140 for (std::size_t a = 0; a < d; ++a)
1141 for (std::size_t b = 0; b < d; ++b)
The algorithm cannot proceed on this instance (singular matrix, ...).
NumericError(const std::string &what)
UnsupportedError(const std::string &what)
Eigenvalues and singular values, backed by LAPACK.
The exception types the port throws.
Dense linear algebra over the templated number type: products, identity, inverse, and powers.
LSODA: the LINE-facing wrapper over the vendored solver in third_party/lsoda.hpp.
LU factorization with partial pivoting, templated on the number type.
Dense matrix and non-owning view.
CacheMissRmfResult< T > cache_miss_rmf(const std::vector< T > &gamma, const std::vector< int > &m_in, const Matrix< T > &lambda, const T &tmax, const std::vector< std::vector< Matrix< T > > > &accost)
Refined mean-field miss rates of a RANDOM(m) multi-list cache.
CacheRmfExpansionTransient< T > cache_miss_rmf_expansion_transient(const std::vector< int > &m_in, const Matrix< T > &lambda, const T &time, std::size_t n_points, int order)
Refined mean-field TRANSIENT, CacheRMF.meanFieldExpansionTransient.
CacheMissRmfResult< T > cache_miss_rmf_transient(const std::vector< int > &m_in, const Matrix< T > &lambda, const T &t0, const T &t1, const std::vector< T > &x0init)
Transient mean-field trajectory over [t0,t1] from a given initial occupancy, the optional TSPAN/X0INI...
std::size_t cache_miss_rmf_index(std::size_t i, std::size_t k, std::size_t n_items)
Flat index of (item i, list k), k = 0 meaning "not cached" (rmf_index.m).
double dot(const std::vector< double > &a, const std::vector< double > &b)
The inner product of a row vector with a column held as a vector.
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.
LsodaSolution lsoda_integrate(const LsodaRhs &f, const std::vector< double > &y0, const std::vector< double > &t_eval, const LsodaOptions &opt=LsodaOptions())
Matrix< T > inverse(const Matrix< T > &A)
Inverse by LU with one factorization and n back substitutions.
Matrix< T > matmul(const Matrix< T > &A, const Matrix< T > &B)
Matrix product A B.
std::vector< T > solve(const Matrix< T > &A, const std::vector< T > &b)
Convenience: solve Ax = b, leaving A and b untouched.
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...
double atol
absolute tolerance, applied to every component
double rtol
relative tolerance, applied to every component
Result of an integration, mirroring OdeSolution in ode.h.
bool success
false when LSODA returned istate < 0
std::vector< std::vector< double > > y
y[i] is the state at t[i]
std::vector< double > t
output times, t[0] = t_eval[0]
Result of an integration.
std::vector< std::vector< T > > y
y[i] is the state at t[i]
std::vector< T > t
accepted time points, t[0] = t0
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
bool refined
true when the 1/N correction was accepted
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
Result of cache_miss_rmf_expansion_transient.
std::vector< Matrix< T > > W
(n_points) covariance, each model_dim x model_dim
Matrix< T > X
(n_points x model_dim) mean-field trajectory
Matrix< T > V
(n_points x model_dim) 1/N correction trajectory
std::vector< T > t
(n_points) output instants, t[0] = 0