5#ifndef LINE_SOLVERS_FLUID_FLUID_CACHEQN_H
6#define LINE_SOLVERS_FLUID_FLUID_CACHEQN_H
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);
122bool fluid_cacheqn_accost_is_linear(
const std::vector<std::vector<
Matrix<T> > >& accost,
124 if (accost.empty())
return true;
126 const double eps = 1e-9;
128 for (std::size_t a = 0; a < h; ++a) lin(a, a + 1) = one;
130 for (std::size_t v = 0; v < accost.size(); ++v)
131 for (std::size_t k = 0; k < accost[v].size(); ++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)
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) {
161 T(num_traits<T>::from_int(10000)), ch.accost)
170 "solver_fld_cacheqn: replacement strategy " +
171 std::to_string(
static_cast<int>(ch.replacestrat)) +
172 " has no drift-based fluid model");
186void fluid_cacheqn_gate(
const qn::NetworkStruct<T>& sn,
const std::vector<std::size_t>& caches,
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;
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) {
206 who +
": SolverFLD supports only RANDOM(m)/FIFO(m) (refined mean field) and "
207 "strict FIFO(m) (position-resolved mean field) cache replacement; "
209 std::to_string(
static_cast<int>(ch.replacestrat)) +
210 " has no drift-based fluid model. Use SolverNC/SolverMVA or SolverLDES for "
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);
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);
246 for (std::size_t k = 1; k <= h; ++k)
247 for (
int j = 0; j < m[k - 1]; ++j) {
270 if constexpr (!std::is_same<T, double>::value) {
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");
278 const std::vector<std::size_t> caches = detail::fluid_cacheqn_nodes(
sn);
281 "solver_fld_cacheqn_analyzer: the model has no Cache node; use solver_fluid");
282 detail::fluid_cacheqn_gate(
sn, caches,
false);
295 const bool use_moments = (
opt.method ==
"minnormal");
301 fo.
method = use_moments ?
"minnormal" :
"matrix";
319 std::function<std::vector<T>(
const Matrix<T>&,
const std::vector<int>&,
321 missfun = [](
const Matrix<T>& gamma,
const std::vector<int>& m,
322 const std::vector<Matrix<T> >& lambda_cache,
325 const std::size_t u = lambda_cache.
size();
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];
344 "solver_fld_cacheqn_analyzer: the decomposition ran no sweep, so there is no fluid "
345 "solution; iter_max must be at least one");
349 out.
sol.
method = use_moments ?
"minnormal" :
"rmf";
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];
397 const std::vector<std::vector<T> >& x0cell = std::vector<std::vector<T> >()) {
398 if constexpr (!std::is_same<T, double>::value) {
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");
408 const std::vector<std::size_t> caches = detail::fluid_cacheqn_nodes(
sn);
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;
414 throw InputError(
"solver_fld_cacheqn_tran: the timespan must have positive width");
425 last = detail::fluid_dispatch(snit, fo);
435 std::function<std::vector<T>(
const Matrix<T>&,
const std::vector<int>&,
437 missfun = [](
const Matrix<T>& gamma,
const std::vector<int>& m,
438 const std::vector<Matrix<T> >& lambda_cache,
441 const std::size_t u = lambda_cache.
size();
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];
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();
471 if (ci < x0cell.size() && !x0cell[ci].empty())
473 else if (!positional)
474 x0 = detail::fluid_cacheqn_default_x0<T>(m, nitems);
475 std::size_t want = nitems * (m.size() + 1);
478 for (std::size_t l = 0; l < m.size(); ++l)
479 want +=
static_cast<std::size_t
>(m[l]);
482 if (!x0.empty() && x0.size() != want)
484 "solver_fld_cacheqn_tran: the seed occupancy of cache " + std::to_string(ci + 1) +
485 " is not " + std::to_string(want) +
" long");
503 oc.
node = caches[ci];
506 const std::size_t nt =
tr.tout.size();
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;
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;
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.
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...
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
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)
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
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.
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