5#ifndef LINE_SOLVERS_ENV_SOLVER_ENV_LIMIT_H
6#define LINE_SOLVERS_ENV_SOLVER_ENV_LIMIT_H
149 : envObj(e), opt(o), stage_fn_(stage_fn) {
157 if (
opt.method !=
"avg" &&
opt.method !=
"dec")
159 "' is not a closed-form environment limit; the two are 'avg' "
160 "(fast environment) and 'dec' (slow environment)");
161 dec_ = (
opt.method ==
"dec");
162 if (!stage_fn_ &&
opt.stage_solver !=
"fluid" &&
opt.stage_solver !=
"ctmc")
164 "SolverENV limit: stage solver '" +
opt.stage_solver +
165 "' is not available; the limits solve each stage in STEADY STATE, by the fluid "
166 "analyzer or the enumerated CTMC. Hand a stage solver to the EnvStageAvgFn "
167 "constructor to run another one");
168 if (!std::is_same<T, double>::value)
170 "SolverENV limit: a fluid stage integrates its drift with LSODA, which is double "
171 "only; rerun with --arith double");
178 envObj.reject_lqn_stages(
180 "the fast/slow limits read a stage's station rates directly -- 'dec' solves each "
181 "stage network in steady state and 'avg' builds one network at the probEnv-weighted "
182 "rates -- and a layered model has no such rate table, only the layers SolverLN "
185 const std::size_t E = envObj.nstages();
186 M = envObj.stage(0).model.nstations;
187 K = envObj.stage(0).model.nclasses;
188 for (std::size_t e = 1; e < E; ++e)
189 if (envObj.stage(e).model.nstations != M || envObj.stage(e).model.nclasses != K)
191 "SolverENV limit: every stage must have the same stations and classes; the "
192 "metrics are blended entrywise across them");
208 const std::vector<double> arv_all = cache_arrival(
sn, stage.
TN);
209 for (std::size_t c = 0; c < stage.
cache.caches.size(); ++c) {
211 Entry& en = entry(cm);
213 std::vector<double> arv(std::max(K, en.arv.size()), 0.0);
214 for (std::size_t r = 0; r < arv.size(); ++r) {
215 const std::size_t ind = cm.
node;
216 const std::size_t off = (ind >= 1 ? (ind - 1) *
sn.nclasses + r : arv_all.size());
217 if (off < arv_all.size()) arv[r] = w * arv_all[off];
219 grow(en.arv, arv.size());
220 for (std::size_t r = 0; r < arv.size(); ++r) en.arv[r] += arv[r];
221 accum(en.hit, cm.
hitprob, arv);
231 for (std::size_t c = 0; c < order_.size(); ++c) {
232 const Entry& en = order_[c];
238 cm.
hitprob = divide(en.hit, en.arv);
239 cm.
missprob = divide(en.miss, en.arv);
241 if (en.hitl.
rows() > 0) {
243 for (std::size_t r = 0; r < en.hitl.
rows(); ++r) {
244 const double d = r < en.arv.size() ? en.arv[r] : 0.0;
245 for (std::size_t l = 0; l < en.hitl.
cols(); ++l)
246 cm.
hitproblist(r, l) = d > 0.0 ? en.hitl(r, l) / d : nan_();
256 std::size_t node = 0;
258 std::vector<double> itemcap;
259 std::size_t nitems = 0;
260 std::vector<double> arv;
262 std::vector<double> hit, miss, dhit;
266 static double nan_() {
return std::numeric_limits<double>::quiet_NaN(); }
268 static void grow(std::vector<double>& v, std::size_t n) {
269 if (v.size() < n) v.resize(n, 0.0);
272 Entry& entry(
const solvers::CacheNodeMetrics<T>& cm) {
273 for (std::size_t i = 0; i < order_.size(); ++i)
274 if (order_[i].name == cm.name)
return order_[i];
278 en.itemcap = cm.itemcap;
279 en.nitems = cm.nitems;
280 order_.push_back(en);
281 return order_.back();
285 static void accum(std::vector<double>& a,
const std::vector<T>& ratio,
286 const std::vector<double>& arv) {
287 if (ratio.empty())
return;
288 grow(a, ratio.size());
289 for (std::size_t r = 0; r < ratio.size() && r < arv.size(); ++r) {
290 const double v = num_traits<T>::to_double(ratio[r]);
291 if (std::isfinite(v)) a[r] += arv[r] * v;
295 static void accum_rows(Matrix<double>& a,
const Matrix<T>& hl,
296 const std::vector<double>& arv) {
297 if (hl.rows() == 0 || hl.cols() == 0)
return;
298 if (a.rows() == 0) a = Matrix<double>(hl.rows(), hl.cols(), 0.0);
299 for (std::size_t r = 0; r < hl.rows() && r < a.rows(); ++r) {
300 const double w = r < arv.size() ? arv[r] : 0.0;
301 for (std::size_t l = 0; l < hl.cols() && l < a.cols(); ++l) {
302 const double v = num_traits<T>::to_double(hl(r, l));
303 if (std::isfinite(v)) a(r, l) += w * v;
312 static std::vector<double> divide(
const std::vector<double>& a,
313 const std::vector<double>& d) {
314 std::vector<double> r;
315 if (a.empty())
return r;
316 r.assign(a.size(), nan_());
317 for (std::size_t i = 0; i < a.size(); ++i)
318 if (i < d.size() && d[i] > 0.0) r[i] = a[i] / d[i];
331 static std::vector<double> cache_arrival(
const qn::NetworkStruct<T>& sn,
332 const Matrix<double>& TN) {
333 const std::size_t I = sn.nodes.size(), R = sn.nclasses, M = sn.nstations;
334 std::vector<double> out(I * R, 0.0);
335 if (TN.rows() == 0)
return out;
336 Matrix<T> TNt(TN.rows(), TN.cols(), num_traits<T>::from_int(0));
337 for (std::size_t i = 0; i < TN.rows(); ++i)
338 for (std::size_t r = 0; r < TN.cols(); ++r)
339 TNt(i, r) = num_traits<T>::from_double(TN(i, r));
340 const Matrix<T> AN(M, R, num_traits<T>::from_int(0));
342 if (ANn.rows() != I)
return out;
343 for (std::size_t ind = 0; ind < I; ++ind)
344 for (std::size_t r = 0; r < R; ++r) {
345 const double v = num_traits<T>::to_double(ANn(ind, r));
346 out[ind * R + r] = std::isfinite(v) ? v : 0.0;
351 std::vector<Entry> order_;
363 out.
QN = to_double(r.
QN);
364 out.
UN = to_double(r.
UN);
365 out.
TN = to_double(r.
TN);
374 for (std::size_t i = 0; i < A.
rows(); ++i)
380 EnvLimitSolution solve_dec() {
381 const std::size_t E = envObj.nstages();
382 EnvLimitSolution out;
384 out.prob_env = envObj.prob_env;
392 for (std::size_t e = 0; e < E; ++e) {
393 const EnvStageAvg<T> s = stage_steady_state(envObj.stage(e).model);
394 const double p = envObj.prob_env[e];
395 acc.add(envObj.stage(e).model, s, p);
396 for (std::size_t i = 0; i < M; ++i)
397 for (std::size_t r = 0; r < K; ++r) {
398 out.QStage[e](i, r) = s.QN(i, r);
399 out.UStage[e](i, r) = s.UN(i, r);
400 out.TStage[e](i, r) = s.TN(i, r);
401 out.QN(i, r) += p * s.QN(i, r);
402 out.UN(i, r) += p * s.UN(i, r);
403 out.TN(i, r) += p * s.TN(i, r);
406 out.cache = acc.finish();
411 EnvLimitSolution solve_avg() {
412 EnvLimitSolution out;
414 out.prob_env = envObj.prob_env;
415 const qn::NetworkStruct<T> avg = rate_averaged_model();
416 const EnvStageAvg<T> s = stage_steady_state(avg);
420 acc.
add(avg, s, 1.0);
421 out.cache = acc.finish();
422 out.QN = Matrix<double>(M, K, 0.0);
423 out.UN = Matrix<double>(M, K, 0.0);
424 out.TN = Matrix<double>(M, K, 0.0);
425 for (std::size_t i = 0; i < M; ++i)
426 for (std::size_t r = 0; r < K; ++r) {
427 out.QN(i, r) = s.QN(i, r);
428 out.UN(i, r) = s.UN(i, r);
429 out.TN(i, r) = s.TN(i, r);
458 EnvStageAvg<T> stage_steady_state(
const qn::NetworkStruct<T>& sn)
const {
459 if (stage_fn_)
return stage_fn_(sn);
461 fluid::FluidOptions fo = opt.stage;
466 const fluid::FluidSolution s =
467 fluid::detail::fluid_has_cache(sn)
484 qn::NetworkStruct<T> rate_averaged_model()
const {
485 const std::size_t E = envObj.nstages();
486 qn::NetworkStruct<T> sn = envObj.stage(0).model;
487 std::vector<double> r(E, 0.0);
488 for (std::size_t i = 0; i < M; ++i) {
493 for (std::size_t k = 0; k < K; ++k) {
495 for (std::size_t e = 0; e < E && ok; ++e) {
496 const qn::NetworkStruct<T>& se = envObj.stage(e).model;
500 if (se.disabled[i][k]) {
504 r[e] = num_traits<T>::to_double(se.rates(i, k));
505 if (!(r[e] > 0.0) || !std::isfinite(r[e])) ok =
false;
508 double lo = r[0], hi = r[0], avg = 0.0;
509 for (std::size_t e = 0; e < E; ++e) {
510 lo = std::min(lo, r[e]);
511 hi = std::max(hi, r[e]);
512 avg += envObj.prob_env[e] * r[e];
516 if (hi - lo <= 1e-12 * std::max(1.0, hi))
continue;
517 sn.set_service(i + 1, k + 1,
525 Environment<T>& envObj;
528 std::size_t M = 0, K = 0;
What a solver observed about the Cache nodes of a model.
UnsupportedError(const std::string &what)
The RATE accumulator behind the cache blend, SolverENV.accumCacheMetric.
solvers::CacheMetrics< double > finish() const
Divide the accumulated rates by the accumulated arrivals, ONCE.
void add(const qn::NetworkStruct< T > &sn, const EnvStageAvg< T > &stage, double w)
One stage's contribution, weighted by its arrival into each cache.
SolverEnvLimit(Environment< T > &e, const EnvOptions &o, EnvStageAvgFn< T > stage_fn)
With an injected stage solver: stage_fn runs every stage, exactly as the reference calls its factory.
SolverEnvLimit(Environment< T > &e, const EnvOptions &o)
static EnvStageAvg< T > ctmc_stage_steady(const qn::NetworkStruct< T > &sn, double cutoff)
One stage in steady state under the enumerated CTMC at cutoff, with its cache split,...
A network plus its refreshed NetworkStruct.
A random environment: a port of matlab/src/lang/Environment.m, restricted to what SolverENV reads out...
The exception types the port throws.
The fluid solver's outermost entry point: @@SolverFLD/runAnalyzer.m's method resolution over solver_f...
Dense matrix and non-owning view.
Matrix< T > sn_get_node_arvr_from_tput(const qn::NetworkStruct< T > &sn, const Matrix< T > &TN, const Matrix< T > &AN)
Port of sn_get_node_arvr_from_tput.
mva::AvgResult< T > solver_ctmc_run_analyzer(const NetworkStruct< T > &sn, const CtmcOptions &opt)
Solve and format in one call, for a caller with no use for the chain.
EnvLimitSolution solver_env_limit(Environment< T > &e, const EnvOptions &o)
solveEnvLimit on the original stages.
std::function< EnvStageAvg< T >(const qn::NetworkStruct< T > &)> EnvStageAvgFn
The stage solver, as a callable: the C++ spelling of the MATLAB function handle SolverENV(renv,...
FluidSolution solver_fluid(const qn::NetworkStruct< T > &sn_in, const FluidOptions &opt, qn::NetworkStruct< T > *sn_out=nullptr)
Port of solver_fluid_analyzer.m: dispatch on the method, refit the non-exponential FCFS stations the ...
FluidSolution solver_fluid_run_analyzer(const qn::NetworkStruct< T > &sn, const FluidOptions &opt, qn::NetworkStruct< T > *sn_out=nullptr, qn::NetworkStruct< T > *refreshed_out=nullptr, solvers::CacheMetrics< T > *cache_out=nullptr)
Port of @@SolverFLD/runAnalyzer.m: resolve the method, route to the function the reference routes to,...
NodeType
Node kinds, with the values of MATLAB NodeType.
Conservation laws of a layered queueing network, enumerated from its structure.
A queueing network and its refreshed NetworkStruct.
Number-type abstraction for the templated API port.
Ports of matlab/src/api/sn/sn_get_node_arvr_from_tput.m and sn_get_node_tput_from_tput....
Port of solver_ctmc_analyzer.m and the parts of @@SolverCTMC/runAnalyzer.m that surround one solve: t...
SolverENV: a queueing network in a random environment.
SolverFluid: the closing method, a port of solver_fluid.m, solver_fluid_iteration....
The SolverCTMC knobs this port honours.
double cutoff
< 0 = not given
What a limit solve reports.
std::vector< Matrix< double > > UStage
std::vector< double > prob_env
The stage probabilities the blend used.
Matrix< double > QN
Environment-averaged metrics, (nstations x nclasses).
std::vector< Matrix< double > > QStage
dec only: each stage's own steady state, before the probEnv blend.
std::vector< Matrix< double > > TStage
solvers::CacheMetrics< double > cache
The environment-blended cache surface, one entry per Cache node.
std::string method
The limit that ran: avg or dec.
What one stage solve reports back to the limits.
solvers::CacheMetrics< T > cache
static Distrib exp_rate(const T &r)
The metrics getAvg returns, after filtering.
Matrix< T > UN
utilization
Matrix< T > QN
queue length
solvers::CacheMetrics< T > cache
What the cache branches observed, EMPTY on a model with no Cache node and on every solver that does n...
Every Cache node of the model, in node order; empty on a model with none.
std::vector< CacheNodeMetrics< T > > caches
One Cache node's measured behaviour.
std::vector< double > itemcap
(h) capacity of each list
std::vector< T > hitprob
(K) TRUE hit fraction, EMPTY = not computed
std::vector< T > delayedprob
(K) delayed-hit fraction, EMPTY off a retrieval system
Matrix< T > hitproblist
(K x h) per-list hit fraction, EMPTY = not computed
std::size_t node
1-based node index of the Cache
std::vector< T > missprob
(K)
std::string name
The Cache node's NAME, which is what a cross-language payload must key on.