LINE Solver (C++)
Templated C++ port of the LINE queueing solver
Loading...
Searching...
No Matches
fluid_cacheqn.h
Go to the documentation of this file.
1/*
2 * Copyright (c) 2012-2026, QORE Lab, Imperial College London
3 * All rights reserved.
4 */
5#ifndef LINE_SOLVERS_FLUID_FLUID_CACHEQN_H
6#define LINE_SOLVERS_FLUID_FLUID_CACHEQN_H
7
8/**
9 * @file
10 * @ingroup line_solvers
11 * The INTEGRATED caching-queueing network under the fluid solver: ports of
12 * `solver_fld_cacheqn_analyzer.m` (steady state) and `solver_fld_cacheqn_tran.m`
13 * (transient cache trajectory).
14 *
15 * WHY THIS IS A FIXED POINT AND NOT TWO SOLVES IN SEQUENCE. A cache embedded in
16 * a queueing network splits the flow it receives into a hit stream and a miss
17 * stream, and that split IS the routing of the surrounding network. The flow
18 * reaching the cache is in turn a network throughput. So neither side can be
19 * evaluated first: the two are alternated until the cache arrival rates stop
20 * moving. Between passes the routing has changed, so the visits must be rebuilt
21 * from the rewritten `rtnodes` -- the reference re-enters `sn_refresh_visits`,
22 * and this port's `da_cacheqn` calls `da_recompute_visits_from_rtnodes` in the
23 * same place. Reusing the previous pass's visits would leave every downstream
24 * metric computed against a routing the model no longer has.
25 *
26 * WHAT THIS FILE SUPPLIES, and it is only two things, exactly as the MVA and NC
27 * siblings do: the isolated-cache MISS ALGORITHM and the NETWORK SOLVER.
28 *
29 * miss `cache_miss_rmf`, the refined (1/N-accurate) mean field, or the
30 * position-resolved `cache_miss_fifo_rmf` / `cache_miss_sfifo_rmf`
31 * where the policy or the access graph calls for them
32 * (SolverMVA uses `cache_mva` / `cache_miss_fpi`, SolverNC
33 * `cache_prob_erec` / `cache_miss_spm`)
34 * network the fluid `matrix` method, as the reference's `netsolve` calls
35 * `solver_fluid_matrix`
36 *
37 * WHICH REPLACEMENT POLICIES HAVE A FLUID MODEL AT ALL. Only those with a
38 * drift. RANDOM(m) has one; FIFO(m) shares its STEADY STATE with RANDOM(m) on
39 * the linear access graph (Gast15 Thm 1, pi_FIFO(m) = pi_RAND(m)) but not its
40 * transient; strict FIFO(m) has its own position-resolved drift. LRU, HLRU,
41 * CLIMB and QLRU have none: the characteristic-time (FPI) approximation those
42 * are solved with is not a fluid method, and substituting it here would return
43 * a number produced by a different model under the fluid solver's name. They
44 * are refused, as the reference refuses them.
45 *
46 * FLUID IS AN APPROXIMATION. The drift is the mean-field limit of the queueing
47 * network and the cache miss rates carry a 1/N correction; neither is exact at
48 * finite population, so nothing here reproduces MVA or NC to integrator
49 * tolerance. What IS exact is the structure: hit and miss probability sum to
50 * one, and a cache that holds every item never misses.
51 *
52 * DOUBLE ONLY, and gated with `if constexpr` rather than the runtime check
53 * `solver_fluid.h` uses: the fluid result type is built on `Matrix<double>`
54 * while `da_cacheqn` is templated on T, so the two only meet when T is double.
55 */
56
57#include <cmath>
58#include <cstddef>
59#include <functional>
60#include <string>
61#include <type_traits>
62#include <vector>
63
71#include "line/util/error.h"
73#include "line/util/matrix.h"
74
75namespace line {
76namespace fluid {
77
78/** What the steady analyzer returns: the fluid metrics plus the converged split. */
79template <class T>
82 Matrix<T> hitprob; ///< (ncaches x nclasses), cache order as the node scan
83 Matrix<T> missprob; ///< (ncaches x nclasses)
84 int iter = 0; ///< decomposition sweeps, not integrator steps
85 /**
86 * The struct whose cache self-switch carries the CONVERGED split rather than
87 * the offered one, from which the runner derives ArvR and ResidT. Same
88 * contract as the MVA and NC siblings.
89 */
91};
92
93/** One cache's transient, the per-cache slice of the reference's 3-D outputs. */
94template <class T>
96 std::size_t node = 0; ///< 0-based Cache node index
97 std::vector<T> t; ///< time grid of THIS cache
98 Matrix<T> hitprob_t; ///< (nclasses x nt)
99 Matrix<T> missprob_t; ///< (nclasses x nt)
100 std::vector<T> arate; ///< (nclasses) converged arrival rate at the cache
101 Matrix<T> xocc; ///< (nitems*(h+1) x nt) DDPP occupancy trajectory
102};
103
104namespace detail {
105
106/** 0-based Cache node indices, in the order `find(sn.nodetype == Cache)` gives. */
107template <class T>
108std::vector<std::size_t> fluid_cacheqn_nodes(const qn::NetworkStruct<T>& sn) {
109 std::vector<std::size_t> caches;
110 for (std::size_t nd = 0; nd < sn.nodes.size(); ++nd)
111 if (sn.nodes[nd].nodetype == qn::NodeType::Cache) caches.push_back(nd);
112 return caches;
113}
114
115/**
116 * `accost_is_linear` of the reference: true when every per-(user,item) access
117 * graph is the linear chain (miss -> list 1, hit in list a -> list a+1,
118 * self-loop on the top list), which is the graph `da_cache_isolate` installs
119 * when `accost` is empty.
120 */
121template <class T>
122bool fluid_cacheqn_accost_is_linear(const std::vector<std::vector<Matrix<T> > >& accost,
123 std::size_t h) {
124 if (accost.empty()) return true;
125 const T one = num_traits<T>::from_int(1);
126 const double eps = 1e-9;
127 Matrix<T> lin(h + 1, h + 1, num_traits<T>::from_int(0));
128 for (std::size_t a = 0; a < h; ++a) lin(a, a + 1) = one;
129 lin(h, h) = one;
130 for (std::size_t v = 0; v < accost.size(); ++v)
131 for (std::size_t k = 0; k < accost[v].size(); ++k) {
132 const Matrix<T>& g = accost[v][k];
133 if (g.rows() == 0) continue;
134 if (g.rows() != h + 1 || g.cols() != h + 1) return false;
135 for (std::size_t a = 0; a <= h; ++a)
136 for (std::size_t b = 0; b <= h; ++b)
137 if (std::fabs(num_traits<T>::to_double(g(a, b)) -
138 num_traits<T>::to_double(lin(a, b))) > eps)
139 return false;
140 }
141 return true;
142}
143
144/**
145 * `miss_isolated` of the reference: the drift that answers THIS cache.
146 *
147 * RANDOM(m) always takes `cache_miss_rmf`, which now honours a declared access
148 * graph. FIFO(m) is served from `cache_miss_rmf` on the LINEAR chain, where
149 * Gast15 Thm 1 makes the two steady states identical, and from its own
150 * position-resolved drift otherwise -- the equality is proved for the chain
151 * only. Strict FIFO(m) always takes its own drift; it is a different policy,
152 * not a spelling of FIFO(m).
153 */
154template <class T>
155std::vector<T> fluid_cacheqn_miss(const std::vector<int>& m, const Matrix<T>& lam,
156 const qn::CacheParam<T>& ch) {
157 const bool linear = fluid_cacheqn_accost_is_linear(ch.accost, m.size());
158 switch (ch.replacestrat) {
160 return cache::cache_miss_rmf(std::vector<T>(), m, lam,
161 T(num_traits<T>::from_int(10000)), ch.accost)
162 .MU;
164 if (linear) return cache::cache_miss_rmf(std::vector<T>(), m, lam).MU;
165 return cache::cache_miss_fifo_rmf(std::vector<T>(), m, lam, ch.accost).MU;
167 return cache::cache_miss_sfifo_rmf(std::vector<T>(), m, lam, ch.accost).MU;
168 default:
169 throw UnsupportedError(
170 "solver_fld_cacheqn: replacement strategy " +
171 std::to_string(static_cast<int>(ch.replacestrat)) +
172 " has no drift-based fluid model");
173 }
174}
175
176/**
177 * The admissibility gate, run ONCE before the decomposition starts.
178 *
179 * The reference selects the drift inside `miss_isolated`, per cache and per
180 * sweep, and so does `fluid_cacheqn_miss` above; this gate exists to refuse the
181 * policies that have no drift at all BEFORE a sweep runs, which is the honest
182 * behaviour -- a policy without a drift cannot become supported halfway through
183 * a fixed point.
184 */
185template <class T>
186void fluid_cacheqn_gate(const qn::NetworkStruct<T>& sn, const std::vector<std::size_t>& caches,
187 bool transient) {
188 const std::string who =
189 transient ? std::string("solver_fld_cacheqn_tran") : std::string("solver_fld_cacheqn_analyzer");
190 for (std::size_t ci = 0; ci < caches.size(); ++ci) {
191 const std::size_t key = caches[ci] + 1; // nodeparam is keyed 1-based
192 if (sn.nodeparam.count(key) == 0)
193 throw InputError(who + ": cache node " + std::to_string(key) +
194 " carries no cache parameters");
195 const qn::CacheParam<T>& ch = sn.nodeparam.at(key);
196 const std::string at = " (cache node " + std::to_string(key) + ")";
197 switch (ch.replacestrat) {
201 break;
202 default:
203 // Verbatim the reference's refusal: the FPI characteristic-time
204 // approximation those policies use is not a fluid method.
205 throw UnsupportedError(
206 who + ": SolverFLD supports only RANDOM(m)/FIFO(m) (refined mean field) and "
207 "strict FIFO(m) (position-resolved mean field) cache replacement; "
208 "strategy " +
209 std::to_string(static_cast<int>(ch.replacestrat)) +
210 " has no drift-based fluid model. Use SolverNC/SolverMVA or SolverLDES for "
211 "this cache" + at);
212 }
213 }
214}
215
216/**
217 * The isolated-cache miss algorithm shared by both entry points.
218 *
219 * `lambda_cache[v]` is (nitems x (h+1)) and repeats the same rate down every
220 * list position; `cache_miss_rmf` wants the (users x items) slice, which is
221 * column 0. This is the same reshaping the MVA and NC siblings do for their own
222 * miss routines.
223 */
224template <class T>
225Matrix<T> fluid_cacheqn_lambda_slice(const std::vector<Matrix<T> >& lambda_cache) {
226 const std::size_t u = lambda_cache.size();
227 const std::size_t n = (u == 0) ? 0 : lambda_cache[0].rows();
228 Matrix<T> lam(u, n, num_traits<T>::from_int(0));
229 for (std::size_t v = 0; v < u; ++v)
230 for (std::size_t k = 0; k < n; ++k) lam(v, k) = lambda_cache[v](k, 0);
231 return lam;
232}
233
234/**
235 * The reference's default initial occupancy: the first m(1) items in list 1, the
236 * next m(2) in list 2, and every remaining item outside the cache. Duplicated
237 * from `cache_miss_rmf`'s steady path because its transient entry point takes
238 * the state as an argument and has no default of its own.
239 */
240template <class T>
241std::vector<T> fluid_cacheqn_default_x0(const std::vector<int>& m, std::size_t nitems) {
242 const T zero = num_traits<T>::from_int(0), one = num_traits<T>::from_int(1);
243 const std::size_t h = m.size();
244 std::vector<T> x0(nitems * (h + 1), zero);
245 std::size_t obj = 0;
246 for (std::size_t k = 1; k <= h; ++k)
247 for (int j = 0; j < m[k - 1]; ++j) {
248 ++obj;
249 if (obj <= nitems) x0[cache::cache_miss_rmf_index(obj - 1, k, nitems)] = one;
250 }
251 for (std::size_t i = obj; i < nitems; ++i) x0[cache::cache_miss_rmf_index(i, 0, nitems)] = one;
252 return x0;
253}
254
255} // namespace detail
256
257/**
258 * Port of `solver_fld_cacheqn_analyzer.m`.
259 *
260 * Returns the branch result BEFORE `solver_fluid`'s analyzer-level utilization
261 * and response-time correction, exactly as the reference's branch returns what
262 * `solver_fluid_matrix` gave it and lets `solver_fluid_analyzer` correct once
263 * after the method switch. Applying the correction here as well would apply it
264 * twice, and it is not idempotent (it rescales U by a share computed from the
265 * U it is given).
266 */
267template <class T>
269 const FluidOptions& opt) {
270 if constexpr (!std::is_same<T, double>::value) {
271 (void)sn;
272 (void)opt;
273 throw UnsupportedError(
274 "solver_fld_cacheqn_analyzer: the fluid network solve integrates its drift with LSODA "
275 "and the cache drift with an adaptive Rosenbrock, both of which assume double "
276 "precision; rerun with --arith double");
277 } else {
278 const std::vector<std::size_t> caches = detail::fluid_cacheqn_nodes(sn);
279 if (caches.empty())
280 throw InputError(
281 "solver_fld_cacheqn_analyzer: the model has no Cache node; use solver_fluid");
282 detail::fluid_cacheqn_gate(sn, caches, false);
283
284 // The network solver, the reference's `netsolve`. `init_sol` is left
285 // empty so each pass restarts from the default initial state, which is
286 // what `solver_fluid_initsol` returns for the default `sn.state`; the
287 // drift forgets it anyway at the fixed point.
288 //
289 // OPT.METHOD SELECTS THE QUEUEING LAYER, not the cache one. "rmf" keeps
290 // the first-order matrix method here, which is the historical
291 // behaviour; "minnormal" puts the moment closure in its place, so a
292 // cache model reaches the same E[min(X,c)] treatment as any other model
293 // and returns a covariance. The cache layer is the refined mean field
294 // either way: it has no first-order alternative.
295 const bool use_moments = (opt.method == "minnormal");
296 FluidSolution last;
297 bool solved = false;
298 std::function<mva::MvaSolution<T>(const qn::NetworkStruct<T>&)> netfun =
299 [&last, &solved, &opt, use_moments](const qn::NetworkStruct<T>& snit) -> mva::MvaSolution<T> {
300 FluidOptions fo = opt;
301 fo.method = use_moments ? "minnormal" : "matrix";
302 fo.init_sol.clear();
303 // fluid_dispatch is the FIRST-ORDER dispatcher; the closure lives in
304 // solver_fluid_moments, which fluid_runner reaches directly
305 last = use_moments ? solver_fluid_moments(snit, fo) : detail::fluid_dispatch(snit, fo);
306 solved = true;
308 ms.Q = last.QN;
309 ms.U = last.UN;
310 ms.R = last.RN;
311 ms.Tp = last.TN;
312 ms.C = last.CN;
313 ms.X = last.XN; // TN at the reference station, as netsolve computes XN
314 ms.method = last.method;
315 ms.iter = static_cast<int>(last.iters);
316 return ms;
317 };
318
319 std::function<std::vector<T>(const Matrix<T>&, const std::vector<int>&,
320 const std::vector<Matrix<T> >&, const qn::CacheParam<T>&)>
321 missfun = [](const Matrix<T>& gamma, const std::vector<int>& m,
322 const std::vector<Matrix<T> >& lambda_cache,
323 const qn::CacheParam<T>& ch) -> std::vector<T> {
324 (void)gamma; // every drift here is built from the request rates alone
325 const std::size_t u = lambda_cache.size();
326 std::vector<T> missrate(u, num_traits<T>::from_int(0));
327 if (u == 0) return missrate;
328 const std::vector<T> mu =
329 detail::fluid_cacheqn_miss(m, detail::fluid_cacheqn_lambda_slice(lambda_cache), ch);
330 for (std::size_t v = 0; v < mu.size() && v < u; ++v) missrate[v] = mu[v];
331 return missrate;
332 };
333
334 // `da_cacheqn` reads the fixed-point tolerance off MvaOptions::tol,
335 // whereas MATLAB's da_fpi reads options.iter_tol; mapping iter_tol onto
336 // it is what keeps the two stopping on the same increment.
337 mva::MvaOptions mopt;
338 mopt.tol = opt.iter_tol;
339 mopt.iter_max = static_cast<int>(opt.iter_max);
340
341 const da::CacheqnResult<T> r = da::da_cacheqn<T>(sn, false, mopt, netfun, missfun);
342 if (!solved)
343 throw NumericError(
344 "solver_fld_cacheqn_analyzer: the decomposition ran no sweep, so there is no fluid "
345 "solution; iter_max must be at least one");
346
348 out.sol = last;
349 out.sol.method = use_moments ? "minnormal" : "rmf";
350 out.sol.iters = static_cast<std::size_t>(r.iter);
351 out.hitprob = r.hitprob;
352 out.missprob = r.missprob;
353 out.iter = r.iter;
354
355 // REFERENCE DEFECT, reproduced deliberately. The analyzer overwrites the
356 // system response time with njobs(k)/XN(k) instead of the sum of the
357 // queue lengths over the throughput. For a closed class that is Little's
358 // law; for an OPEN class njobs is infinite, so the reference reports an
359 // infinite response time, and substituting sum(Q)/X here would report a
360 // number this analyzer does not produce.
361 const std::size_t M = sn.nstations, K = sn.nclasses;
362 out.sol.CN.assign(K, 0.0);
363 for (std::size_t k = 0; k < K; ++k) {
364 const std::size_t rs = sn.classes[k].refstat;
365 if (rs >= 1 && rs <= M && out.sol.XN[k] > 0.0)
366 out.sol.CN[k] = sn.classes[k].population / out.sol.XN[k];
367 }
368
369 out.refreshed = sn;
370 out.refreshed.refresh_cacheqn_actual_visits(r.hitprob, r.missprob);
371 return out;
372 }
373}
374
375/**
376 * Port of `solver_fld_cacheqn_tran.m`: the transient counterpart of the analyzer
377 * above.
378 *
379 * The steady solver drives the cache drift to its fixed point; this integrates
380 * the SAME drift over [t0,t1] from a given occupancy. The decomposition still
381 * runs first and in full, because the drift is parameterised by the converged
382 * per-class arrival rates -- a transient computed at the offered rates would be
383 * the trajectory of a cache the network does not feed.
384 *
385 * `x0cell[c]`, when present and non-empty, seeds cache c with a flat DDPP state
386 * of length nitems*(h+1); otherwise the reference default is used.
387 *
388 * WHY THE TIME GRID IS PER CACHE. The reference builds one `tcache` from the
389 * FIRST cache and writes every other cache's trajectory into a slice of that
390 * width. The grids come from an adaptive integrator on different drifts, so they
391 * agree in length only by accident; keeping each cache's own grid is the same
392 * information without the coincidence.
393 */
394template <class T>
395std::vector<FluidCacheqnTranCache<T> > solver_fld_cacheqn_tran(
396 const qn::NetworkStruct<T>& sn, const FluidOptions& opt, double t0, double t1,
397 const std::vector<std::vector<T> >& x0cell = std::vector<std::vector<T> >()) {
398 if constexpr (!std::is_same<T, double>::value) {
399 (void)sn;
400 (void)opt;
401 (void)t0;
402 (void)t1;
403 (void)x0cell;
404 throw UnsupportedError(
405 "solver_fld_cacheqn_tran: the cache drift is integrated by an adaptive Rosenbrock and "
406 "the network by LSODA, both double precision; rerun with --arith double");
407 } else {
408 const std::vector<std::size_t> caches = detail::fluid_cacheqn_nodes(sn);
409 if (caches.empty())
410 throw InputError("solver_fld_cacheqn_tran: the model has no Cache node");
411 detail::fluid_cacheqn_gate(sn, caches, true);
412 if (!std::isfinite(t0)) t0 = 0.0; // options.timespan(1) = -Inf, as the reference
413 if (!(t1 > t0))
414 throw InputError("solver_fld_cacheqn_tran: the timespan must have positive width");
415
416 // Converge the cache arrival rates, and keep the isolated-cache inputs
417 // the last sweep built -- gamma, the capacities and the per-item rates
418 // ARE the drift's parameters.
419 FluidSolution last;
420 std::function<mva::MvaSolution<T>(const qn::NetworkStruct<T>&)> netfun =
421 [&last, &opt](const qn::NetworkStruct<T>& snit) -> mva::MvaSolution<T> {
422 FluidOptions fo = opt;
423 fo.method = "matrix";
424 fo.init_sol.clear();
425 last = detail::fluid_dispatch(snit, fo);
427 ms.Q = last.QN;
428 ms.U = last.UN;
429 ms.R = last.RN;
430 ms.Tp = last.TN;
431 ms.C = last.CN;
432 ms.X = last.XN;
433 return ms;
434 };
435 std::function<std::vector<T>(const Matrix<T>&, const std::vector<int>&,
436 const std::vector<Matrix<T> >&, const qn::CacheParam<T>&)>
437 missfun = [](const Matrix<T>& gamma, const std::vector<int>& m,
438 const std::vector<Matrix<T> >& lambda_cache,
439 const qn::CacheParam<T>& ch) -> std::vector<T> {
440 (void)gamma;
441 const std::size_t u = lambda_cache.size();
442 std::vector<T> missrate(u, num_traits<T>::from_int(0));
443 if (u == 0) return missrate;
444 const std::vector<T> mu =
445 detail::fluid_cacheqn_miss(m, detail::fluid_cacheqn_lambda_slice(lambda_cache), ch);
446 for (std::size_t v = 0; v < mu.size() && v < u; ++v) missrate[v] = mu[v];
447 return missrate;
448 };
449 mva::MvaOptions mopt;
450 mopt.tol = opt.iter_tol;
451 mopt.iter_max = static_cast<int>(opt.iter_max);
452 const da::CacheqnResult<T> r = da::da_cacheqn<T>(sn, false, mopt, netfun, missfun);
453
454 const std::size_t K = sn.nclasses;
455 std::vector<FluidCacheqnTranCache<T> > out(caches.size());
456 for (std::size_t ci = 0; ci < caches.size(); ++ci) {
457 const std::vector<int>& m = r.info.itemcap[ci];
458 const std::vector<Matrix<T> >& lam_c = r.info.lambda_cache[ci];
459 const Matrix<T> lam = detail::fluid_cacheqn_lambda_slice(lam_c);
460 const std::size_t nitems = lam.cols();
461 const qn::CacheParam<T>& ch = sn.nodeparam.at(caches[ci] + 1);
462 const bool positional = ch.replacestrat == lang::ReplacementStrategy::FIFO ||
464 // The two families do not share a state space: RANDOM(m) tracks
465 // nitems*(h+1) per-list occupancies, the position-resolved policies
466 // nitems*sum(m) per-SLOT ones. A seed is validated against the one
467 // its own policy uses, and an absent seed is left to the routine's
468 // own default (popularity-ordered, or cold on a declared graph)
469 // rather than replaced by the RANDOM(m) layout.
470 std::vector<T> x0;
471 if (ci < x0cell.size() && !x0cell[ci].empty())
472 x0 = x0cell[ci];
473 else if (!positional)
474 x0 = detail::fluid_cacheqn_default_x0<T>(m, nitems);
475 std::size_t want = nitems * (m.size() + 1);
476 if (positional) {
477 want = 0;
478 for (std::size_t l = 0; l < m.size(); ++l)
479 want += static_cast<std::size_t>(m[l]);
480 want *= nitems;
481 }
482 if (!x0.empty() && x0.size() != want)
483 throw InputError(
484 "solver_fld_cacheqn_tran: the seed occupancy of cache " + std::to_string(ci + 1) +
485 " is not " + std::to_string(want) + " long");
486
488 switch (ch.replacestrat) {
490 tr = cache::cache_miss_fifo_rmf_transient(std::vector<T>(), m, lam, T(t0),
491 T(t1), x0, ch.accost);
492 break;
494 tr = cache::cache_miss_sfifo_rmf_transient(std::vector<T>(), m, lam, T(t0),
495 T(t1), x0, ch.accost);
496 break;
497 default:
498 tr = cache::cache_miss_rmf_transient(m, lam, T(t0), T(t1), x0);
499 break;
500 }
501
502 FluidCacheqnTranCache<T>& oc = out[ci];
503 oc.node = caches[ci];
504 oc.t = tr.tout;
505 oc.xocc = tr.xtraj;
506 const std::size_t nt = tr.tout.size();
509 oc.arate.assign(K, num_traits<T>::from_int(0));
510 for (std::size_t v = 0; v < lam.rows() && v < K; ++v) {
511 double rowrate = 0.0;
512 for (std::size_t i = 0; i < nitems; ++i) rowrate += lam(v, i);
513 oc.arate[v] = rowrate;
514 // A class that does not read this cache has neither probability:
515 // the reference leaves both rows at zero rather than at 1 and 0.
516 if (!(rowrate > 0.0)) continue;
517 for (std::size_t j = 0; j < nt; ++j) {
518 double mp = tr.MU_t(v, j) / rowrate;
519 if (mp < 0.0) mp = 0.0;
520 if (mp > 1.0) mp = 1.0;
521 oc.missprob_t(v, j) = mp;
522 oc.hitprob_t(v, j) = 1.0 - mp;
523 }
524 }
525 }
526 return out;
527 }
528}
529
530} // namespace fluid
531} // namespace line
532
533#endif // LINE_SOLVERS_FLUID_FLUID_CACHEQN_H
Position-resolved mean-field miss rates for FIFO(m) and strict FIFO(m).
Refined mean field (RMF) miss rates of a multi-list RANDOM(m) cache.
InputError(const std::string &what)
Definition error.h:39
std::size_t size() const
Definition matrix.h:91
std::size_t cols() const
Definition matrix.h:90
std::size_t rows() const
Definition matrix.h:89
NumericError(const std::string &what)
Definition error.h:45
UnsupportedError(const std::string &what)
Definition error.h:51
A network plus its refreshed NetworkStruct.
Decomposition-aggregation driver for integrated cache-queueing models, a port of matlab/src/api/da/da...
The exception types the port throws.
The second-order fluid methods: fluid_moment_terms.m, fluid_lyapunov.m, fluid_drift_jacobian....
Enumerations and the minimal distribution descriptor shared by the model layer of the C++ port.
Dense matrix and non-owning view.
The option and result types every MVA analyzer shares.
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.
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.
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 > 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).
CacheqnResult< T > da_cacheqn(qn::NetworkStruct< T > sn, bool exact, const mva::MvaOptions &opt, const std::function< mva::MvaSolution< T >(const qn::NetworkStruct< T > &)> &netfun, const std::function< std::vector< T >(const Matrix< T > &, const std::vector< int > &, const std::vector< Matrix< T > > &, const qn::CacheParam< T > &)> &missfun=nullptr)
Decomposition-aggregation driver for integrated cache-queueing models, a port of matlab/src/api/da/da...
Definition da_cacheqn.h:133
FluidCacheqnSolution< T > solver_fld_cacheqn_analyzer(const qn::NetworkStruct< T > &sn, const FluidOptions &opt)
Port of solver_fld_cacheqn_analyzer.m.
std::vector< FluidCacheqnTranCache< T > > solver_fld_cacheqn_tran(const qn::NetworkStruct< T > &sn, const FluidOptions &opt, double t0, double t1, const std::vector< std::vector< T > > &x0cell=std::vector< std::vector< T > >())
Port of solver_fld_cacheqn_tran.m: the transient counterpart of the analyzer above.
FluidSolution solver_fluid_moments(const qn::NetworkStruct< T > &sn, const FluidOptions &opt)
Port of solver_fluid_moments.m: the second-order fluid analysis backing minnormal and refined.
@ FIFO
first in, first out
Definition lang_types.h:380
A queueing network and its refreshed NetworkStruct.
SolverFluid: the closing method, a port of solver_fluid.m, solver_fluid_iteration....
Return value of cache_miss_rmf, mirroring [M,MU,MI,pi0,tout,pi0_t,MU_t,xtraj].
Matrix< T > missprob
(ncaches x nclasses)
Definition da_cacheqn.h:61
Matrix< T > hitprob
(ncaches x nclasses)
Definition da_cacheqn.h:60
CacheqnInfo< T > info
Definition da_cacheqn.h:63
What the steady analyzer returns: the fluid metrics plus the converged split.
Matrix< T > missprob
(ncaches x nclasses)
int iter
decomposition sweeps, not integrator steps
qn::NetworkStruct< T > refreshed
The struct whose cache self-switch carries the CONVERGED split rather than the offered one,...
Matrix< T > hitprob
(ncaches x nclasses), cache order as the node scan
One cache's transient, the per-cache slice of the reference's 3-D outputs.
std::vector< T > t
time grid of THIS cache
std::size_t node
0-based Cache node index
Matrix< T > xocc
(nitems*(h+1) x nt) DDPP occupancy trajectory
Matrix< T > hitprob_t
(nclasses x nt)
std::vector< T > arate
(nclasses) converged arrival rate at the cache
Matrix< T > missprob_t
(nclasses x nt)
Controls, defaulting to SolverOptions('Fluid') in the reference.
std::vector< double > init_sol
initial state; empty selects the default below
What the analyzer returns, in the same shape as the MVA solver's result.
std::vector< double > XN
std::vector< double > CN
The options SolverMVA reads.
Definition mva_types.h:31
Class-level results, the [Q,U,R,T,C,X] of the MATLAB analyzers.
Definition mva_types.h:96
std::vector< T > X
Definition mva_types.h:98
std::vector< T > C
Definition mva_types.h:98
std::vector< std::vector< Matrix< T > > > accost
(u) x (n) of (h+1)x(h+1), or empty
lang::ReplacementStrategy replacestrat