5#ifndef LINE_SOLVERS_SSA_SSA_DISPATCH_H
6#define LINE_SOLVERS_SSA_SSA_DISPATCH_H
128bool ssa_check_scheduling(
const qn::NetworkStruct<T>& sn,
bool raise =
true) {
130 for (std::size_t i = 0; i < sn.nstations; ++i) {
158 if (!raise)
return false;
161 sn.stations[i].name +
162 "' is not supported by the NRM in any codebase");
179bool ssa_check_nodes(
const qn::NetworkStruct<T>& sn,
bool raise =
true) {
180 for (
const qn::NodeDef& nd : sn.nodes) {
181 switch (nd.nodetype) {
184 case qn::NodeType::Fork:
185 case qn::NodeType::Join:
186 if (!raise)
return false;
188 "' makes this a fork-join model, which the NRM does not "
189 "handle in any codebase (isNrmEligible excludes it); the "
190 "reference falls back to the serial engine, whose own "
191 "fork handler (sn.fjsync / State.afterFJEvent) is not "
216bool ssa_check_routing(
const qn::NetworkStruct<T>& sn,
bool raise =
true) {
217 for (
const qn::NodeDef& nd : sn.nodes)
218 for (std::size_t r = 0; r < nd.routing.size(); ++r) {
220 if (rs == qn::RoutingStrategy::PROB || rs == qn::RoutingStrategy::RAND ||
221 rs == qn::RoutingStrategy::DISABLED || rs == qn::RoutingStrategy::RROBIN ||
222 rs == qn::RoutingStrategy::WRROBIN || rs == qn::RoutingStrategy::JSQ ||
223 rs == qn::RoutingStrategy::SQ)
225 if (!raise)
return false;
227 "SolverSSA(method='nrm'): node '" + nd.name +
"' routes class '" +
229 "'. Its destination is a function the C++ NetworkStruct does not carry, so "
230 "there is nothing to resolve at firing time");
249bool ssa_check_phases(
const qn::NetworkStruct<T>& sn,
bool raise =
true) {
251 for (std::size_t i = 0; i < sn.nstations; ++i) {
260 for (std::size_t r = 0; r < sn.nclasses; ++r) {
261 if (sn.disabled[i][r])
continue;
266 if (!raise)
return false;
268 "SolverSSA(method='nrm'): class '" + sn.classes[r].name +
"' has non-exponential "
269 "service at station '" + sn.stations[i].name +
"', whose '" +
271 "' discipline the NRM phase expansion does not cover; ask for 'default', which "
272 "falls back to the serial engine as the reference does");
288bool ssa_check_cdscaling(
const qn::NetworkStruct<T>& sn,
bool raise =
true) {
292 if (!raise)
return false;
294 "SolverSSA(method='nrm'): the model declares a global dependence "
295 "(setGlobalDependence). The NRM builds its propensities from the per-station "
296 "population slice and never sees the whole population matrix the handle reads, so "
297 "the sample path would run at the unscaled rates; ask for method='serial', which "
313bool ssa_check_regions(
const qn::NetworkStruct<T>& sn,
bool raise =
true) {
314 for (
const typename qn::NetworkStruct<T>::Region& rg : sn.regions)
315 for (std::size_t r = 0; r < rg.rule.size(); ++r) {
317 if (d != qn::DropStrategy::BAS && d != qn::DropStrategy::BBS &&
318 d != qn::DropStrategy::RSRD)
320 if (!raise)
return false;
322 "SolverSSA(method='nrm'): finite capacity region '" + rg.name +
323 "' declares a blocking rule for class '" + sn.classes[r].name +
324 "'. The NRM region gate implements DROP and WAITQ only, as the reference's does");
339bool ssa_check_reply(
const qn::NetworkStruct<T>& sn,
bool raise =
true) {
340 for (std::size_t ind = 0; ind < sn.replyblock.size(); ++ind)
341 for (std::size_t k = 0; k < sn.replyblock[ind].size(); ++k) {
342 if (!sn.replyblock[ind][k])
continue;
343 if (!raise)
return false;
345 "SolverSSA(method='nrm'): class '" + sn.classes[k].name +
"' makes a synchronous "
346 "call (REPLY signal) from node '" + sn.nodes[ind].name +
"'. The NRM has no "
347 "held-server counter for a pending reply; use method='serial'");
363bool ssa_nrm_eligible(
const qn::NetworkStruct<T>& sn) {
372 if (sn.has_immediate_feedback())
return false;
373 return ssa_check_nodes(sn,
false) && ssa_check_scheduling(sn,
false) &&
374 ssa_check_routing(sn,
false) && ssa_check_phases(sn,
false) &&
375 ssa_check_cdscaling(sn,
false) && ssa_check_regions(sn,
false) &&
376 ssa_check_reply(sn,
false) && spn_nrm_supported(sn,
false);
390std::string ssa_nrm_supports(
const qn::NetworkStruct<T>& sn) {
392 ssa_check_nodes(sn,
true);
393 ssa_check_scheduling(sn,
true);
394 ssa_check_routing(sn,
true);
395 ssa_check_phases(sn,
true);
396 ssa_check_cdscaling(sn,
true);
397 ssa_check_regions(sn,
true);
398 ssa_check_reply(sn,
true);
399 spn_nrm_supported(sn,
true);
400 }
catch (
const UnsupportedError& e) {
414inline SsaNrmSpaceOptions ssa_space_options(
const SsaOptions& o) {
415 SsaNrmSpaceOptions s;
416 static_cast<SsaOptions&
>(s) = o;
430inline SsaSerialOptions ssa_serial_options(
const SsaOptions& o) {
432 static_cast<SsaOptions&
>(s) = o;
436inline SsaParallelOptions ssa_parallel_options(
const SsaOptions& o) {
437 SsaParallelOptions p;
438 static_cast<SsaOptions&
>(p) = o;
451SsaSolution ssa_serial_avg(
const qn::NetworkStruct<T>& sn,
const SsaOptions& opt,
452 std::vector<SsaCacheRatio>* cache) {
454 if (cache) *cache = s.cache;
485SsaSolution ssa_pn_token_rates(
const qn::NetworkStruct<T>& sn,
const SsaSolution& in) {
486 bool has_place =
false;
487 for (std::size_t a = 0; a < sn.nodes.size() && !has_place; ++a)
488 if (sn.nodes[a].nodetype == qn::NodeType::Place) has_place =
true;
489 if (!has_place || in.TN.rows() == 0)
return in;
491 const std::size_t M = in.TN.rows(), K = in.TN.cols();
492 Matrix<T>
QN(M, K), TN(M, K), RN(M, K);
493 for (std::size_t i = 0; i < M; ++i)
494 for (std::size_t k = 0; k < K; ++k) {
495 QN(i, k) = num_traits<T>::from_double(in.QN(i, k));
496 TN(i, k) = num_traits<T>::from_double(in.TN(i, k));
497 RN(i, k) = num_traits<T>::from_double(in.RN(i, k));
501 SsaSolution out = in;
502 for (std::size_t i = 0; i < M; ++i)
503 for (std::size_t k = 0; k < K; ++k) {
504 out.TN(i, k) = num_traits<T>::to_double(pn.TN(i, k));
505 out.RN(i, k) = num_traits<T>::to_double(pn.RN(i, k));
523 return {
"default",
"ssa",
"serial",
"para",
"pana",
"parallel",
"nrm"};
535 std::vector<SsaCacheRatio>*
cache =
nullptr) {
540 if constexpr (!std::is_same<T, double>::value) {
545 "solver_ssa_nrm: an SSA sample path is generated from exponential clocks, which are "
546 "logarithms of uniform draws; there is no exact value to compute and a wider float "
547 "carries no information the Monte Carlo error does not swamp. Rerun with --arith "
550 detail::ssa_check_nodes(
sn);
551 detail::ssa_check_scheduling(
sn);
552 detail::ssa_check_routing(
sn);
553 detail::ssa_check_phases(
sn);
554 detail::ssa_check_cdscaling(
sn);
555 detail::ssa_check_regions(
sn);
556 detail::ssa_check_reply(
sn);
557 detail::spn_nrm_supported(
sn);
566 if (nd.
nodetype == qn::NodeType::Transition) {
572 if (
opt.state_space_gen !=
"none" &&
opt.state_space_gen !=
"default")
619 std::vector<SsaCacheRatio>*
cache) {
628 if (
sn.has_immediate_feedback()) {
629 const std::string& mm =
opt.method;
630 const bool runs_nrm =
631 mm ==
"nrm" || ((mm ==
"default" || mm ==
"para" || mm ==
"pana" || mm ==
"parallel") &&
632 detail::ssa_nrm_eligible(
sn));
634 std::cerr <<
"[LINE] Warning: SolverSSA(method=nrm) does not model immediate feedback "
635 "(immfeed); self-loops are treated as class-switching with re-queueing. "
636 "Use method='serial' for immediate feedback."
639 const std::string& m =
opt.method;
640 if (m ==
"default") {
648 if (serial_detail::serial_can_run(
sn))
649 return detail::ssa_serial_avg(
sn,
opt,
cache);
655 if (!detail::ssa_check_reply(
sn,
false)) {
656 std::cerr <<
"[LINE] Warning: SolverSSA: NRM does not support synchronous calls "
657 "(REPLY signals); falling back to the serial method."
659 return detail::ssa_serial_avg(
sn,
opt,
cache);
663 if (m ==
"ssa" || m ==
"serial")
return detail::ssa_serial_avg(
sn,
opt,
cache);
664 if (m ==
"para" || m ==
"pana" || m ==
"parallel") {
671 "' is not a valid method; the reference offers 'default', 'ssa', "
672 "'serial', 'para', 'parallel' and 'nrm', all of which this port "
689 std::vector<SsaCacheRatio>*
cache =
nullptr) {
708 const std::vector<SsaCacheRatio>&
cache) {
710 sn, std::vector<T>(), std::vector<T>(), std::vector<T>(), std::vector<T>(),
Matrix<T>(),
712 for (std::size_t c = 0; c < out.
caches.size(); ++c) {
713 for (std::size_t j = 0; j <
cache.size(); ++j) {
720 for (std::size_t r = 0; r <
cache[j].hitprob.size(); ++r)
722 for (std::size_t r = 0; r <
cache[j].missprob.size(); ++r)
724 for (std::size_t r = 0; r <
cache[j].delayedprob.size(); ++r)
726 for (std::size_t r = 0; r <
cache[j].residt.size(); ++r)
756 const std::vector<SsaCacheRatio>&
cache) {
757 if (
cache.empty())
return base;
759 const std::size_t I =
sn.nodes.size(), K =
sn.nclasses;
760 if (I == 0 || K == 0 ||
sn.rtnodes.rows() < I * K)
return base;
764 std::vector<std::vector<bool> > conn(I, std::vector<bool>(I,
false));
765 for (std::size_t a = 0; a < I; ++a)
766 for (std::size_t b = 0; b < I; ++b) {
767 if (a == b)
continue;
768 for (std::size_t r = 0; r < K && !conn[a][b]; ++r)
769 for (std::size_t s = 0; s < K && !conn[a][b]; ++s)
770 if (
sn.rtnodes(a * K + r, b * K + s) > zero) conn[a][b] =
true;
772 bool rewrote =
false;
773 for (std::size_t j = 0; j <
cache.size(); ++j) {
774 const std::size_t ind =
cache[j].node;
775 if (ind == 0 || ind > I)
continue;
776 typename std::map<std::size_t, qn::CacheParam<T> >::const_iterator ci =
777 sn.nodeparam.find(ind);
778 if (ci ==
sn.nodeparam.end())
continue;
779 const std::size_t nd = ind - 1;
780 for (std::size_t r = 0; r < K; ++r) {
781 if (ci->second.hitclass.size() <= r || ci->second.missclass.size() <= r)
continue;
782 const std::size_t hc = ci->second.hitclass[r],
mc = ci->second.missclass[r];
783 if (hc == 0 ||
mc == 0 || hc > K ||
mc > K)
continue;
784 if (
cache[j].hitprob.size() <= r ||
cache[j].missprob.size() <= r)
continue;
785 const double hp =
cache[j].hitprob[r], mp =
cache[j].missprob[r];
786 if (!(hp == hp) || !(mp == mp))
continue;
787 for (std::size_t col = 0; col < I * K; ++col)
sn.rtnodes(nd * K + r, col) = zero;
788 for (std::size_t jnd = 0; jnd < I; ++jnd) {
789 if (!conn[nd][jnd])
continue;
796 if (!rewrote)
return base;
797 sn.da_recompute_visits_from_rtnodes();
What a solver observed about the Cache nodes of a model.
UnsupportedError(const std::string &what)
A network plus its refreshed NetworkStruct.
The NRM engine: the reaction network built from sn, and the sample path.
const std::vector< SsaCacheRatio > & cache() const
The cache write-back of the last run(), one entry per Cache node.
SsaSolution run()
Run opt.samples firings and return the time-averaged metrics.
The SPN Next-Reaction-Method engine, solver_ssa_nrm_spn of the reference.
The exception types the port throws.
SnPnAvgRates< T > sn_pn_avg_rates(const qn::NetworkStruct< T > &sn, const Matrix< T > &QN, const Matrix< T > &TN, const Matrix< T > &AN, const Matrix< T > &RN)
Port of sn_pn_avg_rates: rewrite the place rows of TN, AN and RN so that they agree with the recovere...
SchedStrategy
Scheduling disciplines, with the values of MATLAB SchedStrategy.
DropStrategy
Blocking and loss rules, with the values of MATLAB DropStrategy.
RoutingStrategy
Routing strategies, with the values of MATLAB RoutingStrategy.
ProcessType
Distribution kinds, with the values of MATLAB ProcessType.
const char * sched_to_text(SchedStrategy s)
const char * routing_to_text(RoutingStrategy r)
CacheMetrics< T > cache_metrics_of(const qn::NetworkStruct< T > &sn, const std::vector< T > &hitprob, const std::vector< T > &missprob, const std::vector< T > &delayedprob, const std::vector< T > &latency, const Matrix< T > &hitproblist, const Matrix< T > &itemprob, const std::vector< T > &listcost)
Assemble CacheMetrics from what a cache analyzer returned.
SsaSerialSolution< T > solver_ssa_serial_analyzer(const qn::NetworkStruct< T > &sn, const SsaSerialOptions &opt)
Port of solver_ssa_analyzer_serial.m plus the fork-join wrapper @@SolverSSA/runAnalyzer....
SsaSolution solver_ssa_nrm_analyzer(const qn::NetworkStruct< T > &sn, const SsaOptions &opt, std::vector< SsaCacheRatio > *cache=nullptr)
solver_ssa_analyzer_nrm.m: run the NRM and return the metric table.
SsaSolution solver_ssa_engine(const qn::NetworkStruct< T > &sn, const SsaOptions &opt, std::vector< SsaCacheRatio > *cache)
solver_ssa_analyzer.m: choose the method.
SsaParallelSolution< T > solver_ssa_parallel_analyzer(const qn::NetworkStruct< T > &sn, const SsaParallelOptions &opt)
solver_ssa_analyzer_parallel.m: run R replicas of the serial engine and combine their estimates.
SsaNrmSpaceSolution< T > solver_ssa_nrm_space_analyzer(const qn::NetworkStruct< T > &sn, const SsaNrmSpaceOptions &opt)
Port of the else branch of solver_ssa_analyzer_nrm.m, the one state_space_gen selects: the means as p...
std::vector< std::string > list_valid_methods()
Port of SolverSSA.listValidMethods.
SsaSolution solver_ssa(const qn::NetworkStruct< T > &sn, const SsaOptions &opt, std::vector< SsaCacheRatio > *cache=nullptr)
@@SolverSSA/runAnalyzer itself: the engine the method selects, then the result assembly the reference...
qn::NetworkStruct< T > sn_with_ssa_cache_split(const qn::NetworkStruct< T > &base, const std::vector< SsaCacheRatio > &cache)
The struct with the cache split the SIMULATION MEASURED, visits rebuilt.
solvers::CacheMetrics< T > cache_metrics_of_ssa(const qn::NetworkStruct< T > &sn, const std::vector< SsaCacheRatio > &cache)
CacheMetrics from the serial engine's cache write-back.
Conservation laws of a layered queueing network, enumerated from its structure.
A queueing network and its refreshed NetworkStruct.
Ports of matlab/src/api/sn/sn_pn_firing_rates.m and sn_pn_avg_rates.m.
SolverSSA, the nrm method: a port of solver_ssa_nrm.m and its analyzer solver_ssa_analyzer_nrm....
SolverSSA, the EXPLICIT STATE SPACE variant of the Next Reaction Method: a port of solver_ssa_nrm_spa...
SolverSSA on a STOCHASTIC PETRI NET: the port of solver_ssa_nrm_spn, the sub-engine solver_ssa_nrm....
SolverSSA, the para / parallel method: a port of solver_ssa_analyzer_parallel.m.
SolverSSA, the serial method: a port of solver_ssa_reachability.m, of the run loop of solver_ssa....
Controls, results and the random source of SolverSSA.
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< T > hitprob
(K) TRUE hit fraction, EMPTY = not computed
std::vector< T > delayedprob
(K) delayed-hit fraction, EMPTY off a retrieval system
std::vector< T > latency
(K) expected retrieval latency, EMPTY = not computed
std::vector< T > missprob
(K)
Controls, defaulting to SolverOptions('SSA') in the reference.
What the analyzer returns, in the same shape as the MVA and fluid results.