5#ifndef LINE_IO_NETWORK_READER_H
6#define LINE_IO_NETWORK_READER_H
52using json = nlohmann::json;
82inline double num_from_json(
const json& v) {
83 if (v.is_null())
return std::numeric_limits<double>::quiet_NaN();
85 const std::string s = v.get<std::string>();
86 if (s ==
"Infinity" || s ==
"inf" || s ==
"Inf")
87 return std::numeric_limits<double>::infinity();
88 if (s ==
"-Infinity" || s ==
"-inf" || s ==
"-Inf")
89 return -std::numeric_limits<double>::infinity();
90 if (s ==
"NaN" || s ==
"nan")
return std::numeric_limits<double>::quiet_NaN();
93 const char* begin = s.c_str();
95 const double parsed = std::strtod(begin, &end);
96 if (end == begin || *end !=
'\0')
97 throw InputError(
"network_reader: expected a number, got the string '" + s +
"'");
100 if (v.is_boolean())
return v.get<
bool>() ? 1.0 : 0.0;
103 return v.get<
double>();
107inline double num_value(
const json& obj,
const char* key,
double def) {
108 return obj.contains(key) ? num_from_json(obj.at(key)) : def;
119inline std::size_t fork_dest_index(
const json& ov,
120 const std::map<std::string, std::size_t>& node_idx,
121 const std::string& fork_name) {
122 if (!ov.contains(
"dest"))
return 0;
123 const std::string d = ov.at(
"dest").get<std::string>();
124 if (d.empty())
return 0;
125 const std::map<std::string, std::size_t>::const_iterator it = node_idx.find(d);
126 if (it == node_idx.end())
127 throw InputError(
"network_reader: the Fork node '" + fork_name +
128 "' declares an override towards '" + d +
"', which is not a node");
135 if (s ==
"INF" || s ==
"inf")
return S::INF;
136 if (s ==
"FCFS" || s ==
"fcfs")
return S::FCFS;
137 if (s ==
"PS" || s ==
"ps")
return S::PS;
138 if (s ==
"LCFS" || s ==
"lcfs")
return S::LCFS;
139 if (s ==
"LCFSPR" || s ==
"lcfspr")
return S::LCFSPR;
140 if (s ==
"SIRO" || s ==
"siro")
return S::SIRO;
141 if (s ==
"HOL" || s ==
"hol")
return S::HOL;
142 if (s ==
"DPS" || s ==
"dps")
return S::DPS;
143 if (s ==
"GPS" || s ==
"gps")
return S::GPS;
144 if (s ==
"SEPT" || s ==
"sept")
return S::SEPT;
145 if (s ==
"LEPT" || s ==
"lept")
return S::LEPT;
146 if (s ==
"SJF" || s ==
"sjf")
return S::SJF;
147 if (s ==
"LJF" || s ==
"ljf")
return S::LJF;
148 if (s ==
"SRPT" || s ==
"srpt")
return S::SRPT;
149 if (s ==
"LPS" || s ==
"lps")
return S::LPS;
150 if (s ==
"POLLING" || s ==
"polling")
return S::POLLING;
156 if (s ==
"FCFSPR" || s ==
"fcfspr")
return S::FCFSPR;
157 if (s ==
"FCFSPI" || s ==
"fcfspi")
return S::FCFSPI;
158 if (s ==
"FCFSPRIO" || s ==
"fcfsprio")
return S::HOL;
159 if (s ==
"FCFSPRPRIO" || s ==
"fcfsprprio")
return S::FCFSPRPRIO;
160 if (s ==
"FCFSPIPRIO" || s ==
"fcfspiprio")
return S::FCFSPIPRIO;
161 if (s ==
"LCFSPI" || s ==
"lcfspi")
return S::LCFSPI;
162 if (s ==
"LCFSPRIO" || s ==
"lcfsprio")
return S::LCFSPRIO;
163 if (s ==
"LCFSPRPRIO" || s ==
"lcfsprprio")
return S::LCFSPRPRIO;
164 if (s ==
"LCFSPIPRIO" || s ==
"lcfspiprio")
return S::LCFSPIPRIO;
165 if (s ==
"PSPRIO" || s ==
"psprio")
return S::PSPRIO;
166 if (s ==
"DPSPRIO" || s ==
"dpsprio")
return S::DPSPRIO;
167 if (s ==
"GPSPRIO" || s ==
"gpsprio")
return S::GPSPRIO;
168 if (s ==
"SRPT" || s ==
"srpt")
return S::SRPT;
169 if (s ==
"SRPTPRIO" || s ==
"srptprio")
return S::SRPTPRIO;
170 if (s ==
"PSJF" || s ==
"psjf")
return S::PSJF;
171 if (s ==
"FB" || s ==
"fb")
return S::FB;
172 if (s ==
"LRPT" || s ==
"lrpt")
return S::LRPT;
173 if (s ==
"SETF" || s ==
"setf")
return S::SETF;
174 if (s ==
"FSP" || s ==
"fsp")
return S::FSP;
175 if (s ==
"EDD" || s ==
"edd")
return S::EDD;
176 if (s ==
"EDF" || s ==
"edf")
return S::EDF;
177 if (s ==
"PAS" || s ==
"pas")
return S::PAS;
178 if (s ==
"OI" || s ==
"oi")
return S::OI;
179 if (s ==
"REF" || s ==
"ref")
return S::REF;
180 if (s ==
"EXT" || s ==
"ext")
return S::EXT;
181 throw UnsupportedError(
"network_reader: unsupported scheduling discipline '" + s +
"'");
188 if (s ==
"drop")
return D::DROP;
189 if (s ==
"waitingQueue")
return D::WAITQ;
190 if (s ==
"blockingAfterService")
return D::BAS;
191 if (s ==
"retrial")
return D::RETRIAL;
192 if (s ==
"retrialWithLimit")
return D::RETRIAL_WITH_LIMIT;
198 if (s ==
"RR" || s ==
"rr")
return R::RR;
199 if (s ==
"FIFO" || s ==
"fifo")
return R::FIFO;
200 if (s ==
"SFIFO" || s ==
"sfifo")
return R::SFIFO;
201 if (s ==
"LRU" || s ==
"lru")
return R::LRU;
202 if (s ==
"HLRU" || s ==
"hlru")
return R::HLRU;
203 if (s ==
"CLIMB" || s ==
"climb")
return R::CLIMB;
204 if (s ==
"QLRU" || s ==
"qlru")
return R::QLRU;
205 throw UnsupportedError(
"network_reader: unsupported cache replacement strategy '" + s +
"'");
210 if (s ==
"GATED" || s ==
"gated")
return P::GATED;
211 if (s ==
"EXHAUSTIVE" || s ==
"exhaustive")
return P::EXHAUSTIVE;
212 if (s ==
"KLIMITED" || s ==
"klimited" || s ==
"K-LIMITED")
return P::KLIMITED;
213 if (s ==
"DECREMENTING" || s ==
"decrementing")
return P::DECREMENTING;
214 throw UnsupportedError(
"network_reader: unsupported polling type '" + s +
"'");
224lang::Distrib<T> hyperexp_fit_mean_scv(
double mean,
double scv) {
226 throw InputError(
"network_reader: HyperExp fitMeanAndSCV needs SCV >= 1, got " +
227 std::to_string(scv));
228 const double p = 0.5 * (1.0 + std::sqrt((scv - 1.0) / (scv + 1.0)));
229 const double mu1 = 2.0 * p / mean;
230 const double mu2 = 2.0 * (1.0 - p) / mean;
232 num_traits<T>::from_double(mu1),
233 num_traits<T>::from_double(mu2));
246inline bool has_cache_key(
const json& nd,
const json& cj,
const char* key) {
247 return nd.contains(key) || cj.contains(key);
249inline const json& cache_key(
const json& nd,
const json& cj,
const char* key) {
250 return nd.contains(key) ? nd.at(key) : cj.at(key);
256 if (s ==
"PROB")
return R::PROB;
257 if (s ==
"RAND")
return R::RAND;
258 if (s ==
"RROBIN")
return R::RROBIN;
259 if (s ==
"WRROBIN")
return R::WRROBIN;
260 if (s ==
"JSQ")
return R::JSQ;
261 if (s ==
"SQ" || s ==
"KCHOICES")
return R::SQ;
264 if (s ==
"SDR")
return R::SDR;
265 if (s ==
"FIRING")
return R::FIRING;
266 if (s ==
"DISABLED")
return R::DISABLED;
267 throw UnsupportedError(
"network_reader: unsupported routing strategy '" + s +
"'");
273 if (s ==
"RENEGING")
return I::RENEGING;
274 if (s ==
"BALKING")
return I::BALKING;
275 if (s ==
"RETRIAL")
return I::RETRIAL;
276 throw UnsupportedError(
"network_reader: unsupported impatience type '" + s +
"'");
282 if (s ==
"QUEUE_LENGTH")
return B::QUEUE_LENGTH;
283 if (s ==
"EXPECTED_WAIT")
return B::EXPECTED_WAIT;
284 if (s ==
"COMBINED")
return B::COMBINED;
285 throw UnsupportedError(
"network_reader: unsupported balking strategy '" + s +
"'");
291 if (s ==
"ORDER")
return H::ORDER;
292 if (s ==
"ALIS")
return H::ALIS;
293 if (s ==
"ALFS")
return H::ALFS;
294 if (s ==
"FAIRNESS")
return H::FAIRNESS;
295 if (s ==
"FSF")
return H::FSF;
296 if (s ==
"RAIS")
return H::RAIS;
297 throw UnsupportedError(
"network_reader: unsupported heterogeneous scheduling policy '" + s +
"'");
304 throw UnsupportedError(
"network_reader: unsupported departure discipline '" + s +
"'");
309std::vector<T> num_vec_from_json(
const json& v) {
316 for (
const json& x : v) out.push_back(num_traits<T>::from_double(num_from_json(x)));
318 out.push_back(num_traits<T>::from_double(num_from_json(v)));
324Matrix<T> mat_from_json(
const json& v) {
326 throw InputError(
"network_reader: expected an array of rows");
327 Matrix<T> M(v.size(), v.empty() ? 0 : v.at(0).size());
328 for (std::size_t i = 0; i < v.size(); ++i) {
329 const json& row = v.at(i);
330 for (std::size_t j = 0; j < row.size(); ++j)
331 M(i, j) = num_traits<T>::from_double(num_from_json(row.at(j)));
338std::vector<Matrix<T> > mat_list_from_json(
const json& v) {
339 std::vector<Matrix<T> > out;
340 for (
const json& m : v) out.push_back(mat_from_json<T>(m));
346lang::Distrib<T> dist_from_fit(
const std::string& type,
const json& fit) {
347 const std::string method = fit.at(
"method").get<std::string>();
348 if (method ==
"fitMean" || method ==
"fitMeanAndOrder") {
349 const double mean = fit.at(
"mean").get<
double>();
351 if (type ==
"Erlang") {
352 const long order = fit.contains(
"order") ? fit.at(
"order").get<
long>() : 1L;
353 const T rate = num_traits<T>::from_double(
double(order) / mean);
359 if (method ==
"fitMeanAndSCV") {
360 const double mean = fit.at(
"mean").get<
double>();
361 const double scv = fit.at(
"scv").get<
double>();
362 if (type ==
"Erlang")
364 num_traits<T>::from_double(scv));
365 if (type ==
"HyperExp")
return hyperexp_fit_mean_scv<T>(mean, scv);
366 if (std::fabs(scv - 1.0) < 1e-12)
368 throw UnsupportedError(
"network_reader: fitMeanAndSCV for family '" + type +
369 "' is not reconstructed; use explicit params");
371 throw UnsupportedError(
"network_reader: unsupported fit method '" + method +
"' for '" + type +
377lang::Distrib<T> dist_from_json(
const json& obj) {
378 const std::string type = obj.at(
"type").get<std::string>();
392 if (type ==
"Prior") {
393 const std::string kind =
394 obj.contains(
"kind") ? obj.at(
"kind").get<std::string>() : std::string(
"discrete");
395 if (kind !=
"discrete" && kind !=
"continuous")
396 throw InputError(
"network_reader: a Prior's 'kind' is 'discrete' or 'continuous', got '" +
398 if (kind ==
"continuous") {
399 if (!obj.contains(
"paramDist") || !obj.contains(
"factory"))
401 "network_reader: a continuous Prior carries 'paramDist' and 'factory' (a "
402 "template distribution plus the parameter slots it fills)");
403 const json& fac = obj.at(
"factory");
404 if (!fac.contains(
"template") || !fac.contains(
"slots"))
406 "network_reader: a continuous Prior's 'factory' carries 'template' and "
408 const json tmpl = fac.at(
"template");
409 std::vector<std::string> slots;
410 for (
const json& s : fac.at(
"slots")) slots.push_back(s.get<std::string>());
413 "network_reader: a continuous Prior's factory names no parameter slot, so the "
414 "parameter would not reach the distribution it builds");
415 if (!tmpl.contains(
"params"))
417 "network_reader: a continuous Prior's factory template carries its parameters "
418 "as 'params'; a fitted or phase-type representation has no named slot");
419 for (std::size_t l = 0; l < slots.size(); ++l)
420 if (!tmpl.at(
"params").contains(slots[l]))
421 throw InputError(
"network_reader: a continuous Prior's factory names '" +
422 slots[l] +
"' as a parameter slot, but its template has no "
428 const std::function<lang::Distrib<T>(
const T&)> factory =
429 [tmpl, slots](
const T& theta) {
431 for (std::size_t l = 0; l < slots.size(); ++l)
432 j[
"params"][slots[l]] = num_traits<T>::to_double(theta);
433 return dist_from_json<T>(j);
437 if (!obj.contains(
"distributions") || !obj.contains(
"probabilities"))
439 "network_reader: a discrete Prior carries 'distributions' and 'probabilities'");
440 std::vector<lang::Distrib<T> > alts;
441 for (
const json& a : obj.at(
"distributions")) alts.push_back(dist_from_json<T>(a));
442 std::vector<T> probs;
443 for (
const json& p : obj.at(
"probabilities"))
444 probs.push_back(num_traits<T>::from_double(p.get<
double>()));
452 if (type ==
"NHPP" || type ==
"MAPt" || type ==
"PHt") {
453 const json& p = obj.at(
"params");
455 for (
const json& b : p.at(
"breakpoints"))
456 bp.push_back(num_traits<T>::from_double(b.get<
double>()));
457 const bool cyc = p.contains(
"cyclic") && p.at(
"cyclic").get<
bool>();
458 auto read_mats = [&](
const char* key) {
459 std::vector<Matrix<T> > out;
460 for (
const json& seg : p.at(key)) {
461 const std::vector<std::vector<double> > rows =
462 seg.get<std::vector<std::vector<double> > >();
463 Matrix<T> M(rows.size(), rows.empty() ? 0 : rows[0].size());
464 for (std::size_t a = 0; a < rows.size(); ++a)
465 for (std::size_t b = 0; b < rows[a].size(); ++b)
466 M(a, b) = num_traits<T>::from_double(rows[a][b]);
471 if (type ==
"NHPP") {
472 std::vector<T> rates;
473 for (
const json& r : p.at(
"rates"))
474 rates.push_back(num_traits<T>::from_double(r.get<
double>()));
479 std::vector<std::vector<T> > alphas;
480 for (
const json& a : p.at(
"alpha")) {
481 const std::vector<double> row = a.get<std::vector<double> >();
483 for (
double v : row) av.push_back(num_traits<T>::from_double(v));
484 alphas.push_back(av);
495 if (type ==
"Replayer" || type ==
"Trace") {
496 if (obj.contains(
"params") && obj.at(
"params").contains(
"fileName")) {
497 const std::string path = obj.at(
"params").at(
"fileName").get<std::string>();
498 std::ifstream tr(path.c_str());
500 std::vector<T> samples;
502 while (tr >> v) samples.push_back(num_traits<T>::from_double(v));
503 if (!samples.empty()) {
510 if (obj.contains(
"ph")) {
511 const json& ph = obj.at(
"ph");
512 const std::vector<double> a = ph.at(
"alpha").get<std::vector<double> >();
513 const std::vector<std::vector<double> > rows =
514 ph.at(
"T").get<std::vector<std::vector<double> > >();
515 std::vector<T> alpha;
516 for (
double x : a) alpha.push_back(num_traits<T>::from_double(x));
517 Matrix<T> A(rows.size(), rows.empty() ? 0 : rows[0].size());
518 for (std::size_t i = 0; i < rows.size(); ++i)
519 for (std::size_t j = 0; j < rows[i].size(); ++j)
520 A(i, j) = num_traits<T>::from_double(rows[i][j]);
523 if (obj.contains(
"params") && obj.at(
"params").contains(
"mean"))
525 num_traits<T>::from_double(obj.at(
"params").at(
"mean").get<
double>()));
527 "network_reader: a Replayer carries neither a readable trace file, nor the APH fit "
528 "the writers add beside it, nor a mean; there is nothing to reconstruct");
533 if (type ==
"MMPP2") {
534 const json& p = obj.at(
"params");
535 const double l0 = p.at(
"lambda0").get<
double>(), l1 = p.at(
"lambda1").get<
double>();
536 const double s0 = p.at(
"sigma0").get<
double>(), s1 = p.at(
"sigma1").get<
double>();
537 Matrix<T> D0(2, 2), D1(2, 2);
538 D1(0, 0) = num_traits<T>::from_double(l0);
539 D1(1, 1) = num_traits<T>::from_double(l1);
540 D0(0, 0) = num_traits<T>::from_double(-(l0 + s0));
541 D0(0, 1) = num_traits<T>::from_double(s0);
542 D0(1, 0) = num_traits<T>::from_double(s1);
543 D0(1, 1) = num_traits<T>::from_double(-(l1 + s1));
546 if (type ==
"MAP" || type ==
"MMPP") {
547 if (!obj.contains(
"map"))
548 throw InputError(
"network_reader: " + type +
" carries no 'map' (D0, D1) object");
549 const json& mp = obj.at(
"map");
551 mat_from_json<T>(mp.at(
"D1")),
557 if (type ==
"MMAP" || type ==
"MarkedMAP") {
558 if (!obj.contains(
"mmap"))
559 throw InputError(
"network_reader: " + type +
" carries no 'mmap' (D0, D1k) object");
560 const json& mp = obj.at(
"mmap");
562 mat_list_from_json<T>(mp.at(
"D1k")));
567 if (type ==
"BMAP" || type ==
"MarkedMMPP") {
568 const json& p = obj.at(
"params");
569 const std::vector<Matrix<T> > D = mat_list_from_json<T>(p.at(
"D"));
572 throw InputError(
"network_reader: a MarkedMMPP carries D0 and at least one marked block");
575 if (type ==
"DMAP") {
576 const json& p = obj.at(
"params");
580 const json& p = obj.at(
"params");
585 if (type ==
"ME" || type ==
"CME") {
586 const json& p = obj.at(
"params");
587 const std::vector<double> a = p.at(
"alpha").get<std::vector<double> >();
588 std::vector<T> alpha;
589 for (
double v : a) alpha.push_back(num_traits<T>::from_double(v));
601 if (obj.contains(
"ph")) {
602 const json& ph = obj.at(
"ph");
603 const std::vector<double> a = ph.at(
"alpha").get<std::vector<double> >();
604 const std::vector<std::vector<double> > rows =
605 ph.at(
"T").get<std::vector<std::vector<double> > >();
606 if (a.empty() || rows.size() != a.size())
607 throw InputError(
"network_reader: distribution '" + type +
608 "' has a 'ph' block whose alpha and T disagree in order");
609 std::vector<T> alpha;
610 alpha.reserve(a.size());
611 for (
double v : a) alpha.push_back(num_traits<T>::from_double(v));
612 Matrix<T> A(rows.size(), rows.empty() ? 0 : rows[0].size());
613 for (std::size_t i = 0; i < rows.size(); ++i)
614 for (std::size_t j = 0; j < rows[i].size(); ++j)
615 A(i, j) = num_traits<T>::from_double(rows[i][j]);
623 if (!obj.contains(
"params") && obj.contains(
"fit"))
624 return dist_from_fit<T>(type, obj.at(
"fit"));
625 if (!obj.contains(
"params"))
626 throw InputError(
"network_reader: distribution '" + type +
627 "' has neither params nor a fit block");
628 const json& p = obj.at(
"params");
630 const double lam = p.contains(
"lambda") ? p.at(
"lambda").get<
double>()
631 : p.at(
"rate").
get<double>();
634 if (type ==
"Det")
return lang::Distrib<T>::det(num_traits<T>::from_double(p.at(
"value").get<
double>()));
635 if (type ==
"Erlang") {
636 const double lam = p.at(
"lambda").get<
double>();
637 const long k = p.at(
"k").get<
long>();
640 if (type ==
"HyperExp") {
641 const std::vector<T> pv = num_vec_from_json<T>(p.at(
"p"));
642 const std::vector<T> lv = num_vec_from_json<T>(p.at(
"lambda"));
645 if (pv.size() == 2 && lv.size() == 2)
649 if (type ==
"Coxian")
651 num_vec_from_json<T>(p.at(
"phi")));
654 num_traits<T>::from_double(p.at(
"mu2").get<
double>()),
655 num_traits<T>::from_double(p.at(
"phi1").get<
double>()));
656 if (type ==
"Uniform")
658 num_traits<T>::from_double(p.at(
"b").get<
double>()));
662 num_traits<T>::from_double(p.at(
"beta").get<
double>()));
663 if (type ==
"Lognormal")
665 num_traits<T>::from_double(p.at(
"sigma").get<
double>()));
671 if (type ==
"Normal")
673 num_traits<T>::from_double(p.at(
"sigma").get<
double>()));
677 if (type ==
"Pareto")
679 num_traits<T>::from_double(p.at(
"scale").get<
double>()));
680 if (type ==
"Weibull")
682 num_traits<T>::from_double(p.at(
"beta").get<
double>()));
683 if (type ==
"DiscreteUniform")
685 num_traits<T>::from_double(p.at(
"min").get<
double>()),
686 num_traits<T>::from_double(p.at(
"max").get<
double>()));
687 if (type ==
"Bernoulli")
689 if (type ==
"Binomial")
691 num_traits<T>::from_double(p.at(
"p").get<
double>()));
692 if (type ==
"Poisson")
694 if (type ==
"Geometric")
698 std::size_t(p.at(
"n").get<
long>()));
699 if (type ==
"DiscreteSampler") {
700 const std::vector<T> pv = num_vec_from_json<T>(p.at(
"p"));
701 const std::vector<T> xv =
702 p.contains(
"x") ? num_vec_from_json<T>(p.at(
"x")) : std::vector<T>();
705 if (type ==
"EmpiricalCDF" || type ==
"EmpiricalCdf")
707 num_vec_from_json<T>(p.at(
"F")));
718 if (p.contains(
"mean") && !p.contains(
"scv"))
720 if (p.contains(
"mean") && p.contains(
"scv")) {
721 const double mean = p.at(
"mean").get<
double>();
722 const double scv = p.at(
"scv").get<
double>();
723 if (std::fabs(scv - 1.0) < 1e-12)
727 num_traits<T>::from_double(scv));
728 return hyperexp_fit_mean_scv<T>(mean, scv);
730 throw UnsupportedError(
"network_reader: unsupported distribution family '" + type +
752typename qn::CacheParam<T>::Popularity popularity_kind_from_json(
const json& obj,
753 std::size_t nitems) {
754 typename qn::CacheParam<T>::Popularity k;
755 const std::string type = obj.value(
"type", std::string());
756 const json& p = obj.contains(
"params") ? obj.at(
"params") : obj;
757 if (type ==
"Zipf") {
759 k.s = p.at(
"s").get<
double>();
760 k.n = p.contains(
"n") ? std::size_t(p.at(
"n").get<
long>()) : nitems;
761 }
else if (type ==
"DiscreteSampler" || p.contains(
"p")) {
763 k.n = p.contains(
"p") ? p.at(
"p").size() : nitems;
769std::vector<T> pmf_from_json(
const json& obj, std::size_t nitems) {
770 const std::string type = obj.value(
"type", std::string());
771 const json& p = obj.contains(
"params") ? obj.at(
"params") : obj;
777 if (type ==
"DiscreteSampler" || (p.contains(
"p") && p.at(
"p").is_array())) {
778 for (
const json& v : p.at(
"p")) out.push_back(num_traits<T>::from_double(v.get<
double>()));
781 if (type ==
"Zipf") {
782 const double s = p.at(
"s").get<
double>();
783 const std::size_t n = p.contains(
"n") ? std::size_t(p.at(
"n").get<
long>()) : nitems;
785 for (std::size_t k = 1; k <= n; ++k) h += std::pow(
double(k), -s);
786 for (std::size_t k = 1; k <= n; ++k)
787 out.push_back(num_traits<T>::from_double(std::pow(
double(k), -s) / h));
791 "network_reader: a cache popularity is written as '" + type +
792 "', and the discrete families carrying an item pmf are DiscreteSampler and Zipf");
805constexpr std::size_t REMOVAL_PMF_MAX_TERMS = 4096;
827std::vector<T> removal_pmf_from_json(
const json& obj) {
828 const std::string type = obj.value(
"type", std::string());
829 const json& p = obj.contains(
"params") ? obj.at(
"params") : obj;
831 auto put = [&out](std::size_t k,
double v) {
832 if (out.size() <= k) out.resize(k + 1, num_traits<T>::from_int(0));
833 out[k] = num_traits<T>::from_double(num_traits<T>::to_double(out[k]) + v);
835 if (type ==
"DiscreteSampler" || (p.contains(
"p") && p.at(
"p").is_array())) {
839 const json& pv = p.at(
"p");
840 const bool has_x = p.contains(
"x") && p.at(
"x").is_array();
841 for (std::size_t i = 0; i < pv.size(); ++i) {
843 has_x ? p.at(
"x").at(i).get<
double>() : static_cast<double>(i + 1);
844 if (xi < 0)
throw InputError(
"network_reader: a batch size cannot be negative");
845 put(
static_cast<std::size_t
>(xi + 0.5), pv.at(i).get<
double>());
849 if (type ==
"Bernoulli") {
850 const double q = p.at(
"p").get<
double>();
855 if (type ==
"Binomial") {
856 const double q = p.at(
"p").get<
double>();
857 const std::size_t n =
static_cast<std::size_t
>(p.at(
"n").get<double>() + 0.5);
858 double term = std::pow(1.0 - q,
static_cast<double>(n));
859 for (std::size_t k = 0; k <= n; ++k) {
861 if (k < n && q < 1.0)
862 term *= (
static_cast<double>(n - k) /
static_cast<double>(k + 1)) * q / (1.0 - q);
866 if (type ==
"Poisson") {
867 const double lam = p.at(
"lambda").get<
double>();
868 double term = std::exp(-lam), acc = 0.0;
869 for (std::size_t k = 0; k < REMOVAL_PMF_MAX_TERMS; ++k) {
872 if (acc > 1.0 - 1e-15)
break;
873 term *= lam /
static_cast<double>(k + 1);
877 if (type ==
"Geometric") {
880 const double q = p.at(
"p").get<
double>();
881 if (q <= 0.0 || q > 1.0)
throw InputError(
"network_reader: Geometric(p) needs 0 < p <= 1");
883 double term = q, acc = 0.0;
884 for (std::size_t k = 1; k <= REMOVAL_PMF_MAX_TERMS; ++k) {
887 if (acc > 1.0 - 1e-15)
break;
892 if (type ==
"DiscreteUniform") {
893 const long lo =
static_cast<long>(p.at(
"min").get<double>());
894 const long hi =
static_cast<long>(p.at(
"max").get<double>());
895 if (hi < lo || lo < 0)
896 throw InputError(
"network_reader: DiscreteUniform(min,max) needs 0 <= min <= max");
897 const double w = 1.0 /
static_cast<double>(hi - lo + 1);
898 for (
long k = lo; k <= hi; ++k) put(static_cast<std::size_t>(k), w);
902 const double v = p.contains(
"t") ? p.at(
"t").get<
double>() : p.at(
"mean").
get<double>();
903 if (v < 0)
throw InputError(
"network_reader: a batch size cannot be negative");
904 put(
static_cast<std::size_t
>(v + 0.5), 1.0);
908 "network_reader: a signal removal law is written as '" + type +
909 "', and the discrete families a batch size can be drawn from are DiscreteSampler, "
910 "Bernoulli, Binomial, Poisson, Geometric, DiscreteUniform and Det");
927lang::CdScaling<T> cd_scaling_from_json(
const json& tbl,
const std::vector<int>& cutoffs,
929 std::map<std::string, std::vector<T> > table;
930 for (
auto it = tbl.begin(); it != tbl.end(); ++it)
931 table[it.key()] = num_vec_from_json<T>(it.value());
932 const std::vector<int> cut = cutoffs;
933 return [table, cut, K](
const std::vector<T>& n) {
935 for (std::size_t r = 0; r < K; ++r) {
937 ?
static_cast<int>(std::lround(num_traits<T>::to_double(n[r])))
940 if (r < cut.size() && v > cut[r]) v = cut[r];
942 key += std::to_string(v);
944 std::vector<T> out(K, num_traits<T>::from_int(1));
945 typename std::map<std::string, std::vector<T> >::const_iterator it = table.find(key);
946 if (it == table.end())
return out;
947 for (std::size_t r = 0; r < K && r < it->second.size(); ++r) out[r] = it->second[r];
967std::function<T(
const std::vector<std::size_t>&)> oi_rate_from_json(
const json& tbl,
968 const std::vector<int>& cutoffs,
970 std::map<std::string, T> table;
971 for (
auto it = tbl.begin(); it != tbl.end(); ++it)
972 table[it.key()] = num_traits<T>::from_double(it.value().get<
double>());
973 const std::vector<int> cut = cutoffs;
974 return [table, cut, K](
const std::vector<std::size_t>& micro) {
975 std::vector<int> cnt(K, 0);
976 for (std::size_t j = 0; j < micro.size(); ++j)
977 if (micro[j] >= 1 && micro[j] <= K) ++cnt[micro[j] - 1];
979 for (std::size_t r = 0; r < K; ++r) {
981 if (r < cut.size() && v > cut[r]) v = cut[r];
983 key += std::to_string(v);
985 typename std::map<std::string, T>::const_iterator it = table.find(key);
986 return it == table.end() ? num_traits<T>::from_int(0) : it->second;
1002std::vector<T> cd_peak_from_json(
const json& blk,
const json& tbl) {
1003 if (blk.contains(
"peak") && !blk.at(
"peak").
empty())
1004 return num_vec_from_json<T>(blk.at(
"peak"));
1006 for (
auto it = tbl.begin(); it != tbl.end(); ++it) {
1007 if (it.key().find_first_not_of(
"0,") == std::string::npos)
continue;
1008 const std::vector<double> row = num_vec_from_json<double>(it.value());
1009 for (
double x : row)
1010 if (std::isfinite(x) && x > bmax) bmax = x;
1012 return std::vector<T>(1, num_traits<T>::from_double(bmax));
1023inline void reject_unconsumed_model_keys(
const json& model) {
1024 static const char* kModelKeys[] = {
"name",
"type",
"nodes",
1025 "classes",
"routing",
"format",
1026 "version",
"rewards",
"finiteCapacityRegions",
1027 "routingStrategies",
"routingWeights",
"routingParams",
1028 "logPath",
"globalDependence",
"stateDepRouting"};
1035 static const char* kNodeKeys[] = {
1036 "name",
"type",
"scheduling",
"servers",
"service",
1037 "buffer",
"capacity",
"dropRule",
"schedParams",
"pollingType",
1038 "pollingPar",
"classSwitchMatrix",
"csMatrix",
"forkNode",
1039 "tasksPerLink",
"fanOutByDest",
"fanOutDist",
"fanOutProb",
1040 "items",
"numItems",
"itemLevelCap",
"popularity",
"replacementStrategy",
1041 "itemSizes",
"costCaps",
"accessProb",
"admissionProb",
"accessGraph",
1043 "cache",
"initialState",
1044 "retrievalSystem",
"queues",
"hitClass",
"missClass",
1045 "immediateFeedback",
"loadDependence",
1046 "classDependence",
"jointDependence",
1047 "modes",
"classCap",
"departureDiscipline",
1048 "oiServiceRate",
"oiCutoffs",
"swapGraph",
1049 "arrivalBatch",
"markedClasses",
1050 "stateSpace",
"statePrior",
"joinStrategy",
"joinQuorum",
1051 "setupTime",
"delayOffTime",
"switchoverTimes",
"breakdown",
1052 "serverTypes",
"heteroSchedPolicy",
"serverParallelism",
1053 "balking",
"retrial",
"patience",
"orbitImpatience",
1056 "fileName",
"filePath",
"startTime",
"loggerName",
"timestamp",
1057 "jobID",
"jobClass",
"timeSameClass",
"timeAnyClass"};
1062 static const char* kClassKeys[] = {
1063 "name",
"type",
"population",
"refNode",
"priority",
1064 "openOrClosed",
"signalType",
"targetClass",
"removalPolicy",
"removalDistribution",
1065 "isReferenceClass",
"deadline",
"patience",
"impatienceType",
1066 "spawnClass",
"replySignalClass",
"immediateFeedback"};
1067 auto known = [](
const char*
const* tab, std::size_t n,
const std::string& k) {
1068 for (std::size_t i = 0; i < n; ++i)
1069 if (k == tab[i])
return true;
1072 const std::string why =
1073 "', which this reader does not implement. Refusing rather than dropping it: a "
1074 "constraint silently discarded here would make every solver return a confident "
1075 "answer for a different model";
1076 for (
auto it = model.begin(); it != model.end(); ++it)
1077 if (!known(kModelKeys,
sizeof(kModelKeys) /
sizeof(*kModelKeys), it.key()))
1078 throw UnsupportedError(
"network_reader: the model carries '" + it.key() + why);
1079 if (model.contains(
"classes"))
1080 for (
const json& cl : model.at(
"classes"))
1081 for (
auto it = cl.begin(); it != cl.end(); ++it)
1082 if (!known(kClassKeys,
sizeof(kClassKeys) /
sizeof(*kClassKeys), it.key()))
1084 cl.value(
"name", std::string(
"?")) +
"' carries '" +
1086 if (!model.contains(
"nodes"))
return;
1087 for (
const json& nd : model.at(
"nodes"))
1088 for (
auto it = nd.begin(); it != nd.end(); ++it)
1089 if (!known(kNodeKeys,
sizeof(kNodeKeys) /
sizeof(*kNodeKeys), it.key()))
1091 nd.value(
"name", std::string(
"?")) +
"' carries '" +
1108 const json& model = root.contains(
"model") ? root.at(
"model") : root;
1109 const std::string mtype = model.value(
"type", std::string(
"Network"));
1110 if (mtype !=
"Network") {
1114 if (mtype ==
"Environment")
1116 "network_reader: this is an Environment model (a network per stage plus the "
1117 "stage transitions); solve it with -s env, which reads it through "
1118 "environment_reader.h");
1120 "' is not a Network; only Network models are solved by this path");
1133 detail::reject_unconsumed_model_keys(model);
1136 net.
set_log_path(model.value(
"logPath", std::string()));
1144 const json& nodes = model.at(
"nodes");
1145 std::map<std::string, std::size_t> node_idx;
1146 std::vector<std::string> node_type(nodes.size());
1147 for (std::size_t i = 0; i < nodes.size(); ++i)
1148 node_type[i] = nodes[i].at(
"type").get<std::string>();
1151 for (std::size_t i = 0; i < nodes.size(); ++i) {
1152 const json& nd = nodes[i];
1153 const std::string& type = node_type[i];
1159 if (type ==
"ClassSwitch" || type ==
"Cache" || type ==
"Transition")
continue;
1160 const std::string name = nd.at(
"name").get<std::string>();
1161 std::size_t idx = 0;
1162 if (type ==
"Source") {
1164 }
else if (type ==
"Sink") {
1166 }
else if (type ==
"Delay") {
1168 }
else if (type ==
"Queue") {
1170 detail::sched_from_json(nd.value(
"scheduling", std::string(
"FCFS"))));
1171 }
else if (type ==
"Router") {
1173 }
else if (type ==
"Logger" || type ==
"LogTunnel") {
1174 idx = net.
add_logger(name, nd.value(
"fileName", std::string()));
1176 if (nd.contains(
"filePath")) lg.
file_path = nd.at(
"filePath").get<std::string>();
1184 }
else if (type ==
"Place") {
1186 }
else if (type ==
"Join") {
1188 }
else if (type ==
"Fork") {
1189 idx = net.
add_fork(name, detail::num_value(nd,
"tasksPerLink", 1.0));
1191 throw UnsupportedError(
"network_reader: unsupported node type '" + type +
"' at node '" +
1194 node_idx[name] = idx;
1198 const json& classes = model.at(
"classes");
1199 std::map<std::string, std::size_t> class_idx;
1202 std::vector<std::size_t> signal_classes;
1203 for (std::size_t r = 0; r < classes.size(); ++r) {
1204 const json& cl = classes[r];
1205 const std::string name = cl.at(
"name").get<std::string>();
1206 const std::string type = cl.at(
"type").get<std::string>();
1207 std::size_t idx = 0;
1208 if (type ==
"Open") {
1210 }
else if (type ==
"Closed" || type ==
"SelfLooping") {
1215 const double pop = cl.at(
"population").get<
double>();
1216 const std::string ref = cl.at(
"refNode").get<std::string>();
1217 auto it = node_idx.find(ref);
1218 if (it == node_idx.end())
1219 throw InputError(
"network_reader: class '" + name +
"' references unknown node '" +
1221 idx = type ==
"SelfLooping"
1224 }
else if (type ==
"Signal") {
1230 const std::string kind = cl.value(
"openOrClosed", std::string(
"Open"));
1231 if (kind ==
"Closed") {
1232 const std::string ref = cl.at(
"refNode").get<std::string>();
1233 auto it = node_idx.find(ref);
1234 if (it == node_idx.end())
1235 throw InputError(
"network_reader: signal class '" + name +
1236 "' references unknown node '" + ref +
"'");
1243 cl.value(
"priority", 0));
1247 signal_classes.push_back(r);
1250 "' for class '" + name +
"'");
1261 if (cl.contains(
"deadline")) {
1262 const double due = cl.at(
"deadline").get<
double>();
1267 if (cl.contains(
"patience")) {
1268 const json& pt = cl.at(
"patience");
1273 cl.contains(
"impatienceType")
1274 ? detail::impatience_from_json(cl.at(
"impatienceType").get<std::string>())
1277 class_idx[name] = idx;
1281 for (std::size_t r = 0; r < classes.size(); ++r) {
1282 const json& cl = classes[r];
1283 const std::size_t idx = class_idx.at(cl.at(
"name").get<std::string>());
1284 if (cl.contains(
"spawnClass")) {
1285 const std::string sp = cl.at(
"spawnClass").get<std::string>();
1286 auto sit = class_idx.find(sp);
1287 if (sit == class_idx.end())
1288 throw InputError(
"network_reader: class '" + cl.at(
"name").get<std::string>() +
1289 "' spawns class '" + sp +
"', which the model does not declare");
1294 if (cl.contains(
"replySignalClass")) {
1295 const std::string rp = cl.at(
"replySignalClass").get<std::string>();
1296 auto rit = class_idx.find(rp);
1297 if (rit == class_idx.end())
1298 throw InputError(
"network_reader: class '" + cl.at(
"name").get<std::string>() +
1299 "' replies with class '" + rp +
1300 "', which the model does not declare");
1305 for (std::size_t si = 0; si < signal_classes.size(); ++si) {
1306 const json& cl = classes[signal_classes[si]];
1307 const std::string name = cl.at(
"name").get<std::string>();
1308 const std::string st = cl.value(
"signalType", std::string(
"negative"));
1312 else if (st !=
"negative" && st !=
"NEGATIVE")
1313 throw UnsupportedError(
"network_reader: signal class '" + name +
"' is of type '" + st +
1314 "', and the kinds on the wire are negative, catastrophe and "
1317 const std::string rp = cl.value(
"removalPolicy", std::string(
"RANDOM"));
1320 std::size_t target = 0;
1321 if (cl.contains(
"targetClass")) {
1322 auto tit = class_idx.find(cl.at(
"targetClass").get<std::string>());
1323 if (tit == class_idx.end())
1324 throw InputError(
"network_reader: signal class '" + name +
"' targets class '" +
1325 cl.at(
"targetClass").get<std::string>() +
1326 "', which the model does not declare");
1327 target = tit->second;
1329 std::vector<T> remdist;
1330 if (cl.contains(
"removalDistribution"))
1331 remdist = detail::removal_pmf_from_json<T>(cl.at(
"removalDistribution"));
1332 net.
set_signal(class_idx.at(name), kind, pol, target, remdist);
1334 const std::size_t K = class_idx.size();
1338 for (std::size_t i = 0; i < nodes.size(); ++i) {
1339 const json& nd = nodes[i];
1340 const std::string& type = node_type[i];
1341 if (type !=
"ClassSwitch" && type !=
"Join" && type !=
"Cache" && type !=
"Fork")
continue;
1342 const std::string name = nd.at(
"name").get<std::string>();
1343 std::size_t idx = 0;
1344 if (type ==
"Fork") {
1350 if (!nd.contains(
"fanOutByDest") && !nd.contains(
"fanOutDist") &&
1351 !nd.contains(
"fanOutProb"))
1353 idx = node_idx.at(name);
1354 if (nd.contains(
"fanOutByDest")) {
1355 const json& ovs = nd.at(
"fanOutByDest");
1356 for (std::size_t e = 0; e < ovs.size(); ++e)
1358 ovs[e].at(
"value").get<
double>(),
1359 detail::fork_dest_index(ovs[e], node_idx, name));
1361 if (nd.contains(
"fanOutDist")) {
1362 const json& ovs = nd.at(
"fanOutDist");
1363 for (std::size_t e = 0; e < ovs.size(); ++e) {
1364 const std::vector<double> pv = ovs[e].at(
"p").get<std::vector<double> >();
1365 const std::vector<double> xv = ovs[e].at(
"x").get<std::vector<double> >();
1366 std::vector<T> p, x;
1367 for (std::size_t q = 0; q < pv.size(); ++q)
1369 for (std::size_t q = 0; q < xv.size(); ++q)
1373 detail::fork_dest_index(ovs[e], node_idx, name));
1376 if (nd.contains(
"fanOutProb")) {
1377 const json& ovs = nd.at(
"fanOutProb");
1378 for (std::size_t e = 0; e < ovs.size(); ++e)
1380 idx, ovs[e].at(
"class").get<std::size_t>(),
1381 detail::fork_dest_index(ovs[e], node_idx, name),
1382 ovs[e].at(
"value").get<
double>());
1386 if (type ==
"Cache") {
1394 const json empty_obj = json::object();
1395 const json& cj = nd.contains(
"cache") ? nd.at(
"cache") : empty_obj;
1397 cp.
nitems = detail::has_cache_key(nd, cj,
"numItems")
1398 ? detail::cache_key(nd, cj,
"numItems").get<std::size_t>()
1399 : cj.at(
"items").get<std::size_t>();
1400 cp.
itemcap = detail::has_cache_key(nd, cj,
"itemLevelCap")
1401 ? detail::cache_key(nd, cj,
"itemLevelCap").get<std::vector<int> >()
1402 : cj.at(
"capacity").get<std::vector<int> >();
1405 if (detail::has_cache_key(nd, cj,
"itemSizes"))
1406 cp.
itemsize = detail::cache_key(nd, cj,
"itemSizes").get<std::vector<int> >();
1407 if (detail::has_cache_key(nd, cj,
"costCaps")) {
1408 const json& cc = detail::cache_key(nd, cj,
"costCaps");
1409 if (cc.is_array()) {
1410 cp.
costcap = cc.get<std::vector<int> >();
1417 detail::has_cache_key(nd, cj,
"replacementStrategy")
1418 ? detail::cache_key(nd, cj,
"replacementStrategy").get<std::string>()
1419 : cj.value(
"replacement", std::string(
"RR")));
1420 cp.
pread.assign(K, std::vector<T>());
1425 if (detail::has_cache_key(nd, cj,
"popularity")) {
1426 const json& pop = detail::cache_key(nd, cj,
"popularity");
1427 for (
auto it = pop.begin(); it != pop.end(); ++it) {
1431 if (it.value().value(
"type", std::string()) ==
"Disabled")
continue;
1432 cp.
pread[class_idx.at(it.key()) - 1] =
1433 detail::pmf_from_json<T>(it.value(), cp.
nitems);
1434 cp.
preadkind[class_idx.at(it.key()) - 1] =
1435 detail::popularity_kind_from_json<T>(it.value(), cp.
nitems);
1438 auto fill_switch = [&](
const char* key, std::vector<std::size_t>& dst) {
1439 if (!detail::has_cache_key(nd, cj, key))
return;
1440 const json& blk = detail::cache_key(nd, cj, key);
1441 for (
auto it = blk.begin(); it != blk.end(); ++it)
1442 dst[class_idx.at(it.key()) - 1] = class_idx.at(it.value().get<std::string>());
1444 fill_switch(
"hitClass", cp.
hitclass);
1449 if (detail::has_cache_key(nd, cj,
"itemClass")) {
1450 const json& blk = detail::cache_key(nd, cj,
"itemClass");
1451 for (
auto it = blk.begin(); it != blk.end(); ++it)
1452 cp.
classitem[class_idx.at(it.key()) - 1] =
1453 static_cast<std::size_t
>(it.value().get<double>());
1464 if (detail::has_cache_key(nd, cj,
"admissionProb"))
1466 if (detail::has_cache_key(nd, cj,
"accessProb")) {
1467 const json& ap = detail::cache_key(nd, cj,
"accessProb");
1469 for (
const json& per_class : ap) {
1470 std::vector<Matrix<T> > row;
1471 for (
const json& g : per_class) {
1472 if (g.is_null() || g.empty()) {
1476 row.push_back(detail::mat_from_json<T>(g));
1478 cp.
accost.push_back(row);
1480 }
else if (detail::has_cache_key(nd, cj,
"accessGraph")) {
1486 const std::vector<Matrix<T> > shared =
1487 detail::mat_list_from_json<T>(detail::cache_key(nd, cj,
"accessGraph"));
1488 cp.
accost.assign(K, shared);
1492 if (detail::has_cache_key(nd, cj,
"initialState"))
1493 cp.
initstate = detail::num_vec_from_json<T>(detail::cache_key(nd, cj,
"initialState"));
1503 if (detail::has_cache_key(nd, cj,
"retrievalSystem")) {
1504 const json& rs = detail::cache_key(nd, cj,
"retrievalSystem");
1507 if (rs.contains(
"byClass")) {
1508 for (
auto rc = rs.at(
"byClass").begin(); rc != rs.at(
"byClass").end(); ++rc) {
1509 const std::size_t rdcls = class_idx.at(rc.key());
1510 std::vector<std::size_t> qnodes;
1511 for (
const auto&
qn : rc.value().at(
"queues"))
1512 qnodes.push_back(node_idx.at(
qn.get<std::string>()));
1514 for (
auto it2 = rc.value().at(
"items").begin();
1515 it2 != rc.value().at(
"items").end(); ++it2) {
1516 const std::size_t item = std::stoul(it2.key());
1519 class_idx.at(it2.value().get<std::string>());
1525 node_idx[name] = idx;
1528 if (type ==
"Join") {
1529 std::size_t fork = 0;
1530 if (nd.contains(
"forkNode")) {
1531 auto it = node_idx.find(nd.at(
"forkNode").get<std::string>());
1532 if (it == node_idx.end())
1533 throw InputError(
"network_reader: Join '" + name +
1534 "' references unknown fork '" +
1535 nd.at(
"forkNode").get<std::string>() +
"'");
1540 idx = node_idx.at(name);
1545 if (nd.contains(
"joinStrategy") || nd.contains(
"joinQuorum")) {
1546 const std::string js = nd.value(
"joinStrategy", std::string(
"STD"));
1547 if (js !=
"STD" && js !=
"PARTIAL")
1548 throw UnsupportedError(
"network_reader: Join '" + name +
"' declares strategy '" +
1549 js +
"', and the rules on the wire are STD and PARTIAL");
1553 nd.value(
"joinQuorum", 0.0));
1562 if (nd.contains(
"classSwitchMatrix") || nd.contains(
"csMatrix")) {
1564 nd.contains(
"classSwitchMatrix") ? nd.at(
"classSwitchMatrix") : nd.at(
"csMatrix");
1565 for (
auto ri = csm.begin(); ri != csm.end(); ++ri) {
1566 const std::size_t rr = class_idx.at(ri.key()) - 1;
1568 for (
auto ci = ri.value().begin(); ci != ri.value().end(); ++ci)
1569 C(rr, class_idx.at(ci.key()) - 1) =
1575 node_idx[name] = idx;
1587 for (std::size_t i = 0; i < nodes.size(); ++i) {
1588 if (node_type[i] !=
"Transition")
continue;
1589 const json& nd = nodes[i];
1590 const std::string name = nd.at(
"name").get<std::string>();
1591 if (!nd.contains(
"modes") || nd.at(
"modes").empty())
1592 throw InputError(
"network_reader: transition '" + name +
"' declares no mode");
1593 const json& modes = nd.at(
"modes");
1595 tp.
nmodes = modes.size();
1596 const std::size_t nn = nodes.size();
1597 const double inf = std::numeric_limits<double>::infinity();
1598 for (std::size_t m = 0; m < modes.size(); ++m) {
1599 const json& mj = modes[m];
1600 tp.
modenames.push_back(mj.value(
"name", std::string(
"Mode") + std::to_string(m + 1)));
1609 tp.
firingdep.push_back(std::function<T(
const std::vector<T>&)>());
1610 if (mj.contains(
"firingRateDependence")) {
1611 const json& frm = mj.at(
"firingRateDependence");
1612 if (!frm.contains(
"slots") || !frm.contains(
"scaling"))
1613 throw InputError(
"network_reader: the marking-dependent firing rate of mode " +
1614 std::to_string(m + 1) +
" of transition '" + name +
1615 "' carries no 'slots'/'scaling' lattice");
1616 std::vector<std::size_t> slot_node;
1617 for (
const json& sm : frm.at(
"slots")) {
1618 const std::string on = sm.at(
"node").get<std::string>();
1619 const std::map<std::string, std::size_t>::const_iterator ni = node_idx.find(on);
1620 if (ni == node_idx.end())
1621 throw InputError(
"network_reader: the marking-dependent firing rate of "
1623 name +
"' reads node '" + on +
1624 "', which the model does not declare");
1625 slot_node.push_back(ni->second - 1);
1627 std::vector<long> cutoffs;
1628 if (frm.contains(
"cutoffs"))
1629 for (
const json& c : frm.at(
"cutoffs")) cutoffs.push_back(c.get<
long>());
1630 std::map<std::string, double> table;
1631 const json& sc = frm.at(
"scaling");
1632 for (json::const_iterator it = sc.begin(); it != sc.end(); ++it)
1633 table[it.key()] = it.value().get<
double>();
1634 tp.
firingdep.back() = [slot_node, cutoffs,
1635 table](
const std::vector<T>& mk) -> T {
1637 for (std::size_t s = 0; s < slot_node.size(); ++s) {
1638 long c = slot_node[s] < mk.size()
1639 ?
static_cast<long>(std::llround(
1643 if (s < cutoffs.size() && c > cutoffs[s]) c = cutoffs[s];
1645 key += std::to_string(c);
1647 const std::map<std::string, double>::const_iterator hit = table.find(key);
1654 const bool immediate =
1655 mj.value(
"timingStrategy", std::string(
"TIMED")) ==
"IMMEDIATE";
1659 if (mj.contains(
"distribution")) proc = detail::dist_from_json<T>(mj.at(
"distribution"));
1663 tp.
nmodeservers.push_back(detail::num_value(mj,
"numServers", 1.0));
1671 tp.
firingprio.push_back(mj.value(
"firingPriority", 1.0));
1673 const std::size_t K = classes.size();
1677 const char* kArcKey[3] = {
"enablingConditions",
"inhibitingConditions",
1679 for (
int which = 0; which < 3; ++which) {
1680 if (!mj.contains(kArcKey[which]))
continue;
1681 for (
const json& arc : mj.at(kArcKey[which])) {
1682 const std::string on = arc.at(
"node").get<std::string>();
1683 const std::map<std::string, std::size_t>::const_iterator ni = node_idx.find(on);
1684 if (ni == node_idx.end())
1685 throw InputError(
"network_reader: transition '" + name +
"' names node '" +
1686 on +
"', which the model does not declare");
1692 if (arc.contains(
"class")) {
1693 const std::string cn = arc.at(
"class").get<std::string>();
1694 const std::map<std::string, std::size_t>::const_iterator ci =
1696 if (ci == class_idx.end())
1697 throw InputError(
"network_reader: transition '" + name +
1698 "' names class '" + cn +
1699 "', which the model does not declare");
1703 const std::size_t q = ni->second - 1;
1704 if (which == 0) enab(q, rr - 1) = enab(q, rr - 1) + cnt;
1705 else if (which == 1) inhib(q, rr - 1) = cnt;
1706 else fire(q, rr - 1) = fire(q, rr - 1) + cnt;
1711 tp.
firing.push_back(fire);
1717 for (std::size_t i = 0; i < nodes.size(); ++i) {
1718 const json& nd = nodes[i];
1719 const std::string& type = node_type[i];
1720 const std::size_t idx = node_idx.at(nd.at(
"name").get<std::string>());
1722 if (nd.contains(
"servers") && (type ==
"Queue"))
1725 if (nd.contains(
"service")) {
1726 const json& svc = nd.at(
"service");
1727 for (
auto it = svc.begin(); it != svc.end(); ++it) {
1728 auto cit = class_idx.find(it.key());
1729 if (cit == class_idx.end())
1730 throw InputError(
"network_reader: service names unknown class '" + it.key() +
1731 "' at node '" + nd.at(
"name").get<std::string>() +
"'");
1733 if (type ==
"Source") net.
set_arrival(idx, cit->second, d);
1738 if (type ==
"Queue" && nd.contains(
"scheduling") &&
1739 nd.at(
"scheduling").get<std::string>() ==
"POLLING") {
1740 if (nd.contains(
"pollingType"))
1741 net.
set_polling_type(idx, detail::polling_from_json(nd.at(
"pollingType").get<std::string>()),
1742 nd.value(
"pollingPar", 0));
1747 if (nd.contains(
"schedParams") && (type ==
"Queue" || type ==
"Delay")) {
1748 const json& sp = nd.at(
"schedParams");
1749 for (
auto it = sp.begin(); it != sp.end(); ++it) {
1750 auto cit = class_idx.find(it.key());
1751 if (cit != class_idx.end())
1763 const bool station_node =
1764 type ==
"Queue" || type ==
"Delay" || type ==
"Join" || type ==
"Place";
1765 if (nd.contains(
"dropRule") && station_node) {
1766 const json& dr = nd.at(
"dropRule");
1767 for (
auto it = dr.begin(); it != dr.end(); ++it) {
1768 auto cit = class_idx.find(it.key());
1769 if (cit != class_idx.end())
1771 detail::drop_from_json(it.value().get<std::string>()));
1774 if (nd.contains(
"buffer") && station_node)
1775 net.
set_capacity(idx, detail::num_from_json(nd.at(
"buffer")));
1777 if (nd.contains(
"classCap")) {
1778 const json& cc = nd.at(
"classCap");
1779 for (
auto it = cc.begin(); it != cc.end(); ++it) {
1780 auto cit = class_idx.find(it.key());
1781 if (cit != class_idx.end())
1793 if (nd.contains(
"patience")) {
1794 const json& pt = nd.at(
"patience");
1795 for (
auto it = pt.begin(); it != pt.end(); ++it) {
1796 auto cit = class_idx.find(it.key());
1797 if (cit == class_idx.end())
continue;
1798 const json& pj = it.value();
1800 pj.contains(
"impatienceType")
1801 ? detail::impatience_from_json(pj.at(
"impatienceType").get<std::string>())
1803 net.
set_patience(idx, cit->second, detail::dist_from_json<T>(pj.at(
"distribution")),
1807 if (nd.contains(
"orbitImpatience")) {
1808 const json& oi = nd.at(
"orbitImpatience");
1809 for (
auto it = oi.begin(); it != oi.end(); ++it) {
1810 auto cit = class_idx.find(it.key());
1811 if (cit != class_idx.end())
1815 if (nd.contains(
"batchRejectProb")) {
1816 const json& br = nd.at(
"batchRejectProb");
1817 for (
auto it = br.begin(); it != br.end(); ++it) {
1818 auto cit = class_idx.find(it.key());
1819 if (cit != class_idx.end())
1824 if (nd.contains(
"balking")) {
1825 const json& bk = nd.at(
"balking");
1826 for (
auto it = bk.begin(); it != bk.end(); ++it) {
1827 auto cit = class_idx.find(it.key());
1828 if (cit == class_idx.end())
continue;
1829 const json& bj = it.value();
1830 std::vector<typename qn::Station<T>::BalkingThreshold> ths;
1831 if (bj.contains(
"thresholds"))
1832 for (
const json& tj : bj.at(
"thresholds")) {
1834 th.
min_jobs = tj.value(
"minJobs", 0.0);
1838 th.
max_jobs = tj.value(
"maxJobs", -1.0);
1843 detail::balking_from_json(bj.at(
"strategy").get<std::string>()), ths);
1846 if (nd.contains(
"retrial")) {
1847 const json& rt = nd.at(
"retrial");
1848 for (
auto it = rt.begin(); it != rt.end(); ++it) {
1849 auto cit = class_idx.find(it.key());
1850 if (cit == class_idx.end())
continue;
1851 const lang::Distrib<T> delay = detail::dist_from_json<T>(it.value().at(
"delay"));
1856 int(detail::num_value(it.value(),
"maxAttempts", 0.0)));
1861 if (nd.contains(
"setupTime")) {
1862 if (!nd.contains(
"delayOffTime"))
1864 "network_reader: node '" + nd.at(
"name").get<std::string>() +
1865 "' declares a setup time with no delay-off time; a server that never powers "
1866 "down never pays the setup, so the pair is meaningless alone");
1867 const json& su = nd.at(
"setupTime");
1868 const json& doff = nd.at(
"delayOffTime");
1869 for (
auto it = su.begin(); it != su.end(); ++it) {
1870 auto cit = class_idx.find(it.key());
1871 if (cit == class_idx.end() || !doff.contains(it.key()))
continue;
1873 detail::dist_from_json<T>(doff.at(it.key())));
1882 if (nd.contains(
"breakdown")) {
1883 const json& bd = nd.at(
"breakdown");
1884 if (!bd.contains(
"failure") || !bd.contains(
"repair"))
1886 "network_reader: node '" + nd.at(
"name").get<std::string>() +
1887 "' declares a breakdown without both a failure and a repair time; a server "
1888 "that never recovers is an absorbing model, not a breakdown");
1889 std::vector<lang::Distrib<T> > down(class_idx.size(),
1891 if (bd.contains(
"downService")) {
1892 const json& ds = bd.at(
"downService");
1893 for (
auto it = ds.begin(); it != ds.end(); ++it) {
1894 auto cit = class_idx.find(it.key());
1895 if (cit == class_idx.end())
continue;
1896 if (cit->second >= 1 && cit->second <= down.size())
1897 down[cit->second - 1] = detail::dist_from_json<T>(it.value());
1900 net.
set_breakdown(idx, detail::dist_from_json<T>(bd.at(
"failure")),
1901 detail::dist_from_json<T>(bd.at(
"repair")), down);
1903 if (nd.contains(
"switchoverTimes")) {
1914 const bool polling = type ==
"Queue" &&
1915 nd.value(
"scheduling", std::string()) ==
"POLLING";
1917 std::cerr <<
"[LINE] Warning: node '" << nd.at(
"name").get<std::string>()
1918 <<
"' declares switchover times but is not POLLING-scheduled; a "
1919 <<
"switchover is the walk between a polling server's buffers, so "
1920 <<
"the times are ignored." << std::endl;
1926 std::map<std::size_t, lang::Distrib<T> > walk;
1927 std::map<std::size_t, std::string> first_to;
1928 for (
const json& so : nd.at(
"switchoverTimes")) {
1929 auto cit = class_idx.find(so.at(
"from").get<std::string>());
1930 if (cit == class_idx.end())
continue;
1931 const std::string to = so.value(
"to", std::string());
1932 if (walk.count(cit->second) && first_to[cit->second] != to)
1934 "network_reader: node '" + nd.at(
"name").get<std::string>() +
1935 "' declares a switchover that depends on the class moved TO as well "
1936 "as the one moved FROM; this port carries one walk per departing "
1938 walk[cit->second] = detail::dist_from_json<T>(so.at(
"distribution"));
1939 first_to[cit->second] = to;
1941 for (
const auto& kv : walk) net.
set_switchover(idx, kv.first, kv.second);
1944 if (nd.contains(
"serverTypes")) {
1945 for (
const json& st : nd.at(
"serverTypes")) {
1947 stype.
name = st.value(
"name", std::string());
1948 stype.
count = st.value(
"count", 1.0);
1949 if (st.contains(
"compatibleClasses")) {
1950 stype.
compatible.assign(classes.size(),
false);
1951 for (
const json& cn : st.at(
"compatibleClasses")) {
1952 auto cit = class_idx.find(cn.get<std::string>());
1953 if (cit != class_idx.end()) stype.
compatible[cit->second - 1] =
true;
1956 if (st.contains(
"service")) {
1958 const json& sv = st.at(
"service");
1959 for (
auto it = sv.begin(); it != sv.end(); ++it) {
1960 auto cit = class_idx.find(it.key());
1961 if (cit != class_idx.end())
1962 stype.
service[cit->second - 1] = detail::dist_from_json<T>(it.value());
1967 if (nd.contains(
"heteroSchedPolicy"))
1969 idx, detail::hetero_from_json(nd.at(
"heteroSchedPolicy").get<std::string>()));
1973 if (nd.contains(
"serverParallelism")) {
1974 const json& sp = nd.at(
"serverParallelism");
1975 for (
auto it = sp.begin(); it != sp.end(); ++it) {
1976 auto cit = class_idx.find(it.key());
1977 if (cit != class_idx.end())
1983 if (nd.contains(
"arrivalBatch")) {
1984 const json& ab = nd.at(
"arrivalBatch");
1985 for (
auto it = ab.begin(); it != ab.end(); ++it) {
1986 auto cit = class_idx.find(it.key());
1987 if (cit != class_idx.end())
1991 if (nd.contains(
"markedClasses")) {
1992 std::vector<std::size_t> marks;
1993 for (
const json& cn : nd.at(
"markedClasses")) {
1994 auto cit = class_idx.find(cn.get<std::string>());
1995 if (cit == class_idx.end())
1996 throw InputError(
"network_reader: node '" + nd.at(
"name").get<std::string>() +
1997 "' binds mark " + std::to_string(marks.size() + 1) +
1998 " to class '" + cn.get<std::string>() +
1999 "', which the model does not declare");
2000 marks.push_back(cit->second);
2010 if (nd.contains(
"oiServiceRate")) {
2011 std::vector<int> cut(classes.size(), 10);
2012 if (nd.contains(
"oiCutoffs")) {
2013 const std::vector<double> cv = detail::num_vec_from_json<double>(nd.at(
"oiCutoffs"));
2014 for (std::size_t r = 0; r < cut.size() && r < cv.size(); ++r)
2015 cut[r] =
static_cast<int>(std::lround(cv[r]));
2017 std::vector<std::vector<bool> > swap;
2018 if (nd.contains(
"swapGraph")) {
2019 const Matrix<T> sg = detail::mat_from_json<T>(nd.at(
"swapGraph"));
2020 swap.assign(sg.
rows(), std::vector<bool>(sg.
cols(),
false));
2021 for (std::size_t a = 0; a < sg.
rows(); ++a)
2022 for (std::size_t b = 0; b < sg.
cols(); ++b)
2026 detail::oi_rate_from_json<T>(nd.at(
"oiServiceRate"), cut, classes.size()),
2031 if (nd.contains(
"initialState") && type ==
"Place") {
2033 }
else if (nd.contains(
"initialState") && !nd.contains(
"stateSpace")) {
2042 const std::vector<T> row = detail::num_vec_from_json<T>(nd.at(
"initialState"));
2045 for (std::size_t k = 0; k < row.size(); ++k) space(0, k) = row[k];
2049 if (nd.contains(
"statePrior") || nd.contains(
"stateSpace")) {
2050 if (!nd.contains(
"statePrior") || !nd.contains(
"stateSpace"))
2052 "network_reader: node '" + nd.at(
"name").get<std::string>() +
2053 "' declares one of stateSpace / statePrior without the other; the prior is a "
2054 "distribution over the ROWS of that space and means nothing alone");
2055 net.
set_state_prior(idx, detail::mat_from_json<T>(nd.at(
"stateSpace")),
2056 detail::num_vec_from_json<T>(nd.at(
"statePrior")));
2058 if (nd.contains(
"departureDiscipline")) {
2059 const json& dd = nd.at(
"departureDiscipline");
2060 for (
auto it = dd.begin(); it != dd.end(); ++it) {
2061 auto cit = class_idx.find(it.key());
2062 if (cit != class_idx.end())
2064 idx, cit->second, detail::departure_from_json(it.value().get<std::string>()));
2072 for (
int kind = 0; kind < 3; ++kind) {
2073 static const char* kKeys[] = {
"loadDependence",
"classDependence",
2075 static const char* kTypes[] = {
"loadDependent",
"classDependent",
2077 if (!nd.contains(kKeys[kind]))
continue;
2078 if (type !=
"Queue" && type !=
"Delay")
2080 std::string(
"network_reader: node '") + nd.at(
"name").get<std::string>() +
2081 "' carries '" + kKeys[kind] +
2082 "', which scales a SERVICE rate and is only meaningful at a Queue or a "
2084 const detail::json& blk = nd.at(kKeys[kind]);
2085 if (blk.value(
"type", std::string(kTypes[kind])) != kTypes[kind])
2087 "' at node '" + nd.at(
"name").get<std::string>() +
2088 "' declares type '" +
2089 blk.value(
"type", std::string(
"?")) +
2090 "', which this reader does not implement");
2091 if (!blk.contains(
"scaling"))
2092 throw InputError(std::string(
"network_reader: '") + kKeys[kind] +
2093 "' at node '" + nd.at(
"name").get<std::string>() +
2094 "' carries no 'scaling'");
2101 std::vector<int> cut(classes.size(), 1);
2102 if (blk.contains(
"cutoffs")) {
2103 const std::vector<double> cv = detail::num_vec_from_json<double>(blk.at(
"cutoffs"));
2104 for (std::size_t r = 0; r < cut.size() && r < cv.size(); ++r)
2105 cut[r] =
static_cast<int>(std::lround(cv[r]));
2108 detail::cd_scaling_from_json<T>(blk.at(
"scaling"), cut, classes.size());
2109 const std::vector<T> peak = detail::cd_peak_from_json<T>(blk, blk.at(
"scaling"));
2116 if (nd.contains(
"immediateFeedback")) {
2117 const json& imf = nd.at(
"immediateFeedback");
2118 for (
auto it = imf.begin(); it != imf.end(); ++it) {
2119 auto cit = class_idx.find(it.key());
2120 if (cit != class_idx.end() && it.value().get<
bool>())
2127 const json& routing = model.at(
"routing");
2129 const json& mat = routing.at(
"matrix");
2130 for (
auto ck = mat.begin(); ck != mat.end(); ++ck) {
2132 const std::string key = ck.key();
2133 const std::size_t comma = key.find(
',');
2134 const std::string cs = comma == std::string::npos ? key : key.substr(0, comma);
2135 const std::string cd = comma == std::string::npos ? key : key.substr(comma + 1);
2136 const std::size_t r = class_idx.at(cs);
2137 const std::size_t s = class_idx.at(cd);
2138 for (
auto si = ck.value().begin(); si != ck.value().end(); ++si) {
2139 const std::size_t from = node_idx.at(si.key());
2140 for (
auto di = si.value().begin(); di != si.value().end(); ++di)
2141 P.
set(r, s, from, node_idx.at(di.key()),
2154 if (model.contains(
"routingStrategies")) {
2155 const json& rs = model.at(
"routingStrategies");
2156 for (
auto ni = rs.begin(); ni != rs.end(); ++ni) {
2157 auto nit = node_idx.find(ni.key());
2158 for (
auto ci = ni.value().begin(); ci != ni.value().end(); ++ci) {
2160 detail::routing_from_json(ci.value().get<std::string>());
2168 if (nit == node_idx.end()) {
2170 throw InputError(
"network_reader: routingStrategies names node '" + ni.key() +
2171 "', which the model does not declare");
2174 auto cit = class_idx.find(ci.key());
2175 if (cit == class_idx.end())
continue;
2180 if (model.contains(
"routingWeights")) {
2181 const json& rw = model.at(
"routingWeights");
2182 for (
auto ni = rw.begin(); ni != rw.end(); ++ni) {
2183 auto nit = node_idx.find(ni.key());
2184 if (nit == node_idx.end())
continue;
2185 for (
auto ci = ni.value().begin(); ci != ni.value().end(); ++ci) {
2186 auto cit = class_idx.find(ci.key());
2187 if (cit == class_idx.end())
continue;
2188 std::map<std::size_t, double> w;
2189 for (
auto di = ci.value().begin(); di != ci.value().end(); ++di) {
2190 auto dit = node_idx.find(di.key());
2191 if (dit != node_idx.end()) w[dit->second] = di.value().get<
double>();
2197 if (model.contains(
"routingParams")) {
2198 const json& rp = model.at(
"routingParams");
2199 for (
auto ni = rp.begin(); ni != rp.end(); ++ni) {
2200 auto nit = node_idx.find(ni.key());
2201 if (nit == node_idx.end())
continue;
2202 for (
auto ci = ni.value().begin(); ci != ni.value().end(); ++ci) {
2203 auto cit = class_idx.find(ci.key());
2204 if (cit == class_idx.end() || !ci.value().contains(
"d"))
continue;
2219 if (model.contains(
"stateDepRouting")) {
2220 const json& sd = model.at(
"stateDepRouting");
2221 for (
const char* k : {
"entry",
"departure",
"class",
"branches",
"level",
"C",
"d"})
2222 if (!sd.contains(k))
2223 throw InputError(std::string(
"network_reader: 'stateDepRouting' carries no '") + k +
2225 auto node_of = [&](
const std::string& nm) -> std::size_t {
2226 const std::map<std::string, std::size_t>::const_iterator it = node_idx.find(nm);
2227 if (it == node_idx.end())
2228 throw InputError(
"network_reader: 'stateDepRouting' names node '" + nm +
2229 "', which the model does not declare");
2232 const std::map<std::string, std::size_t>::const_iterator ci =
2233 class_idx.find(sd.at(
"class").get<std::string>());
2234 if (ci == class_idx.end())
2235 throw InputError(
"network_reader: 'stateDepRouting' names class '" +
2236 sd.at(
"class").get<std::string>() +
2237 "', which the model does not declare");
2238 std::vector<std::vector<std::size_t>> branches;
2239 for (
const json& b : sd.at(
"branches")) {
2240 std::vector<std::size_t> centres;
2241 for (
const json& nm : b) centres.push_back(node_of(nm.get<std::string>()));
2242 branches.push_back(centres);
2244 std::vector<std::size_t> level;
2245 for (
const json& v : sd.at(
"level")) level.push_back(
static_cast<std::size_t
>(v.get<
double>()));
2246 std::vector<double> C;
2247 for (
const json& v : sd.at(
"C")) C.push_back(v.get<
double>());
2248 const json& dj = sd.at(
"d");
2249 Matrix<double> d(dj.size(), dj.empty() ? 0 : dj.at(0).size(), 0.0);
2250 for (std::size_t t = 0; t < dj.size(); ++t) {
2251 if (dj.at(t).size() != d.
cols())
2252 throw InputError(
"network_reader: 'stateDepRouting' carries a ragged coefficient "
2254 for (std::size_t b = 0; b < d.
cols(); ++b) d(t, b) = dj.at(t).at(b).get<
double>();
2257 node_of(sd.at(
"departure").get<std::string>()), branches, level, C,
2269 if (model.contains(
"globalDependence")) {
2270 const json& blk = model.at(
"globalDependence");
2271 if (blk.value(
"type", std::string(
"globalDependent")) !=
"globalDependent")
2273 std::string(
"network_reader: 'globalDependence' declares type '") +
2274 blk.value(
"type", std::string(
"?")) +
"', which this reader does not implement");
2275 if (!blk.contains(
"scaling"))
2276 throw InputError(
"network_reader: 'globalDependence' carries no 'scaling'");
2279 std::vector<std::size_t> slot_st, slot_cl;
2280 if (blk.contains(
"slots"))
2281 for (
const json& sm : blk.at(
"slots")) {
2282 auto nit = node_idx.find(sm.at(
"station").get<std::string>());
2283 auto cit = class_idx.find(sm.at(
"class").get<std::string>());
2284 if (nit == node_idx.end() || cit == class_idx.end())
2286 "network_reader: 'globalDependence' names a station or class the model "
2287 "does not declare");
2288 slot_st.push_back(sn0.
nodes[nit->second - 1].station - 1);
2289 slot_cl.push_back(cit->second - 1);
2291 const std::size_t P = slot_st.size();
2292 std::vector<int> cuts(P, 0);
2293 if (blk.contains(
"cutoffs")) {
2294 const std::vector<double> cv = detail::num_vec_from_json<double>(blk.at(
"cutoffs"));
2295 for (std::size_t d = 0; d < P && d < cv.size(); ++d)
2296 cuts[d] =
static_cast<int>(std::lround(cv[d]));
2298 const int wcut = blk.value(
"cutoff", 10);
2299 std::map<std::string, std::vector<T>> tbl;
2300 for (
auto it = blk.at(
"scaling").begin(); it != blk.at(
"scaling").end(); ++it)
2301 tbl[it.key()] = detail::num_vec_from_json<T>(it.value());
2303 if (blk.contains(
"peak")) {
2304 const std::vector<T> pv = detail::num_vec_from_json<T>(blk.at(
"peak"));
2305 for (std::size_t j = 0; j < peak.size() && j < pv.size(); ++j) peak[j] = pv[j];
2309 [slot_st, slot_cl, cuts, tbl,
ones, K, P](
const std::vector<T>& n) {
2311 if (P == 0) key =
"0";
2313 for (std::size_t d = 0; d < P; ++d) {
2316 if (x > cuts[d]) x = cuts[d];
2318 key += std::to_string(x);
2320 typename std::map<std::string, std::vector<T>>::const_iterator it = tbl.find(key);
2321 return it == tbl.end() ?
ones : it->second;
2332 if (model.contains(
"finiteCapacityRegions")) {
2333 for (
const json& rj : model.at(
"finiteCapacityRegions")) {
2334 std::vector<std::size_t> members;
2338 std::vector<double> cap(classes.size(), -1.0);
2339 auto read_class_map = [&](
const json& blk, std::vector<double>& dst) {
2340 for (
auto it = blk.begin(); it != blk.end(); ++it) {
2341 auto cit = class_idx.find(it.key());
2342 if (cit == class_idx.end())
continue;
2343 const double v = it.value().get<
double>();
2344 double& slot = dst[cit->second - 1];
2345 slot = slot == -1.0 ? v : std::min(slot, v);
2348 if (rj.contains(
"classMaxJobs")) read_class_map(rj.at(
"classMaxJobs"), cap);
2349 for (
const json& sj : rj.at(
"stations")) {
2350 auto nit = node_idx.find(sj.at(
"node").get<std::string>());
2351 if (nit == node_idx.end())
2352 throw InputError(
"network_reader: finite capacity region '" +
2353 rj.value(
"name", std::string(
"?")) +
"' names node '" +
2354 sj.at(
"node").get<std::string>() +
2355 "', which the model does not declare");
2356 members.push_back(nit->second);
2357 if (sj.contains(
"classCap")) read_class_map(sj.at(
"classCap"), cap);
2359 std::vector<double> mem(classes.size(), -1.0);
2360 if (rj.contains(
"classMaxMemory")) read_class_map(rj.at(
"classMaxMemory"), mem);
2363 for (
const json& sj : rj.at(
"stations")) {
2364 if (sj.contains(
"classSize"))
2365 for (
auto it = sj.at(
"classSize").begin(); it != sj.at(
"classSize").end(); ++it) {
2366 auto cit = class_idx.find(it.key());
2367 if (cit != class_idx.end())
2368 size[cit->second - 1] =
2371 if (sj.contains(
"classWeight"))
2372 for (
auto it = sj.at(
"classWeight").begin(); it != sj.at(
"classWeight").end();
2374 auto cit = class_idx.find(it.key());
2375 if (cit != class_idx.end())
2376 weight[cit->second - 1] =
2381 if (rj.contains(
"dropRule"))
2382 for (
auto it = rj.at(
"dropRule").begin(); it != rj.at(
"dropRule").end(); ++it) {
2383 auto cit = class_idx.find(it.key());
2384 if (cit != class_idx.end())
2385 rule[cit->second - 1] = detail::drop_from_json(it.value().get<std::string>());
2387 const std::size_t
reg =
2388 net.
add_region(members, cap, rj.value(
"globalMaxJobs", -1.0), rule, mem, size,
2389 rj.value(
"globalMaxMemory", -1.0),
2390 rj.value(
"name", std::string()));
2392 if (rj.contains(
"constraintA") && rj.contains(
"constraintB"))
2394 detail::num_vec_from_json<T>(rj.at(
"constraintB")));
2407 if (model.contains(
"rewards")) {
2408 const std::size_t K = classes.size();
2409 for (
const json& rw : model.at(
"rewards")) {
2410 const std::string nm = rw.at(
"name").get<std::string>();
2411 const std::string kind = rw.at(
"type").get<std::string>();
2412 const std::string node_name = rw.at(
"node").get<std::string>();
2413 auto nit = node_idx.find(node_name);
2414 if (nit == node_idx.end())
2415 throw InputError(
"network_reader: reward '" + nm +
"' names node '" + node_name +
2416 "', which the model does not declare");
2419 std::size_t ist = 0;
2420 for (std::size_t s = 0; s < net.
get_struct().station_to_node.size(); ++s)
2421 if (net.
get_struct().station_to_node[s] == nit->second) ist = s + 1;
2423 throw UnsupportedError(
"network_reader: reward '" + nm +
"' is declared at node '" +
2425 "', which is not a station and so has no job count");
2429 std::size_t cls = 0;
2430 if (rw.contains(
"class")) {
2431 auto cit = class_idx.find(rw.at(
"class").get<std::string>());
2432 if (cit == class_idx.end())
2433 throw InputError(
"network_reader: reward '" + nm +
"' names class '" +
2434 rw.at(
"class").get<std::string>() +
2435 "', which the model does not declare");
2438 const double nservers = net.
get_struct().stations[ist - 1].nservers;
2439 const double cap = net.
get_struct().stations[ist - 1].cap;
2440 const std::size_t base = (ist - 1) * K;
2444 if (kind ==
"QLen") {
2445 net.
set_reward(nm, [base, K, cls](
const std::vector<T>& n) {
2446 if (cls)
return n[base + cls - 1];
2448 for (std::size_t k = 0; k < K; ++k) s = T(s + n[base + k]);
2450 }, kind, nit->second, cls);
2451 }
else if (kind ==
"Util") {
2454 net.
set_reward(nm, [base, K, cls, nservers](
const std::vector<T>& n) {
2456 if (cls) s = n[base + cls - 1];
2458 for (std::size_t k = 0; k < K; ++k) s = T(s + n[base + k]);
2461 }, kind, nit->second, cls);
2462 }
else if (kind ==
"Blocking") {
2463 net.
set_reward(nm, [base, K, cap](
const std::vector<T>& n) {
2465 for (std::size_t k = 0; k < K; ++k) s = T(s + n[base + k]);
2468 }, kind, nit->second, cls);
2471 "network_reader: reward '" + nm +
"' is of type '" + kind +
2472 "', and the reproducible templates are QLen, Util and Blocking; a Custom "
2473 "reward wraps an arbitrary function and is not on the wire at all");
2483 std::ifstream in(path.c_str());
2484 if (!in)
throw InputError(
"network_reader: cannot open " + path);
2488 }
catch (
const detail::json::parse_error& e) {
2489 throw InputError(
"network_reader: malformed JSON in " + path +
": " + e.what());
UnsupportedError(const std::string &what)
A network plus its refreshed NetworkStruct.
std::vector< NodeDef > nodes
every node, in creation order
A queueing network under construction.
void set_drop_rule(std::size_t node, std::size_t cls, DropStrategy rule)
station.setDropRule(class, rule).
void set_state_prior(std::size_t node, const Matrix< T > &space, const std::vector< T > &prior)
StatefulNode.setStatePrior(space, prior): a distribution over the rows of a DECLARED state space.
void set_departure_discipline(std::size_t node, std::size_t cls, lang::DepartureDiscipline rule)
Place.setDepartureDiscipline(class, rule).
std::size_t add_logger(const std::string &nm, const std::string &log_file=std::string())
A Logger node: a pass-through that records every job crossing it.
void set_load_dependence(std::size_t node, const std::vector< T > &alpha)
station.setLoadDependence(alpha): the rate multiplier at population 1, 2, ... The vector is indexed f...
void set_class_capacity(std::size_t node, std::size_t cls, double k)
station.setChainCapacity(class, k).
std::size_t add_source(const std::string &nm)
The external arrival station.
std::size_t add_fork(const std::string &nm, double tasks_per_link=1.0)
A Fork node.
void bind_join(std::size_t join_node, std::size_t fork_node)
Record which Fork a Join created by add_join_unbound closes.
void set_arrival_batch(std::size_t node, std::size_t cls, const Distrib< T > &dist)
Source.setArrivalBatch(class, dist): the batch-size law released at each arrival epoch.
std::size_t add_open_class(const std::string &nm, int prio=0)
An open class.
void set_class_patience(std::size_t cls, const Distrib< T > &dist, lang::ImpatienceType kind=lang::ImpatienceType::RENEGING)
JobClass.setPatience(kind, dist): the CLASS-WIDE abandonment law.
std::size_t add_delay(const std::string &nm)
An infinite-server station (a Delay, MATLAB's Delay / DelayStation).
void set_class_spawn(std::size_t cls, std::size_t spawn_cls)
JobClass.spawnClass (sn.classspawn): the class injected at the same station on every completion of cl...
void set_region_constraint(std::size_t region, const Matrix< T > &A, const std::vector< T > &b)
The optional linear constraint A n <= b a region may carry beyond its caps.
void set_class_immediate_feedback(std::size_t cls)
jobclass.setImmediateFeedback(): the same property, class-wide.
std::size_t add_router(const std::string &nm)
A stateless routing node.
void set_initial_marking(std::size_t node, const std::vector< T > &tokens)
Place.setState(marking): the initial token count of the place, per class.
void set_number_of_servers(std::size_t node, double n)
queue.setNumberOfServers(n).
void set_joint_dependence(std::size_t node, const CdScaling< T > &fun, const std::vector< T > &peak)
station.setJointDependence(eta, peakRatePerClass): MATLAB's Station.ljdScaling / ljdScalingPeak.
void set_fork_tasks_per_link(std::size_t fork_node, std::size_t jobclass, double tasks, std::size_t dest_node=0)
Variable forking levels on an existing Fork, the twin of MATLAB Fork.setTasksPerLink(jobclass,...
void set_patience(std::size_t node, std::size_t cls, const Distrib< T > &dist, lang::ImpatienceType kind=lang::ImpatienceType::RENEGING)
Queue.setPatience(class, dist, type): the abandonment timer of a job WAITING at the station,...
void set_reply_signal_class(std::size_t call_cls, std::size_t reply_cls)
JobClass.setReplySignalClass(reply) (sn.syncreply), plus the sn.replyblock the state layer needs.
std::size_t add_queue(const std::string &nm, SchedStrategy sched=SchedStrategy::FCFS)
A queueing station.
void set_routing(std::size_t node, std::size_t cls, RoutingStrategy rs)
node.setRouting(class, strategy).
void set_join_strategy(std::size_t node, lang::JoinStrategy strategy, double quorum=0.0)
Join.setStrategy(...): STD waits for every sibling, PARTIAL for a quorum.
void set_fork_branch_probability(std::size_t fork_node, std::size_t jobclass, std::size_t dest_node, double prob)
A branch that fires only with probability prob.
std::size_t add_cache(const std::string &nm, const CacheParam< T > &par)
A Cache node with its item population, list capacities and popularity.
void set_reward(const std::string &nm, const std::function< T(const std::vector< T > &)> &fn, const std::string &kind=std::string(), std::size_t node=0, std::size_t cls=0)
model.setReward(name, fn): a named reward evaluated on the AGGREGATE state row, the per-(station,...
std::size_t add_self_looping_class(const std::string &nm, double njobs, std::size_t refstat_node, int prio=0)
SelfLoopingClass(model, name, njobs, refstat, prio): a closed class whose jobs perpetually cycle at t...
void set_marked_classes(std::size_t node, const std::vector< std::size_t > &classes)
Source.markedClasses: the 1-based class carried by each mark of an MMAP.
std::size_t add_closed_class(const std::string &nm, double njobs, std::size_t refstat_node, int prio=0)
A closed class of the given population, referencing a station node.
void set_sched_param(std::size_t node, std::size_t cls, const T &weight)
The DPS / GPS weight of a class at a station.
std::size_t add_class_switch(const std::string &nm, const Matrix< T > &C)
A ClassSwitch node carrying the (nclasses x nclasses) switching matrix.
void set_fork_tasks_per_link_dist(std::size_t fork_node, std::size_t jobclass, const lang::Distrib< T > &dist, std::size_t dest_node=0)
A random jobs-per-link degree, redrawn per link and per forked job.
std::size_t add_sink(const std::string &nm)
The external departure node.
const NetworkStruct< T > & get_struct()
The refreshed struct, MATLAB's model.getStruct().
void set_batch_reject(std::size_t node, std::size_t cls, const T &p)
Queue.setBatchRejectProbability(class, p).
void set_class_dependence(std::size_t node, const CdScaling< T > &fun, const std::vector< T > &peak=std::vector< T >())
station.setClassDependence(beta, peakRatePerClass).
void set_state_dep_routing(std::size_t entry, std::size_t departure, const std::vector< std::vector< std::size_t > > &branches, const std::vector< std::size_t > &level, const std::vector< double > &C, const Matrix< double > &d, std::size_t cls=0)
node.setStateDepRouting(class, departure, branches, level, C, d).
std::size_t add_place(const std::string &nm)
A Place: an SPN token container.
void set_routing_weights(std::size_t node, std::size_t cls, const std::map< std::size_t, double > &weights)
The per-destination weights of a WRROBIN dispatcher, per (node, class).
void set_log_path(const std::string &path)
Network.setLogPath: the directory every Logger of this model writes into.
void set_capacity(std::size_t node, double k)
station.setCapacity(k), the K of Kendall's notation.
NetworkStruct< T > & raw_struct()
The struct WITHOUT refreshing it, for a caller that is still building.
void set_switchover(std::size_t node, std::size_t cls, const Distrib< T > &so)
Queue.setSwitchover(jobclass, distrib): the switchover time of a class.
void set_immediate_feedback(std::size_t node, std::size_t cls)
queue.setImmediateFeedback(class): a completing job of that class is fed straight back into service,...
void set_balking(std::size_t node, std::size_t cls, lang::BalkingStrategy strategy, const std::vector< typename Station< T >::BalkingThreshold > &thresholds)
Queue.setBalking(class, strategy, thresholds): an arrival that refuses to JOIN, on the state it finds...
void add_server_type(std::size_t node, const typename Station< T >::ServerType &stype)
Queue.addServerType(...): one heterogeneous server pool of the station.
void set_setup_delayoff(std::size_t node, std::size_t cls, const Distrib< T > &setup, const Distrib< T > &delayoff)
Queue.setDelayOff(class, setupTime, delayoffTime): the station powers down after sitting idle for the...
void set_class_deadline(std::size_t cls, double due)
JobClass.deadline: the soft deadline EDD, EDF and JMT's tardiness use.
void link(const RoutingMatrix< T > &Pm)
model.link(P): install the routing.
void set_routing_param(std::size_t node, std::size_t cls, int d)
The d of a power-of-d (SQ) dispatcher, per (node, class).
void set_retrial(std::size_t node, std::size_t cls, const Distrib< T > &proc, const T &rate, int max_attempts=0)
Queue.setRetrial(...): a station with an ORBIT instead of a waiting line.
void set_service(std::size_t node, std::size_t cls, const Distrib< T > &d)
station.setService(class, dist).
void set_reference_class(std::size_t cls)
JobClass.setReferenceClass(true): sn.refclass(c) picks this class.
std::size_t add_join_unbound(const std::string &nm)
The Join station on its own, with the fork left to bind_join.
void set_hetero_sched_policy(std::size_t node, lang::HeteroSchedPolicy policy)
Queue.setHeteroSchedPolicy(...): how the server pools are picked among.
void set_breakdown(std::size_t node, const Distrib< T > &failure, const Distrib< T > &repair, const std::vector< Distrib< T > > &down_service=std::vector< Distrib< T > >())
Queue.setBreakdown(failure, repair, downService): the server alternates up and down on the two clocks...
void set_pas(std::size_t node, const std::function< T(const std::vector< std::size_t > &)> &mu, const std::vector< std::vector< bool > > &swap_graph=std::vector< std::vector< bool > >())
Queue.setService(@(c) ...) for a pass-and-swap / order-independent station: the total service rate mu...
std::size_t add_transition(const std::string &nm, const TransitionParam< T > &par)
A Transition: the firing rules of an SPN, as Transition in MATLAB.
void set_orbit_impatience(std::size_t node, std::size_t cls, const Distrib< T > &dist)
Queue.setOrbitImpatience(class, dist): abandonment from the retrial orbit.
void set_signal(std::size_t cls, lang::SignalType type, lang::RemovalPolicy policy=lang::RemovalPolicy::RANDOM, std::size_t target=0, const std::vector< T > &remdist=std::vector< T >())
Declare a class to be a G-network SIGNAL rather than a job.
void set_global_dependence(const GdScaling< T > &fun, const std::vector< T > &peak)
model.setGlobalDependence(phi, peak): MATLAB's Network.gdScaling.
void set_server_parallelism(std::size_t node, std::size_t cls, std::size_t n)
Queue.setServerParallelism(class, n): the servers a job seizes for the whole of its service.
void set_region_weights(std::size_t region, const std::vector< T > &weight)
FiniteCapacityRegion.setClassWeight: the per-class weight the region's global cap counts a job agains...
void set_arrival(std::size_t node, std::size_t cls, const Distrib< T > &d)
source.setArrival(class, dist): the same table, at the Source.
std::size_t add_region(const std::vector< std::size_t > &nodes, const std::vector< double > &class_max_jobs, double global_max_jobs=-1.0, const std::vector< DropStrategy > &rule=std::vector< DropStrategy >(), const std::vector< double > &class_max_memory=std::vector< double >(), const std::vector< T > &class_size=std::vector< T >(), double global_max_memory=-1.0, const std::string &name=std::string())
FiniteCapacityRegion(model, nodes): a cap on the jobs held ACROSS a set of stations.
void set_polling_type(std::size_t node, lang::PollingType rule, int par=0)
Queue.setPollingType(rule, par): the polling discipline of a POLLING station, identical across all cl...
The routing matrix a model script fills in, MATLAB's P cell array.
void set(std::size_t r, std::size_t s, std::size_t i, std::size_t j, const T &p)
The exception types the port throws.
Enumerations and the minimal distribution descriptor shared by the model layer of the C++ port.
Response get(const std::string &url, int timeoutMillis)
GET a URL.
qn::Network< T > read_network_json(const std::string &path)
Parse a model.json file into a qn::Network<T>.
qn::Network< T > build_network_from_json(const detail::json &root)
Build a qn::Network<T> from a parsed model.json envelope.
mam::Map< T > dist_to_map(const Distrib< T > &d)
SchedStrategy
Scheduling disciplines, with the values of MATLAB SchedStrategy.
DropStrategy
Blocking and loss rules, with the values of MATLAB DropStrategy.
@ IMMEDIATE
fires with zero delay, resolved by weight and priority
@ TIMED
fires after its firing distribution elapses
BalkingStrategy
Balking rules, with the values of MATLAB BalkingStrategy.
SignalType
G-network signal classes, with the values of MATLAB SignalType.
@ REPLY
completes a synchronous call, releasing a held server
@ NEGATIVE
removes a batch of jobs (Gelenbe's negative customer)
@ CATASTROPHE
removes EVERY job at the station
RoutingStrategy
Routing strategies, with the values of MATLAB RoutingStrategy.
DepartureDiscipline
When a Place releases a served token, MATLAB DepartureDiscipline.
Distrib< T > prior_continuous(const Distrib< T > ¶m_dist, const std::function< Distrib< T >(const T &)> &factory)
Prior(paramDist, distFactory): the continuous form.
RemovalPolicy
Which job a negative signal removes, with the values of MATLAB RemovalPolicy.
@ FCFS
the oldest waiting job; servers only once nobody waits
@ LCFS
the newest waiting job; servers only once nobody waits
@ RANDOM
uniform over waiting AND in-service jobs
HeteroSchedPolicy
How a heterogeneous station picks among its server types, MATLAB HeteroSchedPolicy.
PollingType
Polling service disciplines, with the values of MATLAB PollingType.
Distrib< T > prior_discrete(const std::vector< Distrib< T > > &alternatives, const std::vector< T > &probabilities)
Prior(distributions, probabilities): the discrete form.
std::function< std::vector< T >(const std::vector< T > &)> CdScaling
A class-dependent scaling map, sn.cdscaling.
ReplacementStrategy
Cache replacement policies, with the values of MATLAB ReplacementStrategy.
ImpatienceType
Impatience kinds, with the values of MATLAB ImpatienceType.
std::vector< T > ones(std::size_t n)
Column vector of ones, the ubiquitous e in MAP algebra.
The Network constructor API: Queue, Delay, Source, Sink, Router, ClassSwitch, Cache,...
Number-type abstraction for the templated API port.
Prior: parameter uncertainty as a weighted set of alternative models.
static Distrib empirical_cdf(const std::vector< T > &x, const std::vector< T > &F)
EmpiricalCDF(x, F): the moments of the MIDPOINT rule over the CDF bins, which is what MATLAB Empirica...
static Distrib nhpp(const std::vector< T > &breakpoints, const std::vector< T > &rates, bool cyclic)
NHPP(breakpoints, rates, cyclic): a MAPt of ORDER ONE, which is what an inhomogeneous Poisson process...
static Distrib replayer(const std::vector< T > &samples)
Replayer / Trace: the samples, with their empirical first two moments.
static Distrib normal(const T &mu, const T &sigma)
Normal(mu, sigma): the Gaussian, for use as a continuous Prior's parameter density.
static Distrib dmap(const Matrix< T > &D0, const Matrix< T > &D1)
DMAP(D0, D1): a DISCRETE-time MAP, where D0 + D1 is stochastic rather than a generator.
static Distrib exp_rate(const T &r)
static Distrib phase_type(const std::vector< T > &alpha, const Matrix< T > &A, bool acyclic)
PH / APH given by (alpha, A): D0 = A and D1 = (-A e) alpha.
static Distrib mapt(const std::vector< T > &breakpoints, const std::vector< Matrix< T > > &D0segs, const std::vector< Matrix< T > > &D1segs, bool cyclic)
MAPt(breakpoints, {D0_k}, {D1_k}, cyclic): a piecewise-constant (D0(t), D1(t)).
static Distrib weibull(const T &scale, const T &shape)
static Distrib bmap(const std::vector< Matrix< T > > &D)
BMAP: the batch-size blocks D0, D1, ..., Dk, where Dj carries an arrival of batch size j.
static Distrib mmap(const Matrix< T > &D0, const std::vector< Matrix< T > > &D1k)
MMAP: D0 plus one D1 block per mark.
static Distrib disabled_dist()
static Distrib pht(const std::vector< T > &breakpoints, const std::vector< std::vector< T > > &alphas, const std::vector< Matrix< T > > &Ssegs, bool cyclic)
PHt(breakpoints, {alpha_k}, {S_k}, cyclic), stored as its equivalent MAP schedule: D0 = S and D1 = (-...
static Distrib erlang_fit(const T &m, const T &c2)
Erlang fitted to a mean and an SCV, as MATLAB's Erlang.fitMeanAndSCV.
static Distrib pareto(const T &shape, const T &scale)
Pareto(shape, scale), with the MATLAB parameter order (alpha, k).
static Distrib gamma_dist(const T &shape, const T &scale)
Gamma(shape, scale), Weibull(scale, shape) and Lognormal(mu, sigma).
static Distrib cox2(const T &mu1, const T &mu2, const T &phi1)
Cox2(mu1, mu2, phi1), MATLAB's two-phase Coxian constructor.
static Distrib map_dist(const Matrix< T > &D0, const Matrix< T > &D1, ProcessType tag)
A MAP given by its two matrices; the moments are those of its stationary phase.
static Distrib poisson(const T &lambda)
Poisson(lambda), whose SCV is 1/lambda – the count's variance is lambda and its mean is lambda,...
static Distrib bernoulli(const T &p)
Bernoulli(p): one trial, mean p and variance p(1-p).
static Distrib hyperexp_n(const std::vector< T > &p, const std::vector< T > &lambda)
HyperExp(p, lambda1, lambda2): phase i chosen with probability p_i.
static Distrib discrete_uniform(const T &a, const T &b)
DiscreteUniform(a, b) over the integers a..b inclusive.
static Distrib geometric(const T &p)
Geometric(p) on the MATLAB convention: the NUMBER OF TRIALS to the first success, support {1,...
static Distrib det(const T &m)
static Distrib rap(const Matrix< T > &H0, const Matrix< T > &H1)
RAP(H0, H1): a rational arrival process, whose moments are the MAP ones.
static Distrib uniform(const T &a, const T &b)
Uniform(a, b).
static Distrib lognormal(const T &logmean, const T &logsigma)
static Distrib hyperexp(const T &p, const T &lambda1, const T &lambda2)
static Distrib me(const std::vector< T > &alpha, const Matrix< T > &A)
ME(alpha, A): the matrix-exponential distribution, whose moments are the phase-type ones – k!
static Distrib immediate()
The Immediate singleton.
static Distrib erlang(const T &phase_rate, std::size_t r)
Erlang(alpha, r): r phases of rate alpha, as MATLAB's Erlang(phaseRate, nphases).
static Distrib exp_mean(const T &m)
static Distrib coxian(const std::vector< T > &mu, const std::vector< T > &phi)
Coxian(mu, phi): phase i completes with probability phi(i) and otherwise moves to phase i+1.
static Distrib discrete_sampler(const std::vector< T > &p, const std::vector< T > &x)
DiscreteSampler(p, x): the pmf p over the points x.
static Distrib binomial(const T &n, const T &p)
Binomial(n, p).
static Distrib zipf(const T &s, std::size_t n)
Zipf(s, n) over the ranks 1..n, with the generalized harmonic moments H(s-1,n)/H(s,...
The popularity LAW each class declared, beside the pmf it expands to.
T qlru
Delayed-hit retrieval system (Cache.setRetrievalSystem).
std::vector< Popularity > preadkind
per class, parallel to pread
std::vector< T > initstate
The DECLARED initial contents of the cache, as the reference dumps the node's state row: the per-clas...
std::vector< int > itemsize
Per-item storage cost (size) and per-list cap on the total cost of the resident items (ton21cache Sec...
std::vector< int > costcap
std::map< std::size_t, std::vector< std::size_t > > retrieval_queues
read class(0-based)->nodes
std::vector< std::vector< Matrix< T > > > accost
(u) x (n) of (h+1)x(h+1), or empty
std::vector< std::vector< std::size_t > > retrieval_classes
(nitems x nclasses), 1-based
std::vector< int > itemcap
std::vector< std::size_t > missclass
std::vector< std::size_t > hitclass
lang::ReplacementStrategy replacestrat
std::vector< std::size_t > classitem
Item read by each per-item class of a cache network (MATLAB Cache.setItemReadClasses,...
std::vector< std::vector< T > > pread
(u) x (n), empty row = NaN
A Logger node's trace configuration, MATLAB's Logger properties and sn.nodeparam{ind}...
std::string file_path
directory, MATLAB's model.getLogPath
One balking threshold: with min_jobs <= n <= max_jobs at the station, an arriving job of the class re...
A heterogeneous server pool: count servers that serve only compatible classes, each with its own serv...
std::vector< bool > compatible
per class; empty = every class
std::vector< Distrib< T > > service
per class
The parameters of a Cache node, MATLAB's sn.nodeparam{ind} for a Cache.
std::vector< double > firingprio
firing priority per mode
std::vector< lang::TimingStrategy > timing
immediate or timed
std::vector< std::string > modenames
std::vector< lang::Distrib< T > > firingproc
firing distribution per mode
std::vector< double > nmodeservers
servers per mode, may be infinite
std::vector< T > fireweight
weight among simultaneously enabled modes
std::vector< Matrix< T > > firing
firing[m](p,r): class-r tokens mode m moves to/from place p when it fires.
std::vector< Matrix< T > > enabling
enabling[m](p,r): class-r tokens of place p (0-based node) mode m needs.
std::vector< std::function< T(const std::vector< T > &)> > firingdep
Marking-dependent firing-rate multiplier g_m(marking); an empty entry is the unit multiplier.
std::vector< Matrix< T > > inhibiting
inhibiting[m](p,r): class-r tokens of p that BLOCK mode m (Inf = never).
std::vector< std::size_t > firingphases
phase count per mode, 0 when non-Markovian