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 const std::vector<int>&)>
333 missfun = [](
const Matrix<T>& gamma,
const std::vector<int>& m,
334 const std::vector<Matrix<T> >& lambda_cache,
337 const std::size_t u = lambda_cache.
size();
339 if (u == 0)
return missrate;
340 const std::vector<T> mu =
341 detail::fluid_cacheqn_miss(m, detail::fluid_cacheqn_lambda_slice(lambda_cache), ch);
342 for (std::size_t v = 0; v < mu.size() && v < u; ++v) missrate[v] = mu[v];
356 "solver_fld_cacheqn_analyzer: the decomposition ran no sweep, so there is no fluid "
357 "solution; iter_max must be at least one");
361 out.sol.
method = use_moments ?
"minnormal" :
"rmf";
362 out.sol.iters =
static_cast<std::size_t
>(r.
iter);
373 const std::size_t M =
sn.nstations, K =
sn.nclasses;
374 out.sol.CN.assign(K, 0.0);
375 for (std::size_t k = 0; k < K; ++k) {
376 const std::size_t rs =
sn.classes[k].refstat;
377 if (rs >= 1 && rs <= M && out.sol.XN[k] > 0.0)
378 out.sol.CN[k] =
sn.classes[k].population / out.sol.XN[k];
418 const std::vector<std::vector<T> >& x0cell = std::vector<std::vector<T> >(),
419 const Matrix<T>& hold_D0 =
Matrix<T>(),
const std::vector<T>& hold_pie = std::vector<T>()) {
420 if constexpr (!std::is_same<T, double>::value) {
429 "solver_fld_cacheqn_tran: the cache drift is integrated by an adaptive Rosenbrock and "
430 "the network by LSODA, both double precision; rerun with --arith double");
432 const std::vector<std::size_t> caches = detail::fluid_cacheqn_nodes(
sn);
434 throw InputError(
"solver_fld_cacheqn_tran: the model has no Cache node");
435 detail::fluid_cacheqn_gate(
sn, caches,
true);
436 if (!std::isfinite(t0)) t0 = 0.0;
438 throw InputError(
"solver_fld_cacheqn_tran: the timespan must have positive width");
449 last = detail::fluid_dispatch(snit, fo);
459 std::function<std::vector<T>(
const Matrix<T>&,
const std::vector<int>&,
461 const std::vector<int>&)>
462 missfun = [](
const Matrix<T>& gamma,
const std::vector<int>& m,
463 const std::vector<Matrix<T> >& lambda_cache,
466 const std::size_t u = lambda_cache.
size();
468 if (u == 0)
return missrate;
469 const std::vector<T> mu =
470 detail::fluid_cacheqn_miss(m, detail::fluid_cacheqn_lambda_slice(lambda_cache), ch);
471 for (std::size_t v = 0; v < mu.size() && v < u; ++v) missrate[v] = mu[v];
479 const std::size_t K =
sn.nclasses;
480 std::vector<FluidCacheqnTranCache<T> > out(caches.size());
481 for (std::size_t ci = 0; ci < caches.size(); ++ci) {
482 const std::vector<int>& m = r.
info.itemcap[ci];
483 const std::vector<Matrix<T> >& lam_c = r.
info.lambda_cache[ci];
484 const Matrix<T> lam = detail::fluid_cacheqn_lambda_slice(lam_c);
485 const std::size_t nitems = lam.
cols();
496 if (ci < x0cell.size() && !x0cell[ci].empty())
498 else if (!positional)
499 x0 = detail::fluid_cacheqn_default_x0<T>(m, nitems);
500 std::size_t want = nitems * (m.size() + 1);
503 for (std::size_t l = 0; l < m.size(); ++l)
504 want +=
static_cast<std::size_t
>(m[l]);
507 if (!x0.empty() && x0.size() != want)
509 "solver_fld_cacheqn_tran: the seed occupancy of cache " + std::to_string(ci + 1) +
510 " is not " + std::to_string(want) +
" long");
517 double Lam = 0.0, d0 = t0, d1 = t1;
518 if (!hold_pie.empty()) {
519 for (std::size_t v = 0; v < lam.
rows(); ++v)
520 for (std::size_t i = 0; i < nitems; ++i) Lam += lam(v, i);
531 T(d1), x0, ch.
accost, clk);
535 T(d1), x0, ch.
accost, clk);
542 for (std::size_t j = 0; j <
tr.tout.size(); ++j)
tr.tout[j] = T(
tr.tout[j] / Lam);
545 oc.
node = caches[ci];
549 const std::size_t nt =
tr.tout.size();
553 for (std::size_t v = 0; v < lam.
rows() && v < K; ++v) {
554 double rowrate = 0.0;
555 for (std::size_t i = 0; i < nitems; ++i) rowrate += lam(v, i);
556 oc.
arate[v] = rowrate;
559 if (!(rowrate > 0.0))
continue;
560 for (std::size_t j = 0; j < nt; ++j) {
561 double mp =
tr.MU_t(v, j) / rowrate;
562 if (mp < 0.0) mp = 0.0;
563 if (mp > 1.0) mp = 1.0;
572 for (std::size_t v = 0; v < lam.
rows() && v < K; ++v) {
573 const double rowrate = oc.
arate[v];
574 if (!(rowrate > 0.0))
continue;
575 double mp = oc.
sojourn.MU[v] / rowrate;
576 if (mp < 0.0) mp = 0.0;
577 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 > &, const std::vector< int > &)> &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....
The <kind> part of a LINE:<category>:<kind> identifier (see _kb/17-error-vocabulary....
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.
The parameters of a Cache node, MATLAB's sn.nodeparam{ind} for a Cache.
std::vector< std::vector< Matrix< T > > > accost
(u) x (n) of (h+1)x(h+1), or empty
lang::ReplacementStrategy replacestrat