5#ifndef LINE_SOLVERS_FLUID_FLUID_CACHEQN_H
6#define LINE_SOLVERS_FLUID_FLUID_CACHEQN_H
120 std::vector<std::size_t> caches;
121 for (std::size_t nd = 0; nd <
sn.nodes.size(); ++nd)
122 if (
sn.nodes[nd].nodetype == qn::NodeType::Cache) caches.push_back(nd);
133bool fluid_cacheqn_accost_is_linear(
const std::vector<std::vector<
Matrix<T> > >& accost,
135 if (accost.empty())
return true;
137 const double eps = 1e-9;
139 for (std::size_t a = 0; a < h; ++a) lin(a, a + 1) = one;
141 for (std::size_t v = 0; v < accost.size(); ++v)
142 for (std::size_t k = 0; k < accost[v].size(); ++k) {
144 if (g.
rows() == 0)
continue;
145 if (g.
rows() != h + 1 || g.
cols() != h + 1)
return false;
146 for (std::size_t a = 0; a <= h; ++a)
147 for (std::size_t b = 0; b <= h; ++b)
166std::vector<T> fluid_cacheqn_miss(
const std::vector<int>& m,
const Matrix<T>& lam,
167 const qn::CacheParam<T>& ch) {
168 const bool linear = fluid_cacheqn_accost_is_linear(ch.accost, m.size());
169 switch (ch.replacestrat) {
172 T(num_traits<T>::from_int(10000)), ch.accost)
181 "solver_fld_cacheqn: replacement strategy " +
182 std::to_string(
static_cast<int>(ch.replacestrat)) +
183 " has no drift-based fluid model");
197void fluid_cacheqn_gate(
const qn::NetworkStruct<T>& sn,
const std::vector<std::size_t>& caches,
199 const std::string who =
200 transient ? std::string(
"solver_fld_cacheqn_tran") : std::string(
"solver_fld_cacheqn_analyzer");
201 for (std::size_t ci = 0; ci < caches.size(); ++ci) {
202 const std::size_t key = caches[ci] + 1;
203 if (sn.nodeparam.count(key) == 0)
204 throw InputError(who +
": cache node " + std::to_string(key) +
205 " carries no cache parameters");
206 const qn::CacheParam<T>& ch = sn.nodeparam.at(key);
207 const std::string at =
" (cache node " + std::to_string(key) +
")";
208 switch (ch.replacestrat) {
217 who +
": SolverFLD supports only RANDOM(m)/FIFO(m) (refined mean field) and "
218 "strict FIFO(m) (position-resolved mean field) cache replacement; "
220 std::to_string(
static_cast<int>(ch.replacestrat)) +
221 " has no drift-based fluid model. Use SolverNC/SolverMVA or SolverLDES for "
236Matrix<T> fluid_cacheqn_lambda_slice(
const std::vector<Matrix<T> >& lambda_cache) {
237 const std::size_t u = lambda_cache.size();
238 const std::size_t n = (u == 0) ? 0 : lambda_cache[0].rows();
239 Matrix<T> lam(u, n, num_traits<T>::from_int(0));
240 for (std::size_t v = 0; v < u; ++v)
241 for (std::size_t k = 0; k < n; ++k) lam(v, k) = lambda_cache[v](k, 0);
252std::vector<T> fluid_cacheqn_default_x0(
const std::vector<int>& m, std::size_t nitems) {
253 const T zero = num_traits<T>::from_int(0), one = num_traits<T>::from_int(1);
254 const std::size_t h = m.size();
255 std::vector<T> x0(nitems * (h + 1), zero);
257 for (std::size_t k = 1; k <= h; ++k)
258 for (
int j = 0; j < m[k - 1]; ++j) {
281 if constexpr (!std::is_same<T, double>::value) {
285 "solver_fld_cacheqn_analyzer: the fluid network solve integrates its drift with LSODA "
286 "and the cache drift with an adaptive Rosenbrock, both of which assume double "
287 "precision; rerun with --arith double");
289 const std::vector<std::size_t> caches = detail::fluid_cacheqn_nodes(
sn);
292 "solver_fld_cacheqn_analyzer: the model has no Cache node; use solver_fluid");
293 detail::fluid_cacheqn_gate(
sn, caches,
false);
306 const bool use_moments = (
opt.method ==
"minnormal");
312 fo.
method = use_moments ?
"minnormal" :
"matrix";
330 std::function<std::vector<T>(
const Matrix<T>&,
const std::vector<int>&,
332 missfun = [](
const Matrix<T>& gamma,
const std::vector<int>& m,
333 const std::vector<Matrix<T> >& lambda_cache,
336 const std::size_t u = lambda_cache.
size();
338 if (u == 0)
return missrate;
339 const std::vector<T> mu =
340 detail::fluid_cacheqn_miss(m, detail::fluid_cacheqn_lambda_slice(lambda_cache), ch);
341 for (std::size_t v = 0; v < mu.size() && v < u; ++v) missrate[v] = mu[v];
355 "solver_fld_cacheqn_analyzer: the decomposition ran no sweep, so there is no fluid "
356 "solution; iter_max must be at least one");
360 out.
sol.
method = use_moments ?
"minnormal" :
"rmf";
372 const std::size_t M =
sn.nstations, K =
sn.nclasses;
373 out.
sol.
CN.assign(K, 0.0);
374 for (std::size_t k = 0; k < K; ++k) {
375 const std::size_t rs =
sn.classes[k].refstat;
376 if (rs >= 1 && rs <= M && out.
sol.
XN[k] > 0.0)
377 out.
sol.
CN[k] =
sn.classes[k].population / out.
sol.
XN[k];
417 const std::vector<std::vector<T> >& x0cell = std::vector<std::vector<T> >(),
418 const Matrix<T>& hold_D0 =
Matrix<T>(),
const std::vector<T>& hold_pie = std::vector<T>()) {
419 if constexpr (!std::is_same<T, double>::value) {
428 "solver_fld_cacheqn_tran: the cache drift is integrated by an adaptive Rosenbrock and "
429 "the network by LSODA, both double precision; rerun with --arith double");
431 const std::vector<std::size_t> caches = detail::fluid_cacheqn_nodes(
sn);
433 throw InputError(
"solver_fld_cacheqn_tran: the model has no Cache node");
434 detail::fluid_cacheqn_gate(
sn, caches,
true);
435 if (!std::isfinite(t0)) t0 = 0.0;
437 throw InputError(
"solver_fld_cacheqn_tran: the timespan must have positive width");
448 last = detail::fluid_dispatch(snit, fo);
458 std::function<std::vector<T>(
const Matrix<T>&,
const std::vector<int>&,
460 missfun = [](
const Matrix<T>& gamma,
const std::vector<int>& m,
461 const std::vector<Matrix<T> >& lambda_cache,
464 const std::size_t u = lambda_cache.
size();
466 if (u == 0)
return missrate;
467 const std::vector<T> mu =
468 detail::fluid_cacheqn_miss(m, detail::fluid_cacheqn_lambda_slice(lambda_cache), ch);
469 for (std::size_t v = 0; v < mu.size() && v < u; ++v) missrate[v] = mu[v];
477 const std::size_t K =
sn.nclasses;
478 std::vector<FluidCacheqnTranCache<T> > out(caches.size());
479 for (std::size_t ci = 0; ci < caches.size(); ++ci) {
480 const std::vector<int>& m = r.
info.itemcap[ci];
481 const std::vector<Matrix<T> >& lam_c = r.
info.lambda_cache[ci];
482 const Matrix<T> lam = detail::fluid_cacheqn_lambda_slice(lam_c);
483 const std::size_t nitems = lam.
cols();
494 if (ci < x0cell.size() && !x0cell[ci].empty())
496 else if (!positional)
497 x0 = detail::fluid_cacheqn_default_x0<T>(m, nitems);
498 std::size_t want = nitems * (m.size() + 1);
501 for (std::size_t l = 0; l < m.size(); ++l)
502 want +=
static_cast<std::size_t
>(m[l]);
505 if (!x0.empty() && x0.size() != want)
507 "solver_fld_cacheqn_tran: the seed occupancy of cache " + std::to_string(ci + 1) +
508 " is not " + std::to_string(want) +
" long");
515 double Lam = 0.0, d0 = t0, d1 = t1;
516 if (!hold_pie.empty()) {
517 for (std::size_t v = 0; v < lam.
rows(); ++v)
518 for (std::size_t i = 0; i < nitems; ++i) Lam += lam(v, i);
529 T(d1), x0, ch.
accost, clk);
533 T(d1), x0, ch.
accost, clk);
540 for (std::size_t j = 0; j <
tr.tout.size(); ++j)
tr.tout[j] = T(
tr.tout[j] / Lam);
543 oc.
node = caches[ci];
547 const std::size_t nt =
tr.tout.size();
551 for (std::size_t v = 0; v < lam.
rows() && v < K; ++v) {
552 double rowrate = 0.0;
553 for (std::size_t i = 0; i < nitems; ++i) rowrate += lam(v, i);
554 oc.
arate[v] = rowrate;
557 if (!(rowrate > 0.0))
continue;
558 for (std::size_t j = 0; j < nt; ++j) {
559 double mp =
tr.MU_t(v, j) / rowrate;
560 if (mp < 0.0) mp = 0.0;
561 if (mp > 1.0) mp = 1.0;
570 for (std::size_t v = 0; v < lam.
rows() && v < K; ++v) {
571 const double rowrate = oc.
arate[v];
572 if (!(rowrate > 0.0))
continue;
573 double mp = oc.
sojourn.MU[v] / rowrate;
574 if (mp < 0.0) mp = 0.0;
575 if (mp > 1.0) mp = 1.0;
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.
NumericError(const std::string &what)
UnsupportedError(const std::string &what)
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.
CacheSojournClock< T > cache_sojourn_clock(const Matrix< T > &D0, const std::vector< T > &pie, double lam)
The holding-time clock of a random-environment stage whose holding time has sub-generator D0 and entr...
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_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 > > >(), const CacheSojournClock< T > &clk=CacheSojournClock< T >())
cache_miss_fifo_rmf with the optional TSPAN/X0INIT transient.
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 > > >(), const CacheSojournClock< T > &clk=CacheSojournClock< T >())
cache_miss_sfifo_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.
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).
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...
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 > >(), const Matrix< T > &hold_D0=Matrix< T >(), const std::vector< T > &hold_pie=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
Conservation laws of a layered queueing network, enumerated from its structure.
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].
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.
Matrix< T > missprob
(ncaches x nclasses)
Matrix< T > hitprob
(ncaches x nclasses)
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
std::vector< T > hitprob_bar
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 > lam
(nclasses x nitems) isolated per-item request rates (lamcell)
cache::CacheSojournResult< T > sojourn
Grid-independent average of the transient against the holding time the caller passed (cache::cache_so...
std::vector< T > missprob_bar
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.
The options SolverMVA reads.
Class-level results, the [Q,U,R,T,C,X] of the MATLAB analyzers.
std::vector< std::vector< Matrix< T > > > accost
(u) x (n) of (h+1)x(h+1), or empty
lang::ReplacementStrategy replacestrat