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) {
263 for (std::size_t r = 0; r < sn.nclasses; ++r) {
264 if (sn.disabled[i][r])
continue;
267 if (!raise)
return false;
269 "SolverSSA(method='nrm'): class '" + sn.classes[r].name +
"' has MAP service "
270 "at FCFS station '" + sn.stations[i].name +
"', whose draws are chained "
271 "through a memory phase the NRM does not carry; ask for 'default', which "
272 "falls back to the serial engine");
275 for (std::size_t r = 0; r < sn.nclasses; ++r) {
276 if (sn.disabled[i][r])
continue;
281 if (!raise)
return false;
283 "SolverSSA(method='nrm'): class '" + sn.classes[r].name +
"' has non-exponential "
284 "service at station '" + sn.stations[i].name +
"', whose '" +
286 "' discipline the NRM phase expansion does not cover; ask for 'default', which "
287 "falls back to the serial engine as the reference does");
303bool ssa_check_cdscaling(
const qn::NetworkStruct<T>& sn,
bool raise =
true) {
307 if (!raise)
return false;
309 "SolverSSA(method='nrm'): the model declares a global dependence "
310 "(setGlobalDependence). The NRM builds its propensities from the per-station "
311 "population slice and never sees the whole population matrix the handle reads, so "
312 "the sample path would run at the unscaled rates; ask for method='serial', which "
328bool ssa_check_regions(
const qn::NetworkStruct<T>& sn,
bool raise =
true) {
329 for (
const typename qn::NetworkStruct<T>::Region& rg : sn.regions)
330 for (std::size_t r = 0; r < rg.rule.size(); ++r) {
332 if (d != qn::DropStrategy::BAS && d != qn::DropStrategy::BBS &&
333 d != qn::DropStrategy::RSRD)
335 if (!raise)
return false;
337 "SolverSSA(method='nrm'): finite capacity region '" + rg.name +
338 "' declares a blocking rule for class '" + sn.classes[r].name +
339 "'. The NRM region gate implements DROP and WAITQ only, as the reference's does");
354bool ssa_check_reply(
const qn::NetworkStruct<T>& sn,
bool raise =
true) {
355 for (std::size_t ind = 0; ind < sn.replyblock.size(); ++ind)
356 for (std::size_t k = 0; k < sn.replyblock[ind].size(); ++k) {
357 if (!sn.replyblock[ind][k])
continue;
358 if (!raise)
return false;
360 "SolverSSA(method='nrm'): class '" + sn.classes[k].name +
"' makes a synchronous "
361 "call (REPLY signal) from node '" + sn.nodes[ind].name +
"'. The NRM has no "
362 "held-server counter for a pending reply; use method='serial'");
378bool ssa_nrm_eligible(
const qn::NetworkStruct<T>& sn) {
387 if (sn.has_immediate_feedback())
return false;
388 return ssa_check_nodes(sn,
false) && ssa_check_scheduling(sn,
false) &&
389 ssa_check_routing(sn,
false) && ssa_check_phases(sn,
false) &&
390 ssa_check_cdscaling(sn,
false) && ssa_check_regions(sn,
false) &&
391 ssa_check_reply(sn,
false) && spn_nrm_supported(sn,
false);
405std::string ssa_nrm_supports(
const qn::NetworkStruct<T>& sn) {
407 ssa_check_nodes(sn,
true);
408 ssa_check_scheduling(sn,
true);
409 ssa_check_routing(sn,
true);
410 ssa_check_phases(sn,
true);
411 ssa_check_cdscaling(sn,
true);
412 ssa_check_regions(sn,
true);
413 ssa_check_reply(sn,
true);
414 spn_nrm_supported(sn,
true);
415 }
catch (
const UnsupportedError& e) {
429inline SsaNrmSpaceOptions ssa_space_options(
const SsaOptions& o) {
430 SsaNrmSpaceOptions s;
431 static_cast<SsaOptions&
>(s) = o;
445inline SsaSerialOptions ssa_serial_options(
const SsaOptions& o) {
447 static_cast<SsaOptions&
>(s) = o;
451inline SsaParallelOptions ssa_parallel_options(
const SsaOptions& o) {
452 SsaParallelOptions p;
453 static_cast<SsaOptions&
>(p) = o;
466SsaSolution ssa_serial_avg(
const qn::NetworkStruct<T>& sn,
const SsaOptions& opt,
467 std::vector<SsaCacheRatio>* cache) {
469 if (cache) *cache = s.cache;
500SsaSolution ssa_pn_token_rates(
const qn::NetworkStruct<T>& sn,
const SsaSolution& in) {
501 bool has_place =
false;
502 for (std::size_t a = 0; a < sn.nodes.size() && !has_place; ++a)
503 if (sn.nodes[a].nodetype == qn::NodeType::Place) has_place =
true;
504 if (!has_place || in.TN.rows() == 0)
return in;
506 const std::size_t M = in.TN.rows(), K = in.TN.cols();
507 Matrix<T>
QN(M, K), TN(M, K), RN(M, K);
508 for (std::size_t i = 0; i < M; ++i)
509 for (std::size_t k = 0; k < K; ++k) {
510 QN(i, k) = num_traits<T>::from_double(in.QN(i, k));
511 TN(i, k) = num_traits<T>::from_double(in.TN(i, k));
512 RN(i, k) = num_traits<T>::from_double(in.RN(i, k));
516 SsaSolution out = in;
517 for (std::size_t i = 0; i < M; ++i)
518 for (std::size_t k = 0; k < K; ++k) {
519 out.TN(i, k) = num_traits<T>::to_double(pn.TN(i, k));
520 out.RN(i, k) = num_traits<T>::to_double(pn.RN(i, k));
538 return {
"default",
"ssa",
"serial",
"para",
"pana",
"parallel",
"nrm"};
550 std::vector<SsaCacheRatio>*
cache =
nullptr) {
555 if constexpr (!std::is_same<T, double>::value) {
560 "solver_ssa_nrm: an SSA sample path is generated from exponential clocks, which are "
561 "logarithms of uniform draws; there is no exact value to compute and a wider float "
562 "carries no information the Monte Carlo error does not swamp. Rerun with --arith "
565 detail::ssa_check_nodes(
sn);
566 detail::ssa_check_scheduling(
sn);
567 detail::ssa_check_routing(
sn);
568 detail::ssa_check_phases(
sn);
569 detail::ssa_check_cdscaling(
sn);
570 detail::ssa_check_regions(
sn);
571 detail::ssa_check_reply(
sn);
572 detail::spn_nrm_supported(
sn);
581 if (nd.
nodetype == qn::NodeType::Transition) {
587 if (
opt.state_space_gen !=
"none" &&
opt.state_space_gen !=
"default")
634 std::vector<SsaCacheRatio>*
cache) {
643 if (
sn.has_immediate_feedback()) {
644 const std::string& mm =
opt.method;
645 const bool runs_nrm =
646 mm ==
"nrm" || ((mm ==
"default" || mm ==
"para" || mm ==
"pana" || mm ==
"parallel") &&
647 detail::ssa_nrm_eligible(
sn));
649 std::cerr <<
"[LINE] Warning: SolverSSA(method=nrm) does not model immediate feedback "
650 "(immfeed); self-loops are treated as class-switching with re-queueing. "
651 "Use method='serial' for immediate feedback."
654 const std::string& m =
opt.method;
655 if (m ==
"default") {
663 if (serial_detail::serial_can_run(
sn))
664 return detail::ssa_serial_avg(
sn,
opt,
cache);
670 if (!detail::ssa_check_reply(
sn,
false)) {
671 std::cerr <<
"[LINE] Warning: SolverSSA: NRM does not support synchronous calls "
672 "(REPLY signals); falling back to the serial method."
674 return detail::ssa_serial_avg(
sn,
opt,
cache);
678 if (m ==
"ssa" || m ==
"serial")
return detail::ssa_serial_avg(
sn,
opt,
cache);
679 if (m ==
"para" || m ==
"pana" || m ==
"parallel") {
686 "' is not a valid method; the reference offers 'default', 'ssa', "
687 "'serial', 'para', 'parallel' and 'nrm', all of which this port "
704 std::vector<SsaCacheRatio>*
cache =
nullptr) {
723 const std::vector<SsaCacheRatio>&
cache) {
725 sn, std::vector<T>(), std::vector<T>(), std::vector<T>(), std::vector<T>(),
Matrix<T>(),
727 for (std::size_t c = 0; c < out.
caches.size(); ++c) {
728 for (std::size_t j = 0; j <
cache.size(); ++j) {
735 for (std::size_t r = 0; r <
cache[j].hitprob.size(); ++r)
737 for (std::size_t r = 0; r <
cache[j].missprob.size(); ++r)
739 for (std::size_t r = 0; r <
cache[j].delayedprob.size(); ++r)
741 for (std::size_t r = 0; r <
cache[j].residt.size(); ++r)
771 const std::vector<SsaCacheRatio>&
cache) {
772 if (
cache.empty())
return base;
774 const std::size_t I =
sn.nodes.size(), K =
sn.nclasses;
775 if (I == 0 || K == 0 ||
sn.rtnodes.rows() < I * K)
return base;
779 std::vector<std::vector<bool> > conn(I, std::vector<bool>(I,
false));
780 for (std::size_t a = 0; a < I; ++a)
781 for (std::size_t b = 0; b < I; ++b) {
782 if (a == b)
continue;
783 for (std::size_t r = 0; r < K && !conn[a][b]; ++r)
784 for (std::size_t s = 0; s < K && !conn[a][b]; ++s)
785 if (
sn.rtnodes(a * K + r, b * K + s) > zero) conn[a][b] =
true;
787 bool rewrote =
false;
788 for (std::size_t j = 0; j <
cache.size(); ++j) {
789 const std::size_t ind =
cache[j].node;
790 if (ind == 0 || ind > I)
continue;
791 typename std::map<std::size_t, qn::CacheParam<T> >::const_iterator ci =
792 sn.nodeparam.find(ind);
793 if (ci ==
sn.nodeparam.end())
continue;
794 const std::size_t nd = ind - 1;
795 for (std::size_t r = 0; r < K; ++r) {
796 if (ci->second.hitclass.size() <= r || ci->second.missclass.size() <= r)
continue;
797 const std::size_t hc = ci->second.hitclass[r],
mc = ci->second.missclass[r];
798 if (hc == 0 ||
mc == 0 || hc > K ||
mc > K)
continue;
799 if (
cache[j].hitprob.size() <= r ||
cache[j].missprob.size() <= r)
continue;
800 const double hp =
cache[j].hitprob[r], mp =
cache[j].missprob[r];
801 if (!(hp == hp) || !(mp == mp))
continue;
802 for (std::size_t col = 0; col < I * K; ++col)
sn.rtnodes(nd * K + r, col) = zero;
803 for (std::size_t jnd = 0; jnd < I; ++jnd) {
804 if (!conn[nd][jnd])
continue;
811 if (!rewrote)
return base;
812 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.