5#ifndef LINE_SOLVERS_SSA_SSA_DISPATCH_H
6#define LINE_SOLVERS_SSA_SSA_DISPATCH_H
135bool ssa_check_scheduling(
const qn::NetworkStruct<T>& sn,
bool raise =
true) {
137 for (std::size_t i = 0; i < sn.nstations; ++i) {
159 if (!raise)
return false;
161 "SolverSSA(method='nrm'): station '" + sn.stations[i].name +
162 "' uses pass-and-swap / order-independent scheduling. The reference NRM "
163 "supports it (solver_ssa_nrm.m, oirate/oiDepart/pasInSvc) by enumerating the "
164 "ordered job list against mu(c) and the swap graph; that sub-engine is not "
167 if (!raise)
return false;
169 "SolverSSA(method='nrm'): station '" + sn.stations[i].name +
170 "' uses POLLING scheduling. The reference NRM carries a polling controller "
171 "[mode, pos, swphase, ctr] with its own switchover reactions "
172 "(State.pollingInfo / State.pollingNext); that sub-engine is not ported to "
175 if (!raise)
return false;
178 sn.stations[i].name +
179 "' is not supported by the NRM in any codebase");
187bool ssa_check_nodes(
const qn::NetworkStruct<T>& sn,
bool raise =
true) {
188 for (
const qn::NodeDef& nd : sn.nodes) {
189 switch (nd.nodetype) {
190 case qn::NodeType::Place:
191 case qn::NodeType::Transition:
192 if (!raise)
return false;
194 "SolverSSA(method='nrm'): node '" + nd.name +
195 "' makes this model a stochastic Petri net. The reference routes it to "
196 "solver_ssa_nrm_spn, a separate builder and run loop with immediate-mode "
197 "vanishing-marking collapse; that path is not ported to C++");
198 case qn::NodeType::Cache:
199 if (!raise)
return false;
201 "SolverSSA(method='nrm'): node '" + nd.name +
202 "' is a Cache. The reference NRM models a cache access as a state-dependent "
203 "class switch and applies State.afterEventCache's replacement policies (and "
204 "the delayed-hit retrieval system) to the cache contents; that sub-engine is "
205 "not ported to C++");
206 case qn::NodeType::Fork:
207 case qn::NodeType::Join:
208 if (!raise)
return false;
210 "' makes this a fork-join model, which the NRM does not "
211 "handle in any codebase (isNrmEligible excludes it); the "
212 "reference falls back to the serial engine, whose own "
213 "fork handler (sn.fjsync / State.afterFJEvent) is not "
238bool ssa_check_routing(
const qn::NetworkStruct<T>& sn,
bool raise =
true) {
239 for (
const qn::NodeDef& nd : sn.nodes)
240 for (std::size_t r = 0; r < nd.routing.size(); ++r) {
242 if (rs == qn::RoutingStrategy::PROB || rs == qn::RoutingStrategy::RAND ||
243 rs == qn::RoutingStrategy::DISABLED || rs == qn::RoutingStrategy::RROBIN ||
244 rs == qn::RoutingStrategy::WRROBIN || rs == qn::RoutingStrategy::JSQ ||
245 rs == qn::RoutingStrategy::SQ)
247 if (!raise)
return false;
249 "SolverSSA(method='nrm'): node '" + nd.name +
"' routes class '" +
251 "'. Its destination is a function the C++ NetworkStruct does not carry, so "
252 "there is nothing to resolve at firing time");
271bool ssa_check_phases(
const qn::NetworkStruct<T>& sn,
bool raise =
true) {
273 for (std::size_t i = 0; i < sn.nstations; ++i) {
282 for (std::size_t r = 0; r < sn.nclasses; ++r) {
283 if (sn.disabled[i][r])
continue;
288 if (!raise)
return false;
290 "SolverSSA(method='nrm'): class '" + sn.classes[r].name +
"' has non-exponential "
291 "service at station '" + sn.stations[i].name +
"', whose '" +
293 "' discipline the NRM phase expansion does not cover; ask for 'default', which "
294 "falls back to the serial engine as the reference does");
313bool ssa_check_cdscaling(
const qn::NetworkStruct<T>& sn,
bool raise =
true) {
314 for (std::size_t i = 0; i < sn.nstations; ++i)
315 if (sn.stations[i].cdscaling || sn.stations[i].jdscaling) {
316 if (!raise)
return false;
318 "SolverSSA(method='nrm'): station '" + sn.stations[i].name +
319 "' declares a class- or joint-dependent scaling. The NRM builds its reaction "
320 "rates without evaluating the dependence handle, so the sample path would run at "
321 "the unscaled rates; ask for method='serial', which applies it");
327 if (!raise)
return false;
329 "SolverSSA(method='nrm'): the model declares a global dependence "
330 "(setGlobalDependence). The NRM builds its propensities from the per-station "
331 "population slice and never sees the whole population matrix the handle reads, so "
332 "the sample path would run at the unscaled rates; ask for method='serial', which "
351bool ssa_check_regions(
const qn::NetworkStruct<T>& sn,
bool raise =
true) {
352 if (!sn.regions.empty()) {
353 if (!raise)
return false;
355 "SolverSSA(method='nrm'): the model declares a finite capacity region. The "
356 "reference carries a per-region FIFO (`fcrBuf`) with an admission gate on every "
357 "arrival into the region and a release cascade after every firing (`fcr_release`), "
358 "under both the WAITQ and the DROP rule; that sub-engine is not ported to C++");
374bool ssa_nrm_eligible(
const qn::NetworkStruct<T>& sn) {
375 return ssa_check_nodes(sn,
false) && ssa_check_scheduling(sn,
false) &&
376 ssa_check_routing(sn,
false) && ssa_check_phases(sn,
false) &&
377 ssa_check_cdscaling(sn,
false) && ssa_check_regions(sn,
false);
391std::string ssa_nrm_supports(
const qn::NetworkStruct<T>& sn) {
393 ssa_check_nodes(sn,
true);
394 ssa_check_scheduling(sn,
true);
395 ssa_check_routing(sn,
true);
396 ssa_check_phases(sn,
true);
397 ssa_check_cdscaling(sn,
true);
398 ssa_check_regions(sn,
true);
399 }
catch (
const UnsupportedError& e) {
413inline SsaNrmSpaceOptions ssa_space_options(
const SsaOptions& o) {
414 SsaNrmSpaceOptions s;
415 static_cast<SsaOptions&
>(s) = o;
429inline SsaSerialOptions ssa_serial_options(
const SsaOptions& o) {
431 static_cast<SsaOptions&
>(s) = o;
435inline SsaParallelOptions ssa_parallel_options(
const SsaOptions& o) {
436 SsaParallelOptions p;
437 static_cast<SsaOptions&
>(p) = o;
450SsaSolution ssa_serial_avg(
const qn::NetworkStruct<T>& sn,
const SsaOptions& opt,
451 std::vector<SsaCacheRatio>* cache) {
453 if (cache) *cache = s.cache;
470 return {
"default",
"ssa",
"serial",
"para",
"parallel",
"nrm"};
486 if constexpr (!std::is_same<T, double>::value) {
490 "solver_ssa_nrm: an SSA sample path is generated from exponential clocks, which are "
491 "logarithms of uniform draws; there is no exact value to compute and a wider float "
492 "carries no information the Monte Carlo error does not swamp. Rerun with --arith "
495 detail::ssa_check_nodes(
sn);
496 detail::ssa_check_scheduling(
sn);
497 detail::ssa_check_routing(
sn);
498 detail::ssa_check_phases(
sn);
499 detail::ssa_check_cdscaling(
sn);
500 detail::ssa_check_regions(
sn);
503 if (
opt.state_space_gen !=
"none" &&
opt.state_space_gen !=
"default")
548 std::vector<SsaCacheRatio>*
cache =
nullptr) {
558 if (
sn.has_immediate_feedback()) {
559 const std::string& mm =
opt.method;
563 const bool runs_nrm =
564 mm ==
"nrm" || ((mm ==
"default" || mm ==
"para" || mm ==
"parallel") &&
565 detail::ssa_nrm_eligible(
sn));
568 "SolverSSA(method='" + mm +
569 "'): immediate feedback (setImmediateFeedback) is not ported to this engine. The "
570 "reference holds the server across the fed-back service (solver_ssa.m's "
571 "immfeed_selfloop into State.afterEvent) and this port has no such arc, so the "
572 "trajectory would release and re-queue the job; use method='nrm' on an "
573 "NRM-eligible model, which approximates it as the reference's own NRM does, or "
574 "SolverMVA / SolverNC");
575 std::cerr <<
"[LINE] Warning: SolverSSA(method=nrm) does not model immediate feedback "
576 "(immfeed); self-loops are treated as class-switching with re-queueing. Use "
577 "method='serial' in the reference for immediate feedback."
580 const std::string& m =
opt.method;
581 if (m ==
"default") {
589 if (serial_detail::serial_can_run(
sn))
590 return detail::ssa_serial_avg(
sn,
opt,
cache);
594 if (m ==
"ssa" || m ==
"serial")
return detail::ssa_serial_avg(
sn,
opt,
cache);
595 if (m ==
"para" || m ==
"parallel") {
602 "' is not a valid method; the reference offers 'default', 'ssa', "
603 "'serial', 'para', 'parallel' and 'nrm', all of which this port "
622 const std::vector<SsaCacheRatio>&
cache) {
624 sn, std::vector<T>(), std::vector<T>(), std::vector<T>(), std::vector<T>(),
Matrix<T>(),
626 for (std::size_t c = 0; c < out.
caches.size(); ++c) {
627 for (std::size_t j = 0; j <
cache.size(); ++j) {
634 for (std::size_t r = 0; r <
cache[j].hitprob.size(); ++r)
636 for (std::size_t r = 0; r <
cache[j].missprob.size(); ++r)
638 for (std::size_t r = 0; r <
cache[j].delayedprob.size(); ++r)
640 for (std::size_t r = 0; r <
cache[j].residt.size(); ++r)
670 const std::vector<SsaCacheRatio>&
cache) {
671 if (
cache.empty())
return base;
673 const std::size_t I =
sn.nodes.size(), K =
sn.nclasses;
674 if (I == 0 || K == 0 ||
sn.rtnodes.rows() < I * K)
return base;
678 std::vector<std::vector<bool> > conn(I, std::vector<bool>(I,
false));
679 for (std::size_t a = 0; a < I; ++a)
680 for (std::size_t b = 0; b < I; ++b) {
681 if (a == b)
continue;
682 for (std::size_t r = 0; r < K && !conn[a][b]; ++r)
683 for (std::size_t s = 0; s < K && !conn[a][b]; ++s)
684 if (
sn.rtnodes(a * K + r, b * K + s) > zero) conn[a][b] =
true;
686 bool rewrote =
false;
687 for (std::size_t j = 0; j <
cache.size(); ++j) {
688 const std::size_t ind =
cache[j].node;
689 if (ind == 0 || ind > I)
continue;
690 typename std::map<std::size_t, qn::CacheParam<T> >::const_iterator ci =
691 sn.nodeparam.find(ind);
692 if (ci ==
sn.nodeparam.end())
continue;
693 const std::size_t nd = ind - 1;
694 for (std::size_t r = 0; r < K; ++r) {
695 if (ci->second.hitclass.size() <= r || ci->second.missclass.size() <= r)
continue;
696 const std::size_t hc = ci->second.hitclass[r],
mc = ci->second.missclass[r];
697 if (hc == 0 ||
mc == 0 || hc > K ||
mc > K)
continue;
698 if (
cache[j].hitprob.size() <= r ||
cache[j].missprob.size() <= r)
continue;
699 const double hp =
cache[j].hitprob[r], mp =
cache[j].missprob[r];
700 if (!(hp == hp) || !(mp == mp))
continue;
701 for (std::size_t col = 0; col < I * K; ++col)
sn.rtnodes(nd * K + r, col) = zero;
702 for (std::size_t jnd = 0; jnd < I; ++jnd) {
703 if (!conn[nd][jnd])
continue;
710 if (!rewrote)
return base;
711 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.
SsaSolution run()
Run opt.samples firings and return the time-averaged metrics.
The exception types the port throws.
SchedStrategy
Scheduling disciplines, with the values of MATLAB SchedStrategy.
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)
solver_ssa_analyzer_nrm.m: run the NRM and return the metric table.
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)
solver_ssa_analyzer.m: choose the method.
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.
A queueing network and its refreshed NetworkStruct.
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, 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.