1function [QN, UN, RN, TN, xvec_iter, QNt, UNt, TNt, xvec_t, t] = solver_fluid_rmf(sn, options)
2% SOLVER_FLUID_RMF Refined mean field solver
for multi-list cache analysis
4% Implements mean field and refined mean field (1/N correction) analysis
5%
for multi-list caches with RANDOM(m) replacement policy, based on the
6% DDPP (Density-Dependent Population Process) framework.
8% The cache
is modeled as a population process where each item can be in
9% one of h+1 states: list 1, list 2, ..., list h, or outside the cache
10% (list 0). Transitions occur when an item
is requested: the requested
11% item moves up (toward list 1) while a displaced item moves down
15% N. Gast,
"Expected Values Estimated via Mean-Field Approximation are
16% 1/N-Accurate", Proc. ACM Meas. Anal. Comput. Syst., 2017.
18% Copyright (c) 2012-2026, Imperial College London
24% Initialize output arrays
30% Initialize transient outputs
36 QNt{ist, r} = zeros(2, 1);
37 UNt{ist, r} = zeros(2, 1);
38 TNt{ist, r} = zeros(2, 1);
43xvec_iter = {zeros(1, 1)};
47 if sn.nodetype(ind) ~= NodeType.Cache
50 ch = sn.nodeparam{ind};
55 % Extract cache parameters
59 h = length(itemcap); % number of lists
60 m = itemcap(:)
'; % row vector of list capacities
62 % Initialize actual hit/miss probability arrays if needed
63 if ~isfield(ch, 'actualhitprob
') || isempty(ch.actualhitprob)
64 ch.actualhitprob = zeros(1, K);
66 if ~isfield(ch, 'actualmissprob
') || isempty(ch.actualmissprob)
67 ch.actualmissprob = zeros(1, K);
72 if r > length(pread) || isempty(pread{r}) || all(isnan(pread{r}))
77 if length(p) ~= n_items
81 % Normalize popularity distribution
88 % Build and solve the DDPP cache model
89 model_dim = n_items * (h + 1);
91 % Build initial state: first m(1) items in list 1, next m(2) in
92 % list 2, etc.; remaining items outside cache (list 0)
93 x0 = zeros(model_dim, 1);
97 obj_idx = obj_idx + 1;
99 x0(rmf_index(obj_idx, k, n_items)) = 1.0;
103 for i = (obj_idx + 1):n_items
104 x0(rmf_index(i, 0, n_items)) = 1.0;
107 % Compute mean field fixed point by ODE integration
108 pi = rmf_fixed_point(x0, p, m, n_items, h, model_dim);
110 % Compute hit/miss probabilities from fixed point
113 hit_prob = hit_prob + rmf_hit_rate(pi, p, k, n_items);
115 miss_prob = rmf_hit_rate(pi, p, 0, n_items);
117 % Try refined mean field (1/N correction)
119 [pi_mf, V] = rmf_expansion_steady_state(x0, p, m, n_items, h, model_dim);
120 pi_refined = pi_mf + V / n_items;
122 % Recompute hit/miss with refined approximation
123 hit_prob_refined = 0.0;
125 hit_prob_refined = hit_prob_refined + rmf_hit_rate(pi_refined, p, k, n_items);
127 miss_prob_refined = rmf_hit_rate(pi_refined, p, 0, n_items);
128 hit_prob = hit_prob_refined;
129 miss_prob = miss_prob_refined;
131 % Fall back to plain mean field
134 % Store actual hit/miss probabilities, clipped to [0, 1]
135 ch.actualhitprob(r) = max(0, min(1, hit_prob));
136 ch.actualmissprob(r) = max(0, min(1, miss_prob));
140 sn.nodeparam{ind} = ch;
145%% ========================================================================
146% Helper functions
for DDPP cache model
147% ========================================================================
149function idx = rmf_index(i, k, n_items)
150% RMF_INDEX Map (item i, list k) to flat state index
151% i: item index (1-based)
152% k: list index (0 = outside cache, 1..h = cache lists)
153% n_items: total number of items
154idx = i + k * n_items;
157function hr = rmf_hit_rate(x, p, list_number, n_items)
158% RMF_HIT_RATE Compute hit rate contribution from a specific list
159% hr = sum_i p(i) * x(index(i, list_number))
162 hr = hr + p(i) * x(rmf_index(i, list_number, n_items));
166function dX = rmf_drift(x, p, m, n_items, h, model_dim)
167% RMF_DRIFT Compute mean field drift F(x)
for RANDOM(m) replacement
169% dx[i,k]/dt = -p(i)*x[i,k] + hitRate[k]*x[i,k+1]/m(k) (promotion from k to k+1)
170% dx[i,k+1]/dt = p(i)*x[i,k] - hitRate[k]*x[i,k+1]/m(k)
172hit_rates = zeros(1, h + 1);
174 hit_rates(k + 1) = rmf_hit_rate(x, p, k, n_items);
177dX = zeros(model_dim, 1);
180 flow = p(i) * x(rmf_index(i, k, n_items)) ...
181 - hit_rates(k + 1) * x(rmf_index(i, k + 1, n_items)) / m(k + 1);
182 dX(rmf_index(i, k, n_items)) = dX(rmf_index(i, k, n_items)) - flow;
183 dX(rmf_index(i, k + 1, n_items)) = dX(rmf_index(i, k + 1, n_items)) + flow;
188function Fp = rmf_jacobian(x, p, m, n_items, h, model_dim)
189% RMF_JACOBIAN Compute Jacobian dF/dx at state x
191hit_rates = zeros(1, h + 1);
193 hit_rates(k + 1) = rmf_hit_rate(x, p, k, n_items);
196Fp = zeros(model_dim, model_dim);
199 ik = rmf_index(i, k, n_items);
200 ik1 = rmf_index(i, k + 1, n_items);
203 Fp(ik, ik) = Fp(ik, ik) - p(i);
204 Fp(ik1, ik) = Fp(ik1, ik) + p(i);
205 Fp(ik, ik1) = Fp(ik, ik1) + hit_rates(k + 1) / m(k + 1);
206 Fp(ik1, ik1) = Fp(ik1, ik1) - hit_rates(k + 1) / m(k + 1);
208 % Indirect terms via hit rate dependence on x(j,k)
210 jk = rmf_index(j, k, n_items);
211 jk1 = rmf_index(j, k + 1, n_items);
212 Fp(ik, jk1) = Fp(ik, jk1) - p(i) * x(ik) / m(k + 1);
213 Fp(ik1, jk1) = Fp(ik1, jk1) + p(i) * x(ik) / m(k + 1);
214 Fp(ik, jk) = Fp(ik, jk) + p(j) * x(ik1) / m(k + 1);
215 Fp(ik1, jk) = Fp(ik1, jk) - p(j) * x(ik1) / m(k + 1);
221function Fpp = rmf_hessian(~, p, m, n_items, h, model_dim)
222% RMF_HESSIAN Compute Hessian d^2F/dx^2 (constant
for this quadratic drift)
224Fpp = zeros(model_dim, model_dim, model_dim);
227 ik = rmf_index(i, k, n_items);
228 ik1 = rmf_index(i, k + 1, n_items);
231 jk = rmf_index(j, k, n_items);
232 jk1 = rmf_index(j, k + 1, n_items);
233 % d^2 F[ik] / (d x[jk] d x[ik1])
234 Fpp(ik, jk, ik1) = Fpp(ik, jk, ik1) + p(j) / m(k + 1);
235 Fpp(ik, ik1, jk) = Fpp(ik, ik1, jk) + p(j) / m(k + 1);
236 % d^2 F[ik] / (d x[jk1] d x[ik])
237 Fpp(ik, jk1, ik) = Fpp(ik, jk1, ik) - p(i) / m(k + 1);
238 Fpp(ik, ik, jk1) = Fpp(ik, ik, jk1) - p(i) / m(k + 1);
240 Fpp(ik1, jk, ik1) = Fpp(ik1, jk, ik1) - p(j) / m(k + 1);
241 Fpp(ik1, ik1, jk) = Fpp(ik1, ik1, jk) - p(j) / m(k + 1);
242 Fpp(ik1, jk1, ik) = Fpp(ik1, jk1, ik) + p(i) / m(k + 1);
243 Fpp(ik1, ik, jk1) = Fpp(ik1, ik, jk1) + p(i) / m(k + 1);
250function Q = rmf_noise_matrix(x, p, m, n_items, h, model_dim)
251% RMF_NOISE_MATRIX Compute noise intensity matrix Q(x)
for the DDPP
253% Q[a,b] = sum_ell ell[a]*ell[b]*beta_ell(x)
254% Each transition swaps items i and j across lists k and k+1.
256Q = zeros(model_dim, model_dim);
257signs = [-1, 1, 1, -1];
261 rate = p(i) * x(rmf_index(i, k, n_items)) ...
262 * x(rmf_index(j, k + 1, n_items)) / m(k + 1);
263 indices = [rmf_index(i, k, n_items), ...
264 rmf_index(j, k, n_items), ...
265 rmf_index(i, k + 1, n_items), ...
266 rmf_index(j, k + 1, n_items)];
269 Q(indices(ia), indices(ib)) = Q(indices(ia), indices(ib)) ...
270 + rate * signs(ia) * signs(ib);
278function pi = rmf_fixed_point(x0, p, m, n_items, h, model_dim)
279% RMF_FIXED_POINT Compute mean field fixed point by ODE integration
281% Integrates dx/dt = F(x) until steady state
using LSODA.
284ode_func = @(t, x) rmf_drift(x, p, m, n_items, h, model_dim);
285odeopt = odeset(
'AbsTol', 1e-10,
'RelTol', 1e-8);
286%[~, xvec] = ode15s(ode_func, [0, tmax], x0, odeopt);
287[~, xvec] = lsoda_solve(ode_func, [0, tmax], x0, odeopt);
291function [C, Cinv, rk] = rmf_dimension_reduction(Fp, n_items, h, model_dim)
292% RMF_DIMENSION_REDUCTION Compute change-of-basis for singular Jacobian
294% The Jacobian is singular because item populations are conserved
295% (sum over lists for each item = 1). Returns matrices to project
296% onto the non-singular subspace.
300C = zeros(model_dim, model_dim);
303 for i = 1:(n_items - 1)
305 C(d, rmf_index(i, l_idx, n_items)) = 1.0;
310C((rk + 1):model_dim, :) = U(:, (rk + 1):model_dim)';
314function [pi, V] = rmf_expansion_steady_state(x0, p, m, n_items, h, model_dim)
315% RMF_EXPANSION_STEADY_STATE Compute refined mean field steady-state expansion
317% Computes the mean field fixed point pi and the 1/N correction V
318%
using the Lyapunov equation approach with dimension reduction.
320% The refined approximation
for a system of N items
is:
321% E[X] ~ pi + V/N + O(1/N^2)
323pi = rmf_fixed_point(x0, p, m, n_items, h, model_dim);
325Fp = rmf_jacobian(pi, p, m, n_items, h, model_dim);
326Fpp = rmf_hessian(pi, p, m, n_items, h, model_dim);
327Q = rmf_noise_matrix(pi, p, m, n_items, h, model_dim);
329% Dimension reduction: project onto non-singular subspace
330[C, Cinv, rk] = rmf_dimension_reduction(Fp, n_items, h, model_dim);
332Fp_r = (C * Fp * Cinv);
333Fp_r = Fp_r(1:rk, 1:rk);
335% Reduce Hessian: Fpp_r(a,b,c) = sum_{i,j,k} C(a,i)*Fpp(i,j,k)*Cinv(j,b)*Cinv(k,c)
336% First contraction: tmp1(a,j,k) = sum_i C(a,i)*Fpp(i,j,k)
337tmp1 = zeros(model_dim, model_dim, model_dim);
341 tmp1(a, j, k) = C(a, :) * Fpp(:, j, k);
345% Second contraction: tmp2(a,b,k) = sum_j tmp1(a,j,k)*Cinv(j,b)
346tmp2 = zeros(rk, rk, model_dim);
350 tmp2(a, b, k) = tmp1(a, :, k) * Cinv(:, b);
354% Third contraction: Fpp_r(a,b,c) = sum_k tmp2(a,b,k)*Cinv(k,c)
355Fpp_r = zeros(rk, rk, rk);
358 Fpp_r(a, b, :) = reshape(tmp2(a, b, :), 1, []) * Cinv(:, 1:rk);
363Q_r = Q_r(1:rk, 1:rk);
365% Solve Lyapunov equation: Fp_r * W_r + W_r * Fp_r' + Q_r = 0
366W_r = lyap(Fp_r, Q_r);
368% First-order correction: V_r = -Fp_r \ (C_r / 2)
369% where C_r = sum_{b,c} Fpp_r(:,b,c) * W_r(b,c)
374 C_r(a) = C_r(a) + Fpp_r(a, b, c) * W_r(b, c);
378V_r = -Fp_r \ (C_r / 2.0);
380% Expand back to full dimension
381V = Cinv(:, 1:rk) * V_r;