5#ifndef LINE_API_CACHE_CACHE_MISS_RMF_H
6#define LINE_API_CACHE_CACHE_MISS_RMF_H
107#include <type_traits>
125 return i + k * n_items;
145namespace rmf_detail {
149T hit_rate(
const std::vector<T>& x,
const std::vector<T>& p, std::size_t level,
150 std::size_t n_items) {
152 for (std::size_t i = 0; i < n_items; ++i) hr += p[i] * x[
cache_miss_rmf_index(i, level, n_items)];
158std::vector<T> drift(
const std::vector<T>& x,
const std::vector<T>& p,
const std::vector<T>& m,
159 std::size_t n_items, std::size_t h) {
161 const std::size_t model_dim = n_items * (h + 1);
162 std::vector<T> hr(h + 1, zero);
163 for (std::size_t k = 0; k <= h; ++k) hr[k] = hit_rate(x, p, k, n_items);
164 std::vector<T> dX(model_dim, zero);
165 for (std::size_t i = 0; i < n_items; ++i) {
166 for (std::size_t k = 0; k + 1 <= h; ++k) {
169 const T flow = p[i] * x[ik] - hr[k] * x[ik1] / m[k];
201Matrix<T> jacobian(
const std::vector<T>& x,
const std::vector<T>& p,
const std::vector<T>& m,
202 std::size_t n_items, std::size_t h) {
203 const T zero = num_traits<T>::from_int(0);
204 const std::size_t model_dim = n_items * (h + 1);
205 std::vector<T> hr(h + 1, zero);
206 for (std::size_t k = 0; k <= h; ++k) hr[k] = hit_rate(x, p, k, n_items);
207 Matrix<T> Fp(model_dim, model_dim, zero);
208 for (std::size_t i = 0; i < n_items; ++i) {
209 for (std::size_t k = 0; k + 1 <= h; ++k) {
214 Fp(ik, ik1) += hr[k] / m[k];
215 Fp(ik1, ik1) -= hr[k] / m[k];
216 for (std::size_t j = 0; j < n_items; ++j) {
219 Fp(ik, jk1) -= p[i] * x[ik] / m[k];
220 Fp(ik1, jk1) += p[i] * x[ik] / m[k];
221 Fp(ik, jk) += p[j] * x[ik1] / m[k];
222 Fp(ik1, jk) -= p[j] * x[ik1] / m[k];
235std::vector<T> hessian(
const std::vector<T>& p,
const std::vector<T>& m, std::size_t n_items,
237 const T zero = num_traits<T>::from_int(0);
238 const std::size_t md = n_items * (h + 1);
239 std::vector<T> H(md * md * md, zero);
240 const auto at = [md](std::size_t a, std::size_t b, std::size_t c) {
241 return (a * md + b) * md + c;
243 for (std::size_t i = 0; i < n_items; ++i) {
244 for (std::size_t k = 0; k + 1 <= h; ++k) {
247 for (std::size_t j = 0; j < n_items; ++j) {
248 if (j == i)
continue;
251 H[at(ik, jk, ik1)] += p[j] / m[k];
252 H[at(ik, ik1, jk)] += p[j] / m[k];
253 H[at(ik, jk1, ik)] -= p[i] / m[k];
254 H[at(ik, ik, jk1)] -= p[i] / m[k];
255 H[at(ik1, jk, ik1)] -= p[j] / m[k];
256 H[at(ik1, ik1, jk)] -= p[j] / m[k];
257 H[at(ik1, jk1, ik)] += p[i] / m[k];
258 H[at(ik1, ik, jk1)] += p[i] / m[k];
267Matrix<T> noise_matrix(
const std::vector<T>& x,
const std::vector<T>& p,
const std::vector<T>& m,
268 std::size_t n_items, std::size_t h) {
269 const T zero = num_traits<T>::from_int(0);
270 const std::size_t md = n_items * (h + 1);
271 Matrix<T> Q(md, md, zero);
272 const int signs[4] = {-1, 1, 1, -1};
273 for (std::size_t i = 0; i < n_items; ++i) {
274 for (std::size_t k = 0; k + 1 <= h; ++k) {
275 for (std::size_t j = 0; j < n_items; ++j) {
282 for (
int ia = 0; ia < 4; ++ia)
283 for (
int ib = 0; ib < 4; ++ib)
284 Q(idx[ia], idx[ib]) +=
285 rate * num_traits<T>::from_int(signs[ia] * signs[ib]);
299std::vector<T> fixed_point(
const std::vector<T>& x0,
const std::vector<T>& p,
300 const std::vector<T>& m, std::size_t n_items, std::size_t h,
301 const T& tmax,
const T& rtol,
const T& atol) {
305 opt.store_trajectory =
false;
306 const auto f = [&](
const T& t,
const std::vector<T>& x) {
308 return drift(x, p, m, n_items, h);
311 return ode_rosenbrock4(f, T(num_traits<T>::from_int(0)), tmax, x0, opt).final_state();
316std::vector<T> fixed_point(
const std::vector<T>& x0,
const std::vector<T>& p,
317 const std::vector<T>& m, std::size_t n_items, std::size_t h,
319 return fixed_point(x0, p, m, n_items, h, tmax, T(num_traits<T>::from_double(1e-8)),
320 T(num_traits<T>::from_double(1e-10)));
338Matrix<T> lyapunov(
const Matrix<T>& F,
const Matrix<T>& Q) {
339 const std::size_t r = F.rows();
340 const T zero = num_traits<T>::from_int(0);
341 Matrix<T> A(r * r, r * r, zero);
342 std::vector<T> rhs(r * r, zero);
343 for (std::size_t a = 0; a < r; ++a)
344 for (std::size_t b = 0; b < r; ++b) {
345 const std::size_t row = a * r + b;
346 for (std::size_t c = 0; c < r; ++c) {
347 A(row, c * r + b) += F(a, c);
348 A(row, a * r + c) += F(b, c);
353 Matrix<T> W(r, r, zero);
354 for (std::size_t a = 0; a < r; ++a)
355 for (std::size_t b = 0; b < r; ++b) W(a, b) = w[a * r + b];
380 std::size_t rank = 0;
381 std::vector<std::vector<T>> basis;
385NullSpace<T> left_null_space(
const Matrix<T>& Fp) {
386 const std::size_t n = Fp.rows();
387 const T zero = num_traits<T>::from_int(0);
389 Matrix<T> M(n, n, zero);
391 for (std::size_t i = 0; i < n; ++i)
392 for (std::size_t j = 0; j < n; ++j) {
395 if (a > scale) scale = a;
397 if (scale == zero) scale = num_traits<T>::from_int(1);
400 const T tol = scale * num_traits<T>::from_double(1e-8);
402 std::vector<std::size_t> col_of_pivot;
403 std::vector<std::size_t> perm(n);
404 for (std::size_t i = 0; i < n; ++i) perm[i] = i;
406 for (std::size_t col = 0; col < n && row < n; ++col) {
409 for (std::size_t i = row + 1; i < n; ++i) {
410 const T a =
num_abs(M(i, col));
416 if (best <= tol)
continue;
418 for (std::size_t j = 0; j < n; ++j) std::swap(M(row, j), M(p, j));
419 const T d = M(row, col);
420 for (std::size_t j = 0; j < n; ++j) M(row, j) = M(row, j) / d;
421 for (std::size_t i = 0; i < n; ++i) {
422 if (i == row)
continue;
423 const T f = M(i, col);
424 if (f == zero)
continue;
425 for (std::size_t j = 0; j < n; ++j) M(i, j) -= f * M(row, j);
427 col_of_pivot.push_back(col);
432 ns.rank = col_of_pivot.size();
433 std::vector<bool> is_pivot(n,
false);
434 for (std::size_t c : col_of_pivot) is_pivot[c] =
true;
436 for (std::size_t free_col = 0; free_col < n; ++free_col) {
437 if (is_pivot[free_col])
continue;
438 std::vector<T> v(n, zero);
439 v[free_col] = num_traits<T>::from_int(1);
440 for (std::size_t r = 0; r < col_of_pivot.size(); ++r)
441 v[col_of_pivot[r]] = -M(r, free_col);
442 ns.basis.push_back(v);
446 for (std::size_t i = 0; i < ns.basis.size(); ++i) {
447 for (std::size_t k = 0; k < i; ++k) {
449 for (std::size_t j = 0; j < n; ++j) dot += ns.basis[i][j] * ns.basis[k][j];
450 for (std::size_t j = 0; j < n; ++j) ns.basis[i][j] -= dot * ns.basis[k][j];
453 for (std::size_t j = 0; j < n; ++j) nrm2 += ns.basis[i][j] * ns.basis[i][j];
454 const T nrm = sqrt(nrm2);
455 if (nrm <= tol)
throw NumericError(
"cache_miss_rmf: the null-space basis degenerated");
456 for (std::size_t j = 0; j < n; ++j) ns.basis[i][j] = ns.basis[i][j] / nrm;
478inline NullSpace<double> left_null_space_svd(
const Matrix<double>& Fp) {
479#ifndef LINE_MP_HAVE_LAPACK
480 return left_null_space(Fp);
482 const std::size_t n = Fp.rows();
483 std::vector<double> a(n * n);
484 for (std::size_t i = 0; i < n; ++i)
485 for (std::size_t j = 0; j < n; ++j) a[j * n + i] = Fp(i, j);
486 const int ni =
static_cast<int>(n);
487 std::vector<double> s(n), u(n * n), vt(1);
488 int info = 0, lwork = -1;
491 dgesvd_(
"A",
"N", &ni, &ni, a.data(), &ni, s.data(), u.data(), &ni, vt.data(), &one, &wopt,
493 if (info != 0)
throw NumericError(
"cache_miss_rmf: LAPACK workspace query failed");
494 lwork =
static_cast<int>(wopt);
495 std::vector<double> work(
static_cast<std::size_t
>(lwork));
496 dgesvd_(
"A",
"N", &ni, &ni, a.data(), &ni, s.data(), u.data(), &ni, vt.data(), &one,
497 work.data(), &lwork, &info);
498 if (info != 0)
throw NumericError(
"cache_miss_rmf: LAPACK dgesvd failed to converge");
500 const double tol = 1e-8 * s[0];
501 NullSpace<double> ns;
504 if (v > tol) ++ns.rank;
505 for (std::size_t c = ns.rank; c < n; ++c) {
506 std::vector<double> row(n, 0.0);
507 for (std::size_t i = 0; i < n; ++i) row[i] = u[c * n + i];
508 ns.basis.push_back(row);
516NullSpace<T> left_null_space_for(
const Matrix<T>& Fp) {
517 return left_null_space(Fp);
521inline NullSpace<double> left_null_space_for<double>(
const Matrix<double>& Fp) {
522 return left_null_space_svd(Fp);
534Matrix<T> linear_graph(std::size_t h) {
535 const T zero = num_traits<T>::from_int(0), one = num_traits<T>::from_int(1);
536 Matrix<T> g(h + 1, h + 1, zero);
538 for (std::size_t a = 1; a + 1 <= h; ++a) g(a, a + 1) = one;
555std::vector<Matrix<T> > build_item_graphs(
const std::vector<std::vector<Matrix<T> > >& accost,
556 const Matrix<T>& lambda, std::size_t n, std::size_t h) {
557 std::vector<Matrix<T> > G;
558 if (accost.empty())
return G;
559 const T zero = num_traits<T>::from_int(0);
560 const Matrix<T> lin = linear_graph<T>(h);
561 const std::size_t u = accost.size();
562 std::vector<Matrix<T> > Gc(n, lin);
563 bool is_linear =
true;
564 for (std::size_t k = 0; k < n; ++k) {
565 Matrix<T> num(h + 1, h + 1, zero);
567 for (std::size_t v = 0; v < u; ++v) {
568 if (k >= accost[v].size())
continue;
569 const Matrix<T>& gvk = accost[v][k];
570 if (gvk.rows() == 0)
continue;
571 if (gvk.rows() != h + 1 || gvk.cols() != h + 1)
572 throw InputError(
"cache_miss_rmf: an access graph is not (h+1)x(h+1)");
573 double w = (v < lambda.rows() && k < lambda.cols())
574 ? num_traits<T>::to_double(lambda(v, k))
576 if (!std::isfinite(w)) w = 0.0;
577 const T wv = num_traits<T>::from_double(w);
578 for (std::size_t a = 0; a <= h; ++a)
579 for (std::size_t b = 0; b <= h; ++b) num(a, b) += wv * gvk(a, b);
584 for (std::size_t a = 0; a <= h; ++a)
585 for (std::size_t b = 0; b <= h; ++b) gk(a, b) = num(a, b) / den;
586 }
else if (!accost.empty() && k < accost[0].size() && accost[0][k].rows() == h + 1) {
589 for (std::size_t a = 0; a <= h; ++a) {
591 for (std::size_t b = 0; b <= h; ++b) srow += gk(a, b);
593 for (std::size_t b = 0; b <= h; ++b) gk(a, b) = gk(a, b) / srow;
596 for (std::size_t a = 0; a <= h && is_linear; ++a)
597 for (std::size_t b = 0; b <= h && is_linear; ++b)
598 if (std::fabs(num_traits<T>::to_double(gk(a, b)) -
599 num_traits<T>::to_double(lin(a, b))) >= 1e-9)
602 if (!is_linear) G = Gc;
617std::vector<T> drift_graph(
const std::vector<T>& x_in,
const std::vector<T>& p,
618 const std::vector<Matrix<T> >& G,
const std::vector<T>& m,
619 std::size_t n, std::size_t h) {
620 const T zero = num_traits<T>::from_int(0), one = num_traits<T>::from_int(1);
621 const std::size_t model_dim = n * (h + 1);
622 std::vector<T> x = x_in;
623 for (std::size_t a = 0; a < x.size(); ++a) {
624 if (x[a] < zero) x[a] = zero;
625 if (x[a] > one) x[a] = one;
628 Matrix<T> A(h + 1, h + 1, zero);
629 for (std::size_t s = 0; s <= h; ++s)
630 for (std::size_t j = 0; j < n; ++j) {
632 if (xjs == zero)
continue;
633 for (std::size_t i = 1; i <= h; ++i) A(s, i) += p[j] * xjs * G[j](s, i);
635 std::vector<T> dX(model_dim, zero);
636 for (std::size_t k = 0; k < n; ++k) {
638 for (std::size_t i = 1; i <= h; ++i) {
640 T infl = p[k] * outk * G[k](0, i);
641 for (std::size_t s = 1; s + 1 <= i; ++s)
643 for (std::size_t b = i + 1; b <= h; ++b)
645 T outfl = p[k] * xki * T(one - G[k](i, i));
647 for (std::size_t s = 0; s + 1 <= i; ++s) disp += A(s, i);
648 outfl += disp * xki / m[i - 1];
664std::vector<T> fixed_point_graph(
const std::vector<T>& x0,
const std::vector<T>& p,
665 const std::vector<Matrix<T> >& G,
const std::vector<T>& m,
666 std::size_t n, std::size_t h) {
668 opt.rtol = num_traits<T>::from_double(1e-8);
669 opt.atol = num_traits<T>::from_double(1e-10);
670 opt.store_trajectory =
false;
671 const auto f = [&](
const T& t,
const std::vector<T>& x) {
673 return drift_graph(x, p, G, m, n, h);
676 T(num_traits<T>::from_int(20000)), x0, opt)
703 const std::vector<std::vector<
Matrix<T> > >& accost) {
705 "cache_miss_rmf requires transcendental arithmetic: its fixed point is reached "
706 "by a tolerance-driven integration of the mean-field drift");
710 const std::size_t u = lambda.
rows();
711 const std::size_t n_items = lambda.
cols();
712 const std::size_t h = m_in.size();
713 if (u == 0 || n_items == 0)
throw InputError(
"cache_miss_rmf: empty request-rate matrix");
714 if (h == 0)
throw InputError(
"cache_miss_rmf: at least one cache list is required");
716 std::vector<T> m(h, zero);
717 for (std::size_t k = 0; k < h; ++k) {
718 if (m_in[k] <= 0)
throw InputError(
"cache_miss_rmf: a list has non-positive capacity");
723 std::vector<T> lam_i(n_items, zero);
725 for (std::size_t v = 0; v < u; ++v)
726 for (std::size_t i = 0; i < n_items; ++i) {
727 lam_i[i] += lambda(v, i);
728 lam_tot += lambda(v, i);
730 if (lam_tot == zero)
throw InputError(
"cache_miss_rmf: all request rates are zero");
731 std::vector<T> p(n_items, zero);
732 for (std::size_t i = 0; i < n_items; ++i) p[i] = lam_i[i] / lam_tot;
734 const std::size_t model_dim = n_items * (h + 1);
738 std::vector<T> x0(model_dim, zero);
740 for (std::size_t k = 1; k <= h; ++k)
741 for (
int jj = 0; jj < m_in[k - 1]; ++jj) {
748 const std::vector<Matrix<T> > G = rmf_detail::build_item_graphs(accost, lambda, n_items, h);
752 res.
xss = rmf_detail::fixed_point_graph(x0, p, G, m, n_items, h);
753 res.
pi0.assign(n_items, zero);
754 for (std::size_t i = 0; i < n_items; ++i) {
756 if (v < zero) v = zero;
757 if (v > one) v = one;
760 res.
MI.assign(n_items, zero);
762 for (std::size_t i = 0; i < n_items; ++i) {
763 res.
MI[i] = lam_i[i] * res.
pi0[i];
766 res.
MU.assign(u, zero);
767 for (std::size_t v = 0; v < u; ++v) {
769 for (std::size_t i = 0; i < n_items; ++i) s += lambda(v, i) * res.
pi0[i];
775 std::vector<T> xss = rmf_detail::fixed_point(x0, p, m, n_items, h, tmax);
776 xss = rmf_detail::fixed_point(xss, p, m, n_items, h, tmax,
782 const Matrix<T> Fp = rmf_detail::jacobian(xss, p, m, n_items, h);
783 const rmf_detail::NullSpace<T> ns = rmf_detail::left_null_space_for(Fp);
784 const std::size_t rk = ns.rank;
785 if (rk == 0 || rk >= model_dim)
786 throw NumericError(
"cache_miss_rmf: the reduction is degenerate");
787 const std::vector<T> Fpp = rmf_detail::hessian(p, m, n_items, h);
788 const Matrix<T> Q = rmf_detail::noise_matrix(xss, p, m, n_items, h);
794 for (std::size_t l = 0; l <= h && d < rk; ++l)
795 for (std::size_t i = 0; i + 1 < n_items && d < rk; ++i, ++d)
797 if (d != rk)
throw NumericError(
"cache_miss_rmf: the reduction basis is too small");
799 if (ns.basis.size() != model_dim - rk)
800 throw NumericError(
"cache_miss_rmf: the null-space basis has the wrong size");
801 for (std::size_t i = 0; i < ns.basis.size(); ++i)
802 for (std::size_t j = 0; j < model_dim; ++j) C(rk + i, j) = ns.basis[i][j];
808 for (std::size_t a = 0; a < rk; ++a)
809 for (std::size_t b = 0; b < rk; ++b) Fp_r(a, b) = tmp(a, b);
813 Matrix<T> Ct(model_dim, model_dim, zero);
814 for (std::size_t a = 0; a < model_dim; ++a)
815 for (std::size_t b = 0; b < model_dim; ++b) Ct(a, b) = C(b, a);
817 for (std::size_t a = 0; a < rk; ++a)
818 for (std::size_t b = 0; b < rk; ++b) Q_r(a, b) = tmp(a, b);
822 const auto Hat = [model_dim](std::size_t a, std::size_t b, std::size_t c) {
823 return (a * model_dim + b) * model_dim + c;
825 std::vector<T> tmp1(rk * model_dim * model_dim, zero);
826 for (std::size_t a = 0; a < rk; ++a)
827 for (std::size_t j = 0; j < model_dim; ++j)
828 for (std::size_t k = 0; k < model_dim; ++k) {
830 for (std::size_t i = 0; i < model_dim; ++i) s += C(a, i) * Fpp[Hat(i, j, k)];
831 tmp1[(a * model_dim + j) * model_dim + k] = s;
833 std::vector<T> tmp2(rk * rk * model_dim, zero);
834 for (std::size_t a = 0; a < rk; ++a)
835 for (std::size_t b = 0; b < rk; ++b)
836 for (std::size_t k = 0; k < model_dim; ++k) {
838 for (std::size_t j = 0; j < model_dim; ++j)
839 s += tmp1[(a * model_dim + j) * model_dim + k] * Cinv(j, b);
840 tmp2[(a * rk + b) * model_dim + k] = s;
842 std::vector<T> Fpp_r(rk * rk * rk, zero);
843 for (std::size_t a = 0; a < rk; ++a)
844 for (std::size_t b = 0; b < rk; ++b)
845 for (std::size_t c = 0; c < rk; ++c) {
847 for (std::size_t k = 0; k < model_dim; ++k)
848 s += tmp2[(a * rk + b) * model_dim + k] * Cinv(k, c);
849 Fpp_r[(a * rk + b) * rk + c] = s;
852 const Matrix<T> W_r = rmf_detail::lyapunov(Fp_r, Q_r);
854 std::vector<T> C_r(rk, zero);
855 for (std::size_t a = 0; a < rk; ++a) {
857 for (std::size_t b = 0; b < rk; ++b)
858 for (std::size_t c = 0; c < rk; ++c) s += Fpp_r[(a * rk + b) * rk + c] * W_r(b, c);
861 std::vector<T> rhs(rk, zero);
862 for (std::size_t a = 0; a < rk; ++a)
866 std::vector<T> xref(model_dim, zero);
867 for (std::size_t i = 0; i < model_dim; ++i) {
869 for (std::size_t a = 0; a < rk; ++a) s += Cinv(i, a) * V_r[a];
881 res.
pi0.assign(n_items, zero);
882 for (std::size_t i = 0; i < n_items; ++i) {
884 if (v < zero) v = zero;
885 if (v > one) v = one;
888 res.
MI.assign(n_items, zero);
890 for (std::size_t i = 0; i < n_items; ++i) {
891 res.
MI[i] = lam_i[i] * res.
pi0[i];
894 res.
MU.assign(u, zero);
895 for (std::size_t v = 0; v < u; ++v) {
897 for (std::size_t i = 0; i < n_items; ++i) s += lambda(v, i) * res.
pi0[i];
906 const Matrix<T>& lambda,
const T& tmax) {
929 const Matrix<T>& lambda,
const T& t0,
const T& t1,
930 const std::vector<T>& x0init,
931 const std::vector<std::vector<
Matrix<T> > >& accost,
934 "cache_miss_rmf_transient requires transcendental arithmetic");
937 const std::size_t u = lambda.
rows();
938 const std::size_t n_items = lambda.
cols();
939 const std::size_t h = m_in.size();
940 const std::size_t model_dim = n_items * (h + 1);
941 if (x0init.size() != model_dim)
942 throw InputError(
"cache_miss_rmf_transient: initial occupancy has the wrong length");
944 std::vector<T> m(h, zero);
945 for (std::size_t k = 0; k < h; ++k) m[k] = num_traits<T>::from_int(
static_cast<long>(m_in[k]));
947 std::vector<T> lam_i(n_items, zero);
949 for (std::size_t v = 0; v < u; ++v)
950 for (std::size_t i = 0; i < n_items; ++i) {
951 lam_i[i] += lambda(v, i);
952 lam_tot += lambda(v, i);
954 std::vector<T> p(n_items, zero);
955 for (std::size_t i = 0; i < n_items; ++i) p[i] = lam_i[i] / lam_tot;
962 const std::vector<Matrix<T> > G = rmf_detail::build_item_graphs(accost, lambda, n_items, h);
963 const auto f = [&](
const T& t,
const std::vector<T>& x) {
965 if (!G.empty())
return rmf_detail::drift_graph(x, p, G, m, n_items, h);
966 return rmf_detail::drift(x, p, m, n_items, h);
971 const std::size_t nt = s.
t.size();
974 for (std::size_t j = 0; j < nt; ++j)
975 for (std::size_t i = 0; i < model_dim; ++i) res.
xtraj(i, j) = s.
y[j][i];
977 for (std::size_t i = 0; i < n_items; ++i)
978 for (std::size_t j = 0; j < nt; ++j) {
980 if (v < zero) v = zero;
981 if (v > one) v = one;
985 for (std::size_t v = 0; v < u; ++v)
986 for (std::size_t j = 0; j < nt; ++j) {
988 for (std::size_t i = 0; i < n_items; ++i) acc += lambda(v, i) * res.
pi0_t(i, j);
989 res.
MU_t(v, j) = acc;
994 res.
sojourn.MU.assign(u, zero);
995 for (std::size_t v = 0; v < u; ++v) {
997 for (std::size_t i = 0; i < n_items; ++i) {
999 if (pb < zero) pb = zero;
1000 if (pb > one) pb = one;
1001 acc += lambda(v, i) * pb;
1014 const Matrix<T>& lambda,
const T& t0,
const T& t1,
1015 const std::vector<T>& x0init,
1018 std::vector<std::vector<
Matrix<T> > >(), clk);
1027 std::vector<Matrix<T> >
W;
1060 const std::vector<int>& m_in,
const Matrix<T>& lambda,
const T& time, std::size_t n_points,
1062 if (!std::is_same<T, double>::value)
1064 "cache_miss_rmf_expansion_transient: the coupled (X,V,W) system is integrated with "
1065 "LSODA, whose coefficients assume double precision; rerun with --arith double");
1067 const std::size_t u = lambda.
rows();
1068 const std::size_t n_items = lambda.
cols();
1069 const std::size_t h = m_in.size();
1070 if (u == 0 || n_items == 0)
1071 throw InputError(
"cache_miss_rmf_expansion_transient: empty request-rate matrix");
1073 throw InputError(
"cache_miss_rmf_expansion_transient: at least one cache list is required");
1075 throw InputError(
"cache_miss_rmf_expansion_transient: at least two output points are "
1076 "required to describe a trajectory");
1078 throw InputError(
"cache_miss_rmf_expansion_transient: the horizon must be positive");
1080 std::vector<T> m(h, zero);
1081 for (std::size_t k = 0; k < h; ++k) {
1083 throw InputError(
"cache_miss_rmf_expansion_transient: a list has non-positive capacity");
1087 std::vector<T> lam_i(n_items, zero);
1089 for (std::size_t v = 0; v < u; ++v)
1090 for (std::size_t i = 0; i < n_items; ++i) {
1091 lam_i[i] += lambda(v, i);
1092 lam_tot += lambda(v, i);
1094 if (lam_tot == zero)
1095 throw InputError(
"cache_miss_rmf_expansion_transient: all request rates are zero");
1096 std::vector<T> p(n_items, zero);
1097 for (std::size_t i = 0; i < n_items; ++i) p[i] = lam_i[i] / lam_tot;
1099 const std::size_t d = n_items * (h + 1);
1102 std::vector<T> x0(d, zero);
1104 std::size_t obj = 0;
1105 for (std::size_t k = 1; k <= h; ++k)
1106 for (
int jj = 0; jj < m_in[k - 1]; ++jj) {
1111 for (std::size_t i = obj; i < n_items; ++i)
1115 std::vector<double> grid(n_points, 0.0);
1117 for (std::size_t j = 0; j < n_points; ++j)
1118 grid[j] = tend *
static_cast<double>(j) /
static_cast<double>(n_points - 1);
1120 const std::size_t total = order == 0 ? d : d + d + d * d;
1121 std::vector<double> y0(total, 0.0);
1122 for (std::size_t i = 0; i < d; ++i) y0[i] = num_traits<T>::to_double(x0[i]);
1124 const std::vector<T> Fpp = order == 0 ? std::vector<T>() : rmf_detail::hessian(p, m, n_items, h);
1126 const auto rhs = [&](
double t,
const double* y,
double* dy) {
1128 std::vector<T> x(d, zero);
1129 for (std::size_t i = 0; i < d; ++i) x[i] = num_traits<T>::from_double(y[i]);
1130 const std::vector<T> F = rmf_detail::drift(x, p, m, n_items, h);
1131 for (std::size_t i = 0; i < d; ++i) dy[i] = num_traits<T>::to_double(F[i]);
1132 if (order == 0)
return;
1133 const Matrix<T> Fp = rmf_detail::jacobian(x, p, m, n_items, h);
1134 const Matrix<T> Q = rmf_detail::noise_matrix(x, p, m, n_items, h);
1135 for (std::size_t a = 0; a < d; ++a) {
1140 for (std::size_t b = 0; b < d; ++b)
1141 for (std::size_t c = 0; c < d; ++c) {
1142 const T hv = Fpp[(a * d + b) * d + c];
1143 if (hv == zero)
continue;
1149 for (std::size_t a = 0; a < d; ++a)
1150 for (std::size_t b = 0; b < d; ++b) {
1152 for (std::size_t c = 0; c < d; ++c)
1163 if (!s.
success || s.
y.size() != n_points)
1164 throw NumericError(
"cache_miss_rmf_expansion_transient: the coupled (X,V,W) system could "
1165 "not be integrated over the requested horizon");
1168 out.
t.assign(n_points, zero);
1171 out.
W.assign(n_points,
Matrix<T>(d, d, zero));
1172 for (std::size_t j = 0; j < n_points; ++j) {
1174 for (std::size_t i = 0; i < d; ++i) {
1178 if (order == 0)
continue;
1179 for (std::size_t a = 0; a < d; ++a)
1180 for (std::size_t b = 0; b < d; ++b)
Grid-independent sojourn-weighted average of a cache mean-field transient.
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.
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.
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, const std::vector< std::vector< Matrix< T > > > &accost, const CacheSojournClock< T > &clk=CacheSojournClock< T >())
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.
Conservation laws of a layered queueing network, enumerated from its structure.
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
CacheSojournResult< T > sojourn
Grid-independent average of the transient against a clock; empty unless one was given.
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
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.