5#ifndef LINE_SOLVERS_SSA_SOLVER_SSA_PARALLEL_H
6#define LINE_SOLVERS_SSA_SOLVER_SSA_PARALLEL_H
112 std::vector<unsigned long>
seed;
138namespace parallel_detail {
141inline void mean_sem(
const std::vector<double>& x,
double& mean,
double& sem) {
142 const std::size_t R = x.size();
143 const double nan = std::numeric_limits<double>::quiet_NaN();
150 for (std::size_t r = 0; r < R; ++r) s += x[r];
151 mean = s /
static_cast<double>(R);
160 for (std::size_t r = 0; r < R; ++r) ss += (x[r] - mean) * (x[r] - mean);
161 sem = std::sqrt(ss /
static_cast<double>(R - 1)) / std::sqrt(
static_cast<double>(R));
165inline void reduce_matrix(
const std::vector<SsaSolution>& rep,
Matrix<double> SsaSolution::*m,
167 const std::size_t R = rep.size();
168 const std::size_t nr = R ? (rep[0].*m).rows() : 0;
169 const std::size_t
nc = R ? (rep[0].*m).cols() : 0;
172 std::vector<double> col(R, 0.0);
173 for (std::size_t i = 0; i < nr; ++i)
174 for (std::size_t j = 0; j <
nc; ++j) {
175 for (std::size_t r = 0; r < R; ++r) col[r] = (rep[r].*m)(i, j);
176 double mu = 0.0, se = 0.0;
177 mean_sem(col, mu, se);
184inline void reduce_vector(
const std::vector<SsaSolution>& rep, std::vector<double> SsaSolution::*v,
185 std::vector<double>& mean, std::vector<double>& sem) {
186 const std::size_t R = rep.size();
187 const std::size_t n = R ? (rep[0].*v).size() : 0;
190 std::vector<double> col(R, 0.0);
191 for (std::size_t i = 0; i < n; ++i) {
192 for (std::size_t r = 0; r < R; ++r) col[r] = (rep[r].*v)[i];
193 mean_sem(col, mean[i], sem[i]);
214 if constexpr (!std::is_same<T, double>::value) {
218 "solver_ssa_parallel: an SSA sample path is generated from exponential clocks, which "
219 "are logarithms of uniform draws; there is no exact value to compute and a wider "
220 "float carries no information the Monte Carlo error does not swamp. Rerun with "
228 "solver_ssa_parallel: each replica is an SSA sample path generated from "
229 "exponential clocks drawn as -mean*log(u), which needs transcendental "
234 "SolverSSA(method='parallel'): options.config.eventcache is set. The memo itself "
235 "is ported (ssa::SsaEventCache, which the reference builds per replica via "
236 "EventCache.create) and is verified against recomputation, but SsaSerialEngine "
237 "still calls the free qn::after_event and takes no cache argument, so setting the "
238 "flag here would advertise a memo the replicas never consult. Wire the engine's "
239 "enabled() through SsaEventCache::after_event first");
241 const std::size_t R =
opt.nreplicas > 0 ?
opt.nreplicas : 1;
245 std::size_t per = (
opt.samples + R - 1) / R;
246 if (per == 0) per = 1;
260 std::vector<std::vector<SsaCacheRatio> > rep_cache;
261 rep_cache.reserve(R);
262 for (std::size_t r = 0; r < R; ++r) {
275 ropt.
seed =
opt.seed +
static_cast<unsigned long>(r);
278 rep_cache.push_back(one.
cache);
301 for (std::size_t r = 0; r < R; ++r) {
310 const std::size_t K =
sn.nclasses;
312 sn.nodeparam.begin();
313 ci !=
sn.nodeparam.end(); ++ci) {
314 const std::size_t ind = ci->first;
315 if (ind == 0 || ind >
sn.nodes.size())
continue;
321 cr.
residt.assign(K, std::numeric_limits<double>::quiet_NaN());
322 std::vector<double> dly(K, 0.0);
323 bool any_delayed =
false;
324 for (std::size_t k = 1; k <= K; ++k) {
325 if (ci->second.hitclass.size() < k || ci->second.missclass.size() < k)
continue;
326 double h = 0.0, m = 0.0, d = 0.0;
327 for (std::size_t r = 0; r < R; ++r)
328 for (std::size_t c = 0; c < rep_cache[r].size(); ++c) {
329 if (rep_cache[r][c].node != ind)
continue;
330 h += rep_cache[r][c].hitprob[k - 1] /
static_cast<double>(R);
331 m += rep_cache[r][c].missprob[k - 1] /
static_cast<double>(R);
334 if (!rep_cache[r][c].delayedprob.empty()) {
335 d += rep_cache[r][c].delayedprob[k - 1] /
static_cast<double>(R);
344 out.
cache.push_back(cr);
364 const std::string& m =
opt.method;
365 if (m ==
"para" || m ==
"parallel" || m ==
"default")
367 if (m ==
"serial" || m ==
"ssa")
369 "SolverSSA(parallel): the '" + m +
370 "' method is ONE run of the serial engine, not the mean of " +
371 std::to_string(
opt.nreplicas) +
372 " independent replicas, and the two report the same quantity at different variances. "
373 "Call solver_ssa_serial for it");
375 "' is not a method this entry accepts; it implements 'para' and "
376 "'parallel' and the 'default' alias that reaches them");
UnsupportedError(const std::string &what)
A network plus its refreshed NetworkStruct.
The exception types the port throws.
Dense matrix and non-owning view.
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....
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.
SsaParallelSolution< T > solver_ssa_parallel(const qn::NetworkStruct< T > &sn, const SsaParallelOptions &opt)
The para / parallel entry of solver_ssa_analyzer.m.
A queueing network and its refreshed NetworkStruct.
Number-type abstraction for the templated API port.
SolverSSA, the serial method: a port of solver_ssa_reachability.m, of the run loop of solver_ssa....
Port of EventCache.m and of the lookup that State.afterEvent performs against it (afterEvent....
Controls, results and the random source of SolverSSA.
What the cache write-back of solver_ssa_analyzer_serial.m produces.
std::size_t node
1-based Cache node index
std::vector< double > delayedprob
The delayed-hit share, EMPTY off a retrieval system.
std::vector< double > missprob
per class, NaN where undefined
std::vector< double > hitprob
std::vector< double > residt
actualresidt: NaN, and NOT a port gap.
std::size_t samples
Reaction firings to simulate; options.samples in the reference.
std::string method
default and nrm both select the Next Reaction Method here.
unsigned long seed
options.seed; LINE's own default is 23000.
The replicated engine's knobs: the serial engine's, plus the two the reference reads from options....
bool eventcache
options.config.eventcache: memoize after_event inside each replica.
std::size_t nreplicas
options.config.nreplicas: R, the FIXED number of independent replicas.
What the replicated analyzer returns.
Matrix< double > QN_sem
Standard error of the replica mean, per metric: s / sqrt(R) with s the sample standard deviation ACRO...
SsaSolution avg
The replica MEAN; method = "parallel".
std::vector< double > CN_sem
std::vector< unsigned long > seed
The stream each replica ran on: base_seed + r.
std::vector< double > XN_sem
std::vector< SsaCacheRatio > cache
The Cache write-back of the reference's final loop, averaged over replicas.
std::size_t samples_requested
opt.samples: the budget asked for, which R * samples_per_replica rounds up.
std::size_t samples_per_replica
ceil(samples / R): the firings EACH replica performed.
std::vector< SsaSolution > replica
The R per-replica estimates, in replica order.
The serial engine's knobs: SsaOptions plus the three the serial path reads and the NRM has no use for...
The serial analyzer's return: the metric table, the path, and the stream.
std::vector< SsaCacheRatio > cache
SsaSolution avg
QN, UN, RN, TN, XN, CN; method = "serial".
What the analyzer returns, in the same shape as the MVA and fluid results.
double simulated_time
Simulated time the metrics are averaged over; the reference's totalTime.
std::size_t samples
Reaction firings actually performed.
std::string method
The concrete algorithm, as the reference's method.