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" || type ==
"MMAPt" ||
453 type ==
"MPHt" || type ==
"BMMAPt") {
454 const json& p = obj.at(
"params");
456 for (
const json& b : p.at(
"breakpoints"))
457 bp.push_back(num_traits<T>::from_double(b.get<
double>()));
463 const bool cyc = !p.contains(
"cyclic") || p.at(
"cyclic").get<
bool>();
473 auto read_seg = [&](
const json& seg) {
474 if (seg.is_number()) {
476 M(0, 0) = num_traits<T>::from_double(num_from_json(seg));
480 throw InputError(
"network_reader: a schedule segment must be a matrix, "
481 "a row of numbers, or a single number");
482 if (!seg.empty() && seg.at(0).is_number()) {
483 Matrix<T> M(1, seg.size());
484 for (std::size_t b = 0; b < seg.size(); ++b)
485 M(0, b) = num_traits<T>::from_double(num_from_json(seg.at(b)));
488 return mat_from_json<T>(seg);
490 auto read_mats = [&](
const char* key) {
491 std::vector<Matrix<T> > out;
492 for (
const json& seg : p.at(key)) out.push_back(read_seg(seg));
495 if (type ==
"NHPP") {
496 std::vector<T> rates;
497 for (
const json& r : p.at(
"rates"))
498 rates.push_back(num_traits<T>::from_double(r.get<
double>()));
506 auto read_mark_mats = [&](
const char* key) {
507 std::vector<std::vector<Matrix<T> > > out;
508 for (
const json& per_mark : p.at(key)) {
509 if (!per_mark.is_array())
510 throw InputError(
"network_reader: the marked schedule key '" +
511 std::string(key) +
"' must nest marks over segments");
512 std::vector<Matrix<T> > segs;
513 for (
const json& seg : per_mark) segs.push_back(read_seg(seg));
520 if (type ==
"BMMAPt") {
524 std::vector<std::vector<std::vector<Matrix<T> > > > blocks;
525 for (
const json& per_mark : p.at(
"D1kb")) {
526 if (!per_mark.is_array())
527 throw InputError(
"network_reader: 'D1kb' must nest marks over batch sizes "
529 std::vector<std::vector<Matrix<T> > > per_batch;
530 for (
const json& batch : per_mark) {
531 if (!batch.is_array())
532 throw InputError(
"network_reader: 'D1kb' must nest marks over batch "
533 "sizes over segments");
534 std::vector<Matrix<T> > segs;
535 for (
const json& seg : batch) segs.push_back(read_seg(seg));
536 per_batch.push_back(segs);
538 blocks.push_back(per_batch);
542 std::vector<std::vector<T> > alphas;
543 for (
const json& a : p.at(
"alpha")) {
544 const std::vector<double> row = a.get<std::vector<double> >();
546 for (
double v : row) av.push_back(num_traits<T>::from_double(v));
547 alphas.push_back(av);
549 if (type ==
"MPHt") {
552 const std::vector<std::vector<Matrix<T> > > ex = read_mark_mats(
"exit");
553 std::vector<std::vector<std::vector<T> > > exits;
554 for (
const std::vector<Matrix<T> >& per_mark : ex) {
555 std::vector<std::vector<T> > segs;
556 for (
const Matrix<T>& M : per_mark) {
558 for (std::size_t i = 0; i < M.rows(); ++i)
559 for (std::size_t j = 0; j < M.cols(); ++j) v.push_back(M(i, j));
562 exits.push_back(segs);
575 if (type ==
"Replayer" || type ==
"Trace") {
576 if (obj.contains(
"params") && obj.at(
"params").contains(
"fileName")) {
577 const std::string path = obj.at(
"params").at(
"fileName").get<std::string>();
578 std::ifstream tr(path.c_str());
580 std::vector<T> samples;
582 while (tr >> v) samples.push_back(num_traits<T>::from_double(v));
583 if (!samples.empty()) {
590 if (obj.contains(
"ph")) {
591 const json& ph = obj.at(
"ph");
592 const std::vector<double> a = ph.at(
"alpha").get<std::vector<double> >();
593 const std::vector<std::vector<double> > rows =
594 ph.at(
"T").get<std::vector<std::vector<double> > >();
595 std::vector<T> alpha;
596 for (
double x : a) alpha.push_back(num_traits<T>::from_double(x));
597 Matrix<T> A(rows.size(), rows.empty() ? 0 : rows[0].size());
598 for (std::size_t i = 0; i < rows.size(); ++i)
599 for (std::size_t j = 0; j < rows[i].size(); ++j)
600 A(i, j) = num_traits<T>::from_double(rows[i][j]);
603 if (obj.contains(
"params") && obj.at(
"params").contains(
"mean"))
605 num_traits<T>::from_double(obj.at(
"params").at(
"mean").get<
double>()));
607 "network_reader: a Replayer carries neither a readable trace file, nor the APH fit "
608 "the writers add beside it, nor a mean; there is nothing to reconstruct");
613 if (type ==
"MMPP2") {
614 const json& p = obj.at(
"params");
615 const double l0 = p.at(
"lambda0").get<
double>(), l1 = p.at(
"lambda1").get<
double>();
616 const double s0 = p.at(
"sigma0").get<
double>(), s1 = p.at(
"sigma1").get<
double>();
617 Matrix<T> D0(2, 2), D1(2, 2);
618 D1(0, 0) = num_traits<T>::from_double(l0);
619 D1(1, 1) = num_traits<T>::from_double(l1);
620 D0(0, 0) = num_traits<T>::from_double(-(l0 + s0));
621 D0(0, 1) = num_traits<T>::from_double(s0);
622 D0(1, 0) = num_traits<T>::from_double(s1);
623 D0(1, 1) = num_traits<T>::from_double(-(l1 + s1));
626 if (type ==
"MAP" || type ==
"MMPP") {
627 if (!obj.contains(
"map"))
628 throw InputError(
"network_reader: " + type +
" carries no 'map' (D0, D1) object");
629 const json& mp = obj.at(
"map");
631 mat_from_json<T>(mp.at(
"D1")),
640 if (type ==
"MPH" || type ==
"MarkedPH") {
641 if (!obj.contains(
"mph"))
643 " carries no 'mph' (alpha, S, exit) object");
644 const json& mp = obj.at(
"mph");
645 const Matrix<T> alpha_m = mat_from_json<T>(mp.at(
"alpha"));
646 const Matrix<T> S = mat_from_json<T>(mp.at(
"S"));
647 const std::size_t H = S.rows();
648 if (alpha_m.rows() * alpha_m.cols() != H)
649 throw InputError(
"network_reader: an MPH's alpha and S disagree in order");
650 std::vector<T> alpha;
651 for (std::size_t i = 0; i < alpha_m.rows(); ++i)
652 for (std::size_t j = 0; j < alpha_m.cols(); ++j) alpha.push_back(alpha_m(i, j));
653 const std::vector<Matrix<T> > ex = mat_list_from_json<T>(mp.at(
"exit"));
654 std::vector<Matrix<T> > blocks;
655 for (
const Matrix<T>& sk : ex) {
656 if (sk.rows() * sk.cols() != H)
657 throw InputError(
"network_reader: an MPH exit vector and S disagree in order");
659 for (std::size_t i = 0; i < sk.rows(); ++i)
660 for (std::size_t j = 0; j < sk.cols(); ++j) sv.push_back(sk(i, j));
661 Matrix<T> block(H, H, num_traits<T>::from_int(0));
662 for (std::size_t i = 0; i < H; ++i)
663 for (std::size_t j = 0; j < H; ++j) block(i, j) = T(sv[i] * alpha[j]);
664 blocks.push_back(block);
670 if (type ==
"MMAP" || type ==
"MarkedMAP") {
671 if (!obj.contains(
"mmap"))
672 throw InputError(
"network_reader: " + type +
" carries no 'mmap' (D0, D1k) object");
673 const json& mp = obj.at(
"mmap");
675 mat_list_from_json<T>(mp.at(
"D1k")));
680 if (type ==
"BMAP" || type ==
"MarkedMMPP") {
681 const json& p = obj.at(
"params");
682 const std::vector<Matrix<T> > D = mat_list_from_json<T>(p.at(
"D"));
685 throw InputError(
"network_reader: a MarkedMMPP carries D0 and at least one marked block");
688 if (type ==
"DMAP") {
689 const json& p = obj.at(
"params");
693 const json& p = obj.at(
"params");
698 if (type ==
"ME" || type ==
"CME") {
699 const json& p = obj.at(
"params");
700 const std::vector<double> a = p.at(
"alpha").get<std::vector<double> >();
701 std::vector<T> alpha;
702 for (
double v : a) alpha.push_back(num_traits<T>::from_double(v));
714 if (obj.contains(
"ph")) {
715 const json& ph = obj.at(
"ph");
716 const std::vector<double> a = ph.at(
"alpha").get<std::vector<double> >();
717 const std::vector<std::vector<double> > rows =
718 ph.at(
"T").get<std::vector<std::vector<double> > >();
719 if (a.empty() || rows.size() != a.size())
720 throw InputError(
"network_reader: distribution '" + type +
721 "' has a 'ph' block whose alpha and T disagree in order");
722 std::vector<T> alpha;
723 alpha.reserve(a.size());
724 for (
double v : a) alpha.push_back(num_traits<T>::from_double(v));
725 Matrix<T> A(rows.size(), rows.empty() ? 0 : rows[0].size());
726 for (std::size_t i = 0; i < rows.size(); ++i)
727 for (std::size_t j = 0; j < rows[i].size(); ++j)
728 A(i, j) = num_traits<T>::from_double(rows[i][j]);
736 if (!obj.contains(
"params") && obj.contains(
"fit"))
737 return dist_from_fit<T>(type, obj.at(
"fit"));
738 if (!obj.contains(
"params"))
739 throw InputError(
"network_reader: distribution '" + type +
740 "' has neither params nor a fit block");
741 const json& p = obj.at(
"params");
743 const double lam = p.contains(
"lambda") ? p.at(
"lambda").get<
double>()
744 : p.at(
"rate").
get<double>();
747 if (type ==
"Det")
return lang::Distrib<T>::det(num_traits<T>::from_double(p.at(
"value").get<
double>()));
748 if (type ==
"Erlang") {
749 const double lam = p.at(
"lambda").get<
double>();
750 const long k = p.at(
"k").get<
long>();
753 if (type ==
"HyperExp") {
754 const std::vector<T> pv = num_vec_from_json<T>(p.at(
"p"));
755 const std::vector<T> lv = num_vec_from_json<T>(p.at(
"lambda"));
758 if (pv.size() == 2 && lv.size() == 2)
762 if (type ==
"Coxian")
764 num_vec_from_json<T>(p.at(
"phi")));
767 num_traits<T>::from_double(p.at(
"mu2").get<
double>()),
768 num_traits<T>::from_double(p.at(
"phi1").get<
double>()));
769 if (type ==
"Uniform")
771 num_traits<T>::from_double(p.at(
"b").get<
double>()));
775 num_traits<T>::from_double(p.at(
"beta").get<
double>()));
776 if (type ==
"Lognormal")
778 num_traits<T>::from_double(p.at(
"sigma").get<
double>()));
784 if (type ==
"Normal")
786 num_traits<T>::from_double(p.at(
"sigma").get<
double>()));
790 if (type ==
"Pareto")
792 num_traits<T>::from_double(p.at(
"scale").get<
double>()));
793 if (type ==
"Weibull")
795 num_traits<T>::from_double(p.at(
"beta").get<
double>()));
796 if (type ==
"DiscreteUniform")
798 num_traits<T>::from_double(p.at(
"min").get<
double>()),
799 num_traits<T>::from_double(p.at(
"max").get<
double>()));
800 if (type ==
"Bernoulli")
802 if (type ==
"Binomial")
804 num_traits<T>::from_double(p.at(
"p").get<
double>()));
805 if (type ==
"Poisson")
807 if (type ==
"Geometric")
811 std::size_t(p.at(
"n").get<
long>()));
812 if (type ==
"DiscreteSampler") {
813 const std::vector<T> pv = num_vec_from_json<T>(p.at(
"p"));
814 const std::vector<T> xv =
815 p.contains(
"x") ? num_vec_from_json<T>(p.at(
"x")) : std::vector<T>();
818 if (type ==
"EmpiricalCDF" || type ==
"EmpiricalCdf")
820 num_vec_from_json<T>(p.at(
"F")));
831 if (p.contains(
"mean") && !p.contains(
"scv"))
833 if (p.contains(
"mean") && p.contains(
"scv")) {
834 const double mean = p.at(
"mean").get<
double>();
835 const double scv = p.at(
"scv").get<
double>();
836 if (std::fabs(scv - 1.0) < 1e-12)
840 num_traits<T>::from_double(scv));
841 return hyperexp_fit_mean_scv<T>(mean, scv);
843 throw UnsupportedError(
"network_reader: unsupported distribution family '" + type +
865typename qn::CacheParam<T>::Popularity popularity_kind_from_json(
const json& obj,
866 std::size_t nitems) {
867 typename qn::CacheParam<T>::Popularity k;
868 const std::string type = obj.value(
"type", std::string());
869 const json& p = obj.contains(
"params") ? obj.at(
"params") : obj;
870 if (type ==
"Zipf") {
872 k.s = p.at(
"s").get<
double>();
873 k.n = p.contains(
"n") ? std::size_t(p.at(
"n").get<
long>()) : nitems;
874 }
else if (type ==
"DiscreteSampler" || p.contains(
"p")) {
876 k.n = p.contains(
"p") ? p.at(
"p").size() : nitems;
882std::vector<T> pmf_from_json(
const json& obj, std::size_t nitems) {
883 const std::string type = obj.value(
"type", std::string());
884 const json& p = obj.contains(
"params") ? obj.at(
"params") : obj;
890 if (type ==
"DiscreteSampler" || (p.contains(
"p") && p.at(
"p").is_array())) {
891 for (
const json& v : p.at(
"p")) out.push_back(num_traits<T>::from_double(v.get<
double>()));
894 if (type ==
"Zipf") {
895 const double s = p.at(
"s").get<
double>();
896 const std::size_t n = p.contains(
"n") ? std::size_t(p.at(
"n").get<
long>()) : nitems;
898 for (std::size_t k = 1; k <= n; ++k) h += std::pow(
double(k), -s);
899 for (std::size_t k = 1; k <= n; ++k)
900 out.push_back(num_traits<T>::from_double(std::pow(
double(k), -s) / h));
904 "network_reader: a cache popularity is written as '" + type +
905 "', and the discrete families carrying an item pmf are DiscreteSampler and Zipf");
918constexpr std::size_t REMOVAL_PMF_MAX_TERMS = 4096;
940std::vector<T> removal_pmf_from_json(
const json& obj) {
941 const std::string type = obj.value(
"type", std::string());
942 const json& p = obj.contains(
"params") ? obj.at(
"params") : obj;
944 auto put = [&out](std::size_t k,
double v) {
945 if (out.size() <= k) out.resize(k + 1, num_traits<T>::from_int(0));
946 out[k] = num_traits<T>::from_double(num_traits<T>::to_double(out[k]) + v);
948 if (type ==
"DiscreteSampler" || (p.contains(
"p") && p.at(
"p").is_array())) {
952 const json& pv = p.at(
"p");
953 const bool has_x = p.contains(
"x") && p.at(
"x").is_array();
954 for (std::size_t i = 0; i < pv.size(); ++i) {
956 has_x ? p.at(
"x").at(i).get<
double>() : static_cast<double>(i + 1);
957 if (xi < 0)
throw InputError(
"network_reader: a batch size cannot be negative");
958 put(
static_cast<std::size_t
>(xi + 0.5), pv.at(i).get<
double>());
962 if (type ==
"Bernoulli") {
963 const double q = p.at(
"p").get<
double>();
968 if (type ==
"Binomial") {
969 const double q = p.at(
"p").get<
double>();
970 const std::size_t n =
static_cast<std::size_t
>(p.at(
"n").get<double>() + 0.5);
971 double term = std::pow(1.0 - q,
static_cast<double>(n));
972 for (std::size_t k = 0; k <= n; ++k) {
974 if (k < n && q < 1.0)
975 term *= (
static_cast<double>(n - k) /
static_cast<double>(k + 1)) * q / (1.0 - q);
979 if (type ==
"Poisson") {
980 const double lam = p.at(
"lambda").get<
double>();
981 double term = std::exp(-lam), acc = 0.0;
982 for (std::size_t k = 0; k < REMOVAL_PMF_MAX_TERMS; ++k) {
985 if (acc > 1.0 - 1e-15)
break;
986 term *= lam /
static_cast<double>(k + 1);
990 if (type ==
"Geometric") {
993 const double q = p.at(
"p").get<
double>();
994 if (q <= 0.0 || q > 1.0)
throw InputError(
"network_reader: Geometric(p) needs 0 < p <= 1");
996 double term = q, acc = 0.0;
997 for (std::size_t k = 1; k <= REMOVAL_PMF_MAX_TERMS; ++k) {
1000 if (acc > 1.0 - 1e-15)
break;
1005 if (type ==
"DiscreteUniform") {
1006 const long lo =
static_cast<long>(p.at(
"min").get<double>());
1007 const long hi =
static_cast<long>(p.at(
"max").get<double>());
1008 if (hi < lo || lo < 0)
1009 throw InputError(
"network_reader: DiscreteUniform(min,max) needs 0 <= min <= max");
1010 const double w = 1.0 /
static_cast<double>(hi - lo + 1);
1011 for (
long k = lo; k <= hi; ++k) put(static_cast<std::size_t>(k), w);
1014 if (type ==
"Det") {
1015 const double v = p.contains(
"t") ? p.at(
"t").get<
double>() : p.at(
"mean").
get<double>();
1016 if (v < 0)
throw InputError(
"network_reader: a batch size cannot be negative");
1017 put(
static_cast<std::size_t
>(v + 0.5), 1.0);
1021 "network_reader: a signal removal law is written as '" + type +
1022 "', and the discrete families a batch size can be drawn from are DiscreteSampler, "
1023 "Bernoulli, Binomial, Poisson, Geometric, DiscreteUniform and Det");
1040lang::CdScaling<T> cd_scaling_from_json(
const json& tbl,
const std::vector<int>& cutoffs,
1042 std::map<std::string, std::vector<T> > table;
1043 for (
auto it = tbl.begin(); it != tbl.end(); ++it)
1044 table[it.key()] = num_vec_from_json<T>(it.value());
1045 const std::vector<int> cut = cutoffs;
1046 return [table, cut, K](
const std::vector<T>& n) {
1048 for (std::size_t r = 0; r < K; ++r) {
1049 int v = r < n.size()
1050 ?
static_cast<int>(std::lround(num_traits<T>::to_double(n[r])))
1053 if (r < cut.size() && v > cut[r]) v = cut[r];
1055 key += std::to_string(v);
1057 std::vector<T> out(K, num_traits<T>::from_int(1));
1058 typename std::map<std::string, std::vector<T> >::const_iterator it = table.find(key);
1059 if (it == table.end())
return out;
1060 for (std::size_t r = 0; r < K && r < it->second.size(); ++r) out[r] = it->second[r];
1080std::function<T(
const std::vector<std::size_t>&)> oi_rate_from_json(
const json& tbl,
1081 const std::vector<int>& cutoffs,
1083 std::map<std::string, T> table;
1084 for (
auto it = tbl.begin(); it != tbl.end(); ++it)
1085 table[it.key()] = num_traits<T>::from_double(it.value().get<
double>());
1086 const std::vector<int> cut = cutoffs;
1087 return [table, cut, K](
const std::vector<std::size_t>& micro) {
1088 std::vector<int> cnt(K, 0);
1089 for (std::size_t j = 0; j < micro.size(); ++j)
1090 if (micro[j] >= 1 && micro[j] <= K) ++cnt[micro[j] - 1];
1092 for (std::size_t r = 0; r < K; ++r) {
1094 if (r < cut.size() && v > cut[r]) v = cut[r];
1096 key += std::to_string(v);
1098 typename std::map<std::string, T>::const_iterator it = table.find(key);
1099 return it == table.end() ? num_traits<T>::from_int(0) : it->second;
1115std::vector<T> cd_peak_from_json(
const json& blk,
const json& tbl) {
1116 if (blk.contains(
"peak") && !blk.at(
"peak").
empty())
1117 return num_vec_from_json<T>(blk.at(
"peak"));
1119 for (
auto it = tbl.begin(); it != tbl.end(); ++it) {
1120 if (it.key().find_first_not_of(
"0,") == std::string::npos)
continue;
1121 const std::vector<double> row = num_vec_from_json<double>(it.value());
1122 for (
double x : row)
1123 if (std::isfinite(x) && x > bmax) bmax = x;
1125 return std::vector<T>(1, num_traits<T>::from_double(bmax));
1136inline void reject_unconsumed_model_keys(
const json& model) {
1137 static const char* kModelKeys[] = {
"name",
"type",
"nodes",
1138 "classes",
"routing",
"format",
1139 "version",
"rewards",
"finiteCapacityRegions",
1140 "routingStrategies",
"routingWeights",
"routingParams",
1141 "logPath",
"globalDependence",
"stateDepRouting"};
1148 static const char* kNodeKeys[] = {
1149 "name",
"type",
"scheduling",
"servers",
"service",
1150 "buffer",
"capacity",
"dropRule",
"schedParams",
"pollingType",
1151 "pollingPar",
"classSwitchMatrix",
"csMatrix",
"forkNode",
1152 "tasksPerLink",
"fanOutByDest",
"fanOutDist",
"fanOutProb",
1153 "items",
"numItems",
"itemLevelCap",
"popularity",
"replacementStrategy",
1154 "itemSizes",
"costCaps",
"accessProb",
"admissionProb",
"accessGraph",
1156 "cache",
"initialState",
1157 "retrievalSystem",
"queues",
"hitClass",
"missClass",
1158 "immediateFeedback",
"loadDependence",
1159 "classDependence",
"jointDependence",
1160 "modes",
"classCap",
"departureDiscipline",
1161 "oiServiceRate",
"oiCutoffs",
"swapGraph",
1162 "arrivalBatch",
"markedClasses",
1163 "stateSpace",
"statePrior",
"joinStrategy",
"joinQuorum",
1164 "setupTime",
"delayOffTime",
"switchoverTimes",
"breakdown",
1165 "serverTypes",
"heteroSchedPolicy",
"serverParallelism",
1166 "balking",
"retrial",
"patience",
"orbitImpatience",
1169 "fileName",
"filePath",
"startTime",
"loggerName",
"timestamp",
1170 "jobID",
"jobClass",
"timeSameClass",
"timeAnyClass"};
1175 static const char* kClassKeys[] = {
1176 "name",
"type",
"population",
"refNode",
"priority",
1177 "openOrClosed",
"signalType",
"targetClass",
"removalPolicy",
"removalDistribution",
1178 "isReferenceClass",
"deadline",
"patience",
"impatienceType",
1179 "spawnClass",
"replySignalClass",
"immediateFeedback"};
1180 auto known = [](
const char*
const* tab, std::size_t n,
const std::string& k) {
1181 for (std::size_t i = 0; i < n; ++i)
1182 if (k == tab[i])
return true;
1185 const std::string why =
1186 "', which this reader does not implement. Refusing rather than dropping it: a "
1187 "constraint silently discarded here would make every solver return a confident "
1188 "answer for a different model";
1189 for (
auto it = model.begin(); it != model.end(); ++it)
1190 if (!known(kModelKeys,
sizeof(kModelKeys) /
sizeof(*kModelKeys), it.key()))
1191 throw UnsupportedError(
"network_reader: the model carries '" + it.key() + why);
1192 if (model.contains(
"classes"))
1193 for (
const json& cl : model.at(
"classes"))
1194 for (
auto it = cl.begin(); it != cl.end(); ++it)
1195 if (!known(kClassKeys,
sizeof(kClassKeys) /
sizeof(*kClassKeys), it.key()))
1197 cl.value(
"name", std::string(
"?")) +
"' carries '" +
1199 if (!model.contains(
"nodes"))
return;
1200 for (
const json& nd : model.at(
"nodes"))
1201 for (
auto it = nd.begin(); it != nd.end(); ++it)
1202 if (!known(kNodeKeys,
sizeof(kNodeKeys) /
sizeof(*kNodeKeys), it.key()))
1204 nd.value(
"name", std::string(
"?")) +
"' carries '" +
1221 const json& model = root.contains(
"model") ? root.at(
"model") : root;
1222 const std::string mtype = model.value(
"type", std::string(
"Network"));
1223 if (mtype !=
"Network") {
1227 if (mtype ==
"Environment")
1229 "network_reader: this is an Environment model (a network per stage plus the "
1230 "stage transitions); solve it with -s env, which reads it through "
1231 "environment_reader.h");
1233 "' is not a Network; only Network models are solved by this path");
1246 detail::reject_unconsumed_model_keys(model);
1249 net.
set_log_path(model.value(
"logPath", std::string()));
1257 const json& nodes = model.at(
"nodes");
1258 std::map<std::string, std::size_t> node_idx;
1259 std::vector<std::string> node_type(nodes.size());
1260 for (std::size_t i = 0; i < nodes.size(); ++i)
1261 node_type[i] = nodes[i].at(
"type").get<std::string>();
1264 for (std::size_t i = 0; i < nodes.size(); ++i) {
1265 const json& nd = nodes[i];
1266 const std::string& type = node_type[i];
1272 if (type ==
"ClassSwitch" || type ==
"Cache" || type ==
"Transition")
continue;
1273 const std::string name = nd.at(
"name").get<std::string>();
1274 std::size_t idx = 0;
1275 if (type ==
"Source") {
1277 }
else if (type ==
"Sink") {
1279 }
else if (type ==
"Delay") {
1281 }
else if (type ==
"Queue") {
1283 detail::sched_from_json(nd.value(
"scheduling", std::string(
"FCFS"))));
1284 }
else if (type ==
"Router") {
1286 }
else if (type ==
"Logger" || type ==
"LogTunnel") {
1287 idx = net.
add_logger(name, nd.value(
"fileName", std::string()));
1289 if (nd.contains(
"filePath")) lg.
file_path = nd.at(
"filePath").get<std::string>();
1297 }
else if (type ==
"Place") {
1306 detail::sched_from_json(nd.value(
"scheduling", std::string(
"INF"))));
1307 }
else if (type ==
"Join") {
1309 }
else if (type ==
"Fork") {
1310 idx = net.
add_fork(name, detail::num_value(nd,
"tasksPerLink", 1.0));
1312 throw UnsupportedError(
"network_reader: unsupported node type '" + type +
"' at node '" +
1315 node_idx[name] = idx;
1319 const json& classes = model.at(
"classes");
1320 std::map<std::string, std::size_t> class_idx;
1323 std::vector<std::size_t> signal_classes;
1324 for (std::size_t r = 0; r < classes.size(); ++r) {
1325 const json& cl = classes[r];
1326 const std::string name = cl.at(
"name").get<std::string>();
1327 const std::string type = cl.at(
"type").get<std::string>();
1328 std::size_t idx = 0;
1329 if (type ==
"Open") {
1331 }
else if (type ==
"Closed" || type ==
"SelfLooping") {
1336 const double pop = cl.at(
"population").get<
double>();
1337 const std::string ref = cl.at(
"refNode").get<std::string>();
1338 auto it = node_idx.find(ref);
1339 if (it == node_idx.end())
1340 throw InputError(
"network_reader: class '" + name +
"' references unknown node '" +
1342 idx = type ==
"SelfLooping"
1345 }
else if (type ==
"Signal") {
1351 const std::string kind = cl.value(
"openOrClosed", std::string(
"Open"));
1352 if (kind ==
"Closed") {
1353 const std::string ref = cl.at(
"refNode").get<std::string>();
1354 auto it = node_idx.find(ref);
1355 if (it == node_idx.end())
1356 throw InputError(
"network_reader: signal class '" + name +
1357 "' references unknown node '" + ref +
"'");
1364 cl.value(
"priority", 0));
1368 signal_classes.push_back(r);
1371 "' for class '" + name +
"'");
1382 if (cl.contains(
"deadline")) {
1383 const double due = cl.at(
"deadline").get<
double>();
1388 if (cl.contains(
"patience")) {
1389 const json& pt = cl.at(
"patience");
1394 cl.contains(
"impatienceType")
1395 ? detail::impatience_from_json(cl.at(
"impatienceType").get<std::string>())
1398 class_idx[name] = idx;
1402 for (std::size_t r = 0; r < classes.size(); ++r) {
1403 const json& cl = classes[r];
1404 const std::size_t idx = class_idx.at(cl.at(
"name").get<std::string>());
1405 if (cl.contains(
"spawnClass")) {
1406 const std::string sp = cl.at(
"spawnClass").get<std::string>();
1407 auto sit = class_idx.find(sp);
1408 if (sit == class_idx.end())
1409 throw InputError(
"network_reader: class '" + cl.at(
"name").get<std::string>() +
1410 "' spawns class '" + sp +
"', which the model does not declare");
1415 if (cl.contains(
"replySignalClass")) {
1416 const std::string rp = cl.at(
"replySignalClass").get<std::string>();
1417 auto rit = class_idx.find(rp);
1418 if (rit == class_idx.end())
1419 throw InputError(
"network_reader: class '" + cl.at(
"name").get<std::string>() +
1420 "' replies with class '" + rp +
1421 "', which the model does not declare");
1426 for (std::size_t si = 0; si < signal_classes.size(); ++si) {
1427 const json& cl = classes[signal_classes[si]];
1428 const std::string name = cl.at(
"name").get<std::string>();
1429 const std::string st = cl.value(
"signalType", std::string(
"negative"));
1433 else if (st !=
"negative" && st !=
"NEGATIVE")
1434 throw UnsupportedError(
"network_reader: signal class '" + name +
"' is of type '" + st +
1435 "', and the kinds on the wire are negative, catastrophe and "
1438 const std::string rp = cl.value(
"removalPolicy", std::string(
"RANDOM"));
1441 std::size_t target = 0;
1442 if (cl.contains(
"targetClass")) {
1443 auto tit = class_idx.find(cl.at(
"targetClass").get<std::string>());
1444 if (tit == class_idx.end())
1445 throw InputError(
"network_reader: signal class '" + name +
"' targets class '" +
1446 cl.at(
"targetClass").get<std::string>() +
1447 "', which the model does not declare");
1448 target = tit->second;
1450 std::vector<T> remdist;
1451 if (cl.contains(
"removalDistribution"))
1452 remdist = detail::removal_pmf_from_json<T>(cl.at(
"removalDistribution"));
1453 net.
set_signal(class_idx.at(name), kind, pol, target, remdist);
1455 const std::size_t K = class_idx.size();
1459 for (std::size_t i = 0; i < nodes.size(); ++i) {
1460 const json& nd = nodes[i];
1461 const std::string& type = node_type[i];
1462 if (type !=
"ClassSwitch" && type !=
"Join" && type !=
"Cache" && type !=
"Fork")
continue;
1463 const std::string name = nd.at(
"name").get<std::string>();
1464 std::size_t idx = 0;
1465 if (type ==
"Fork") {
1471 if (!nd.contains(
"fanOutByDest") && !nd.contains(
"fanOutDist") &&
1472 !nd.contains(
"fanOutProb"))
1474 idx = node_idx.at(name);
1475 if (nd.contains(
"fanOutByDest")) {
1476 const json& ovs = nd.at(
"fanOutByDest");
1477 for (std::size_t e = 0; e < ovs.size(); ++e)
1479 ovs[e].at(
"value").get<
double>(),
1480 detail::fork_dest_index(ovs[e], node_idx, name));
1482 if (nd.contains(
"fanOutDist")) {
1483 const json& ovs = nd.at(
"fanOutDist");
1484 for (std::size_t e = 0; e < ovs.size(); ++e) {
1485 const std::vector<double> pv = ovs[e].at(
"p").get<std::vector<double> >();
1486 const std::vector<double> xv = ovs[e].at(
"x").get<std::vector<double> >();
1487 std::vector<T> p, x;
1488 for (std::size_t q = 0; q < pv.size(); ++q)
1490 for (std::size_t q = 0; q < xv.size(); ++q)
1494 detail::fork_dest_index(ovs[e], node_idx, name));
1497 if (nd.contains(
"fanOutProb")) {
1498 const json& ovs = nd.at(
"fanOutProb");
1499 for (std::size_t e = 0; e < ovs.size(); ++e)
1501 idx, ovs[e].at(
"class").get<std::size_t>(),
1502 detail::fork_dest_index(ovs[e], node_idx, name),
1503 ovs[e].at(
"value").get<
double>());
1507 if (type ==
"Cache") {
1515 const json empty_obj = json::object();
1516 const json& cj = nd.contains(
"cache") ? nd.at(
"cache") : empty_obj;
1518 cp.
nitems = detail::has_cache_key(nd, cj,
"numItems")
1519 ? detail::cache_key(nd, cj,
"numItems").get<std::size_t>()
1520 : cj.at(
"items").get<std::size_t>();
1521 cp.
itemcap = detail::has_cache_key(nd, cj,
"itemLevelCap")
1522 ? detail::cache_key(nd, cj,
"itemLevelCap").get<std::vector<int> >()
1523 : cj.at(
"capacity").get<std::vector<int> >();
1526 if (detail::has_cache_key(nd, cj,
"itemSizes"))
1527 cp.
itemsize = detail::cache_key(nd, cj,
"itemSizes").get<std::vector<int> >();
1528 if (detail::has_cache_key(nd, cj,
"costCaps")) {
1529 const json& cc = detail::cache_key(nd, cj,
"costCaps");
1530 if (cc.is_array()) {
1531 cp.
costcap = cc.get<std::vector<int> >();
1538 detail::has_cache_key(nd, cj,
"replacementStrategy")
1539 ? detail::cache_key(nd, cj,
"replacementStrategy").get<std::string>()
1540 : cj.value(
"replacement", std::string(
"RR")));
1541 cp.
pread.assign(K, std::vector<T>());
1546 if (detail::has_cache_key(nd, cj,
"popularity")) {
1547 const json& pop = detail::cache_key(nd, cj,
"popularity");
1548 for (
auto it = pop.begin(); it != pop.end(); ++it) {
1552 if (it.value().value(
"type", std::string()) ==
"Disabled")
continue;
1553 cp.
pread[class_idx.at(it.key()) - 1] =
1554 detail::pmf_from_json<T>(it.value(), cp.
nitems);
1555 cp.
preadkind[class_idx.at(it.key()) - 1] =
1556 detail::popularity_kind_from_json<T>(it.value(), cp.
nitems);
1559 auto fill_switch = [&](
const char* key, std::vector<std::size_t>& dst) {
1560 if (!detail::has_cache_key(nd, cj, key))
return;
1561 const json& blk = detail::cache_key(nd, cj, key);
1562 for (
auto it = blk.begin(); it != blk.end(); ++it)
1563 dst[class_idx.at(it.key()) - 1] = class_idx.at(it.value().get<std::string>());
1565 fill_switch(
"hitClass", cp.
hitclass);
1570 if (detail::has_cache_key(nd, cj,
"itemClass")) {
1571 const json& blk = detail::cache_key(nd, cj,
"itemClass");
1572 for (
auto it = blk.begin(); it != blk.end(); ++it)
1573 cp.
classitem[class_idx.at(it.key()) - 1] =
1574 static_cast<std::size_t
>(it.value().get<double>());
1585 if (detail::has_cache_key(nd, cj,
"admissionProb"))
1587 if (detail::has_cache_key(nd, cj,
"accessProb")) {
1588 const json& ap = detail::cache_key(nd, cj,
"accessProb");
1590 for (
const json& per_class : ap) {
1591 std::vector<Matrix<T> > row;
1592 for (
const json& g : per_class) {
1593 if (g.is_null() || g.empty()) {
1597 row.push_back(detail::mat_from_json<T>(g));
1599 cp.
accost.push_back(row);
1601 }
else if (detail::has_cache_key(nd, cj,
"accessGraph")) {
1607 const std::vector<Matrix<T> > shared =
1608 detail::mat_list_from_json<T>(detail::cache_key(nd, cj,
"accessGraph"));
1609 cp.
accost.assign(K, shared);
1613 if (detail::has_cache_key(nd, cj,
"initialState"))
1614 cp.
initstate = detail::num_vec_from_json<T>(detail::cache_key(nd, cj,
"initialState"));
1624 if (detail::has_cache_key(nd, cj,
"retrievalSystem")) {
1625 const json& rs = detail::cache_key(nd, cj,
"retrievalSystem");
1628 if (rs.contains(
"byClass")) {
1629 for (
auto rc = rs.at(
"byClass").begin(); rc != rs.at(
"byClass").end(); ++rc) {
1630 const std::size_t rdcls = class_idx.at(rc.key());
1631 std::vector<std::size_t> qnodes;
1632 for (
const auto&
qn : rc.value().at(
"queues"))
1633 qnodes.push_back(node_idx.at(
qn.get<std::string>()));
1635 for (
auto it2 = rc.value().at(
"items").begin();
1636 it2 != rc.value().at(
"items").end(); ++it2) {
1637 const std::size_t item = std::stoul(it2.key());
1640 class_idx.at(it2.value().get<std::string>());
1646 node_idx[name] = idx;
1649 if (type ==
"Join") {
1654 if (!nd.contains(
"forkNode"))
1655 throw InputError(
"network_reader: Join '" + name +
1656 "' names no 'forkNode': the document declares no "
1657 "Fork for it to close.");
1658 auto it = node_idx.find(nd.at(
"forkNode").get<std::string>());
1659 if (it == node_idx.end())
1660 throw InputError(
"network_reader: Join '" + name +
1661 "' references unknown fork '" +
1662 nd.at(
"forkNode").get<std::string>() +
"'");
1663 const std::size_t fork = it->second;
1666 idx = node_idx.at(name);
1671 if (nd.contains(
"joinStrategy") || nd.contains(
"joinQuorum")) {
1672 const std::string js = nd.value(
"joinStrategy", std::string(
"STD"));
1673 if (js !=
"STD" && js !=
"PARTIAL")
1674 throw UnsupportedError(
"network_reader: Join '" + name +
"' declares strategy '" +
1675 js +
"', and the rules on the wire are STD and PARTIAL");
1679 nd.value(
"joinQuorum", 0.0));
1688 if (nd.contains(
"classSwitchMatrix") || nd.contains(
"csMatrix")) {
1690 nd.contains(
"classSwitchMatrix") ? nd.at(
"classSwitchMatrix") : nd.at(
"csMatrix");
1691 for (
auto ri = csm.begin(); ri != csm.end(); ++ri) {
1692 const std::size_t rr = class_idx.at(ri.key()) - 1;
1694 for (
auto ci = ri.value().begin(); ci != ri.value().end(); ++ci)
1695 C(rr, class_idx.at(ci.key()) - 1) =
1701 node_idx[name] = idx;
1713 for (std::size_t i = 0; i < nodes.size(); ++i) {
1714 if (node_type[i] !=
"Transition")
continue;
1715 const json& nd = nodes[i];
1716 const std::string name = nd.at(
"name").get<std::string>();
1717 if (!nd.contains(
"modes") || nd.at(
"modes").empty())
1718 throw InputError(
"network_reader: transition '" + name +
"' declares no mode");
1719 const json& modes = nd.at(
"modes");
1721 tp.
nmodes = modes.size();
1722 const std::size_t nn = nodes.size();
1723 const double inf = std::numeric_limits<double>::infinity();
1724 for (std::size_t m = 0; m < modes.size(); ++m) {
1725 const json& mj = modes[m];
1726 tp.
modenames.push_back(mj.value(
"name", std::string(
"Mode") + std::to_string(m + 1)));
1735 tp.
firingdep.push_back(std::function<T(
const std::vector<T>&)>());
1736 if (mj.contains(
"firingRateDependence")) {
1737 const json& frm = mj.at(
"firingRateDependence");
1738 if (!frm.contains(
"slots") || !frm.contains(
"scaling"))
1739 throw InputError(
"network_reader: the marking-dependent firing rate of mode " +
1740 std::to_string(m + 1) +
" of transition '" + name +
1741 "' carries no 'slots'/'scaling' lattice");
1742 std::vector<std::size_t> slot_node;
1743 for (
const json& sm : frm.at(
"slots")) {
1744 const std::string on = sm.at(
"node").get<std::string>();
1745 const std::map<std::string, std::size_t>::const_iterator ni = node_idx.find(on);
1746 if (ni == node_idx.end())
1747 throw InputError(
"network_reader: the marking-dependent firing rate of "
1749 name +
"' reads node '" + on +
1750 "', which the model does not declare");
1751 slot_node.push_back(ni->second - 1);
1753 std::vector<long> cutoffs;
1754 if (frm.contains(
"cutoffs"))
1755 for (
const json& c : frm.at(
"cutoffs")) cutoffs.push_back(c.get<
long>());
1756 std::map<std::string, double> table;
1757 const json& sc = frm.at(
"scaling");
1758 for (json::const_iterator it = sc.begin(); it != sc.end(); ++it)
1759 table[it.key()] = it.value().get<
double>();
1760 tp.
firingdep.back() = [slot_node, cutoffs,
1761 table](
const std::vector<T>& mk) -> T {
1763 for (std::size_t s = 0; s < slot_node.size(); ++s) {
1764 long c = slot_node[s] < mk.size()
1765 ?
static_cast<long>(std::llround(
1769 if (s < cutoffs.size() && c > cutoffs[s]) c = cutoffs[s];
1771 key += std::to_string(c);
1773 const std::map<std::string, double>::const_iterator hit = table.find(key);
1780 const bool immediate =
1781 mj.value(
"timingStrategy", std::string(
"TIMED")) ==
"IMMEDIATE";
1785 if (mj.contains(
"distribution")) proc = detail::dist_from_json<T>(mj.at(
"distribution"));
1789 tp.
nmodeservers.push_back(detail::num_value(mj,
"numServers", 1.0));
1797 tp.
firingprio.push_back(mj.value(
"firingPriority", 1.0));
1799 const std::size_t K = classes.size();
1803 const char* kArcKey[3] = {
"enablingConditions",
"inhibitingConditions",
1805 for (
int which = 0; which < 3; ++which) {
1806 if (!mj.contains(kArcKey[which]))
continue;
1807 for (
const json& arc : mj.at(kArcKey[which])) {
1808 const std::string on = arc.at(
"node").get<std::string>();
1809 const std::map<std::string, std::size_t>::const_iterator ni = node_idx.find(on);
1810 if (ni == node_idx.end())
1811 throw InputError(
"network_reader: transition '" + name +
"' names node '" +
1812 on +
"', which the model does not declare");
1818 if (arc.contains(
"class")) {
1819 const std::string cn = arc.at(
"class").get<std::string>();
1820 const std::map<std::string, std::size_t>::const_iterator ci =
1822 if (ci == class_idx.end())
1823 throw InputError(
"network_reader: transition '" + name +
1824 "' names class '" + cn +
1825 "', which the model does not declare");
1829 const std::size_t q = ni->second - 1;
1830 if (which == 0) enab(q, rr - 1) = enab(q, rr - 1) + cnt;
1831 else if (which == 1) inhib(q, rr - 1) = cnt;
1832 else fire(q, rr - 1) = fire(q, rr - 1) + cnt;
1837 tp.
firing.push_back(fire);
1843 for (std::size_t i = 0; i < nodes.size(); ++i) {
1844 const json& nd = nodes[i];
1845 const std::string& type = node_type[i];
1846 const std::size_t idx = node_idx.at(nd.at(
"name").get<std::string>());
1851 if (nd.contains(
"servers") && (type ==
"Queue" || type ==
"Place"))
1854 if (nd.contains(
"service")) {
1855 const json& svc = nd.at(
"service");
1856 for (
auto it = svc.begin(); it != svc.end(); ++it) {
1857 auto cit = class_idx.find(it.key());
1858 if (cit == class_idx.end())
1859 throw InputError(
"network_reader: service names unknown class '" + it.key() +
1860 "' at node '" + nd.at(
"name").get<std::string>() +
"'");
1862 if (type ==
"Source") net.
set_arrival(idx, cit->second, d);
1867 if (type ==
"Queue" && nd.contains(
"scheduling") &&
1868 nd.at(
"scheduling").get<std::string>() ==
"POLLING") {
1869 if (nd.contains(
"pollingType"))
1870 net.
set_polling_type(idx, detail::polling_from_json(nd.at(
"pollingType").get<std::string>()),
1871 nd.value(
"pollingPar", 0));
1876 if (nd.contains(
"schedParams") && (type ==
"Queue" || type ==
"Delay")) {
1877 const json& sp = nd.at(
"schedParams");
1878 for (
auto it = sp.begin(); it != sp.end(); ++it) {
1879 auto cit = class_idx.find(it.key());
1880 if (cit != class_idx.end())
1892 const bool station_node =
1893 type ==
"Queue" || type ==
"Delay" || type ==
"Join" || type ==
"Place";
1894 if (nd.contains(
"dropRule") && station_node) {
1895 const json& dr = nd.at(
"dropRule");
1896 for (
auto it = dr.begin(); it != dr.end(); ++it) {
1897 auto cit = class_idx.find(it.key());
1898 if (cit != class_idx.end())
1900 detail::drop_from_json(it.value().get<std::string>()));
1903 if (nd.contains(
"buffer") && station_node)
1904 net.
set_capacity(idx, detail::num_from_json(nd.at(
"buffer")));
1906 if (nd.contains(
"classCap")) {
1907 const json& cc = nd.at(
"classCap");
1908 for (
auto it = cc.begin(); it != cc.end(); ++it) {
1909 auto cit = class_idx.find(it.key());
1910 if (cit != class_idx.end())
1922 if (nd.contains(
"patience")) {
1923 const json& pt = nd.at(
"patience");
1924 for (
auto it = pt.begin(); it != pt.end(); ++it) {
1925 auto cit = class_idx.find(it.key());
1926 if (cit == class_idx.end())
continue;
1927 const json& pj = it.value();
1929 pj.contains(
"impatienceType")
1930 ? detail::impatience_from_json(pj.at(
"impatienceType").get<std::string>())
1932 net.
set_patience(idx, cit->second, detail::dist_from_json<T>(pj.at(
"distribution")),
1936 if (nd.contains(
"orbitImpatience")) {
1937 const json& oi = nd.at(
"orbitImpatience");
1938 for (
auto it = oi.begin(); it != oi.end(); ++it) {
1939 auto cit = class_idx.find(it.key());
1940 if (cit != class_idx.end())
1944 if (nd.contains(
"batchRejectProb")) {
1945 const json& br = nd.at(
"batchRejectProb");
1946 for (
auto it = br.begin(); it != br.end(); ++it) {
1947 auto cit = class_idx.find(it.key());
1948 if (cit != class_idx.end())
1953 if (nd.contains(
"balking")) {
1954 const json& bk = nd.at(
"balking");
1955 for (
auto it = bk.begin(); it != bk.end(); ++it) {
1956 auto cit = class_idx.find(it.key());
1957 if (cit == class_idx.end())
continue;
1958 const json& bj = it.value();
1959 std::vector<typename qn::Station<T>::BalkingThreshold> ths;
1960 if (bj.contains(
"thresholds"))
1961 for (
const json& tj : bj.at(
"thresholds")) {
1963 th.
min_jobs = tj.value(
"minJobs", 0.0);
1967 th.
max_jobs = tj.value(
"maxJobs", -1.0);
1972 detail::balking_from_json(bj.at(
"strategy").get<std::string>()), ths);
1975 if (nd.contains(
"retrial")) {
1976 const json& rt = nd.at(
"retrial");
1977 for (
auto it = rt.begin(); it != rt.end(); ++it) {
1978 auto cit = class_idx.find(it.key());
1979 if (cit == class_idx.end())
continue;
1980 const lang::Distrib<T> delay = detail::dist_from_json<T>(it.value().at(
"delay"));
1985 int(detail::num_value(it.value(),
"maxAttempts", 0.0)));
1990 if (nd.contains(
"setupTime")) {
1991 if (!nd.contains(
"delayOffTime"))
1993 "network_reader: node '" + nd.at(
"name").get<std::string>() +
1994 "' declares a setup time with no delay-off time; a server that never powers "
1995 "down never pays the setup, so the pair is meaningless alone");
1996 const json& su = nd.at(
"setupTime");
1997 const json& doff = nd.at(
"delayOffTime");
1998 for (
auto it = su.begin(); it != su.end(); ++it) {
1999 auto cit = class_idx.find(it.key());
2000 if (cit == class_idx.end() || !doff.contains(it.key()))
continue;
2002 detail::dist_from_json<T>(doff.at(it.key())));
2011 if (nd.contains(
"breakdown")) {
2012 const json& bd = nd.at(
"breakdown");
2013 if (!bd.contains(
"failure") || !bd.contains(
"repair"))
2015 "network_reader: node '" + nd.at(
"name").get<std::string>() +
2016 "' declares a breakdown without both a failure and a repair time; a server "
2017 "that never recovers is an absorbing model, not a breakdown");
2018 std::vector<lang::Distrib<T> > down(class_idx.size(),
2020 if (bd.contains(
"downService")) {
2021 const json& ds = bd.at(
"downService");
2022 for (
auto it = ds.begin(); it != ds.end(); ++it) {
2023 auto cit = class_idx.find(it.key());
2024 if (cit == class_idx.end())
continue;
2025 if (cit->second >= 1 && cit->second <= down.size())
2026 down[cit->second - 1] = detail::dist_from_json<T>(it.value());
2029 net.
set_breakdown(idx, detail::dist_from_json<T>(bd.at(
"failure")),
2030 detail::dist_from_json<T>(bd.at(
"repair")), down);
2032 if (nd.contains(
"switchoverTimes")) {
2046 const bool polling = type ==
"Queue" &&
2047 nd.value(
"scheduling", std::string()) ==
"POLLING";
2048 for (
const json& so : nd.at(
"switchoverTimes")) {
2049 auto cit = class_idx.find(so.at(
"from").get<std::string>());
2050 if (cit == class_idx.end())
continue;
2051 const std::string to = so.value(
"to", std::string());
2055 "network_reader: node '" + nd.at(
"name").get<std::string>() +
2056 "' is POLLING-scheduled but declares a switchover that names the "
2057 "class moved TO; a polling switchover is the walk out of one buffer");
2059 detail::dist_from_json<T>(so.at(
"distribution")));
2064 "network_reader: node '" + nd.at(
"name").get<std::string>() +
2065 "' declares a switchover without the class moved TO, which only a "
2066 "POLLING station's per-buffer walk may omit");
2067 auto tit = class_idx.find(to);
2068 if (tit == class_idx.end())
continue;
2070 detail::dist_from_json<T>(so.at(
"distribution")));
2073 if (nd.contains(
"serverTypes")) {
2074 for (
const json& st : nd.at(
"serverTypes")) {
2076 stype.
name = st.value(
"name", std::string());
2077 stype.
count = st.value(
"count", 1.0);
2078 if (st.contains(
"compatibleClasses")) {
2079 stype.
compatible.assign(classes.size(),
false);
2080 for (
const json& cn : st.at(
"compatibleClasses")) {
2081 auto cit = class_idx.find(cn.get<std::string>());
2082 if (cit != class_idx.end()) stype.
compatible[cit->second - 1] =
true;
2085 if (st.contains(
"service")) {
2087 const json& sv = st.at(
"service");
2088 for (
auto it = sv.begin(); it != sv.end(); ++it) {
2089 auto cit = class_idx.find(it.key());
2090 if (cit != class_idx.end())
2091 stype.
service[cit->second - 1] = detail::dist_from_json<T>(it.value());
2096 if (nd.contains(
"heteroSchedPolicy"))
2098 idx, detail::hetero_from_json(nd.at(
"heteroSchedPolicy").get<std::string>()));
2102 if (nd.contains(
"serverParallelism")) {
2103 const json& sp = nd.at(
"serverParallelism");
2104 for (
auto it = sp.begin(); it != sp.end(); ++it) {
2105 auto cit = class_idx.find(it.key());
2106 if (cit != class_idx.end())
2112 if (nd.contains(
"arrivalBatch")) {
2113 const json& ab = nd.at(
"arrivalBatch");
2114 for (
auto it = ab.begin(); it != ab.end(); ++it) {
2115 auto cit = class_idx.find(it.key());
2116 if (cit != class_idx.end())
2120 if (nd.contains(
"markedClasses")) {
2121 std::vector<std::size_t> marks;
2122 for (
const json& cn : nd.at(
"markedClasses")) {
2123 auto cit = class_idx.find(cn.get<std::string>());
2124 if (cit == class_idx.end())
2125 throw InputError(
"network_reader: node '" + nd.at(
"name").get<std::string>() +
2126 "' binds mark " + std::to_string(marks.size() + 1) +
2127 " to class '" + cn.get<std::string>() +
2128 "', which the model does not declare");
2129 marks.push_back(cit->second);
2139 if (nd.contains(
"oiServiceRate")) {
2140 std::vector<int> cut(classes.size(), 10);
2141 if (nd.contains(
"oiCutoffs")) {
2142 const std::vector<double> cv = detail::num_vec_from_json<double>(nd.at(
"oiCutoffs"));
2143 for (std::size_t r = 0; r < cut.size() && r < cv.size(); ++r)
2144 cut[r] =
static_cast<int>(std::lround(cv[r]));
2146 std::vector<std::vector<bool> > swap;
2147 if (nd.contains(
"swapGraph")) {
2148 const Matrix<T> sg = detail::mat_from_json<T>(nd.at(
"swapGraph"));
2149 swap.assign(sg.
rows(), std::vector<bool>(sg.
cols(),
false));
2150 for (std::size_t a = 0; a < sg.
rows(); ++a)
2151 for (std::size_t b = 0; b < sg.
cols(); ++b)
2155 detail::oi_rate_from_json<T>(nd.at(
"oiServiceRate"), cut, classes.size()),
2160 if (nd.contains(
"initialState") && type ==
"Place") {
2162 }
else if (nd.contains(
"initialState") && !nd.contains(
"stateSpace")) {
2171 const std::vector<T> row = detail::num_vec_from_json<T>(nd.at(
"initialState"));
2174 for (std::size_t k = 0; k < row.size(); ++k) space(0, k) = row[k];
2178 if (nd.contains(
"statePrior") || nd.contains(
"stateSpace")) {
2179 if (!nd.contains(
"statePrior") || !nd.contains(
"stateSpace"))
2181 "network_reader: node '" + nd.at(
"name").get<std::string>() +
2182 "' declares one of stateSpace / statePrior without the other; the prior is a "
2183 "distribution over the ROWS of that space and means nothing alone");
2184 net.
set_state_prior(idx, detail::mat_from_json<T>(nd.at(
"stateSpace")),
2185 detail::num_vec_from_json<T>(nd.at(
"statePrior")));
2187 if (nd.contains(
"departureDiscipline")) {
2188 const json& dd = nd.at(
"departureDiscipline");
2189 for (
auto it = dd.begin(); it != dd.end(); ++it) {
2190 auto cit = class_idx.find(it.key());
2191 if (cit != class_idx.end())
2193 idx, cit->second, detail::departure_from_json(it.value().get<std::string>()));
2201 for (
int kind = 0; kind < 3; ++kind) {
2202 static const char* kKeys[] = {
"loadDependence",
"classDependence",
2204 static const char* kTypes[] = {
"loadDependent",
"classDependent",
2206 if (!nd.contains(kKeys[kind]))
continue;
2207 if (type !=
"Queue" && type !=
"Delay")
2209 std::string(
"network_reader: node '") + nd.at(
"name").get<std::string>() +
2210 "' carries '" + kKeys[kind] +
2211 "', which scales a SERVICE rate and is only meaningful at a Queue or a "
2213 const detail::json& blk = nd.at(kKeys[kind]);
2214 if (blk.value(
"type", std::string(kTypes[kind])) != kTypes[kind])
2216 "' at node '" + nd.at(
"name").get<std::string>() +
2217 "' declares type '" +
2218 blk.value(
"type", std::string(
"?")) +
2219 "', which this reader does not implement");
2220 if (!blk.contains(
"scaling"))
2221 throw InputError(std::string(
"network_reader: '") + kKeys[kind] +
2222 "' at node '" + nd.at(
"name").get<std::string>() +
2223 "' carries no 'scaling'");
2230 std::vector<int> cut(classes.size(), 1);
2231 if (blk.contains(
"cutoffs")) {
2232 const std::vector<double> cv = detail::num_vec_from_json<double>(blk.at(
"cutoffs"));
2233 for (std::size_t r = 0; r < cut.size() && r < cv.size(); ++r)
2234 cut[r] =
static_cast<int>(std::lround(cv[r]));
2237 detail::cd_scaling_from_json<T>(blk.at(
"scaling"), cut, classes.size());
2238 const std::vector<T> peak = detail::cd_peak_from_json<T>(blk, blk.at(
"scaling"));
2245 if (nd.contains(
"immediateFeedback")) {
2246 const json& imf = nd.at(
"immediateFeedback");
2247 for (
auto it = imf.begin(); it != imf.end(); ++it) {
2248 auto cit = class_idx.find(it.key());
2249 if (cit != class_idx.end() && it.value().get<
bool>())
2256 const json& routing = model.at(
"routing");
2258 const json& mat = routing.at(
"matrix");
2259 for (
auto ck = mat.begin(); ck != mat.end(); ++ck) {
2261 const std::string key = ck.key();
2262 const std::size_t comma = key.find(
',');
2263 const std::string cs = comma == std::string::npos ? key : key.substr(0, comma);
2264 const std::string cd = comma == std::string::npos ? key : key.substr(comma + 1);
2265 const std::size_t r = class_idx.at(cs);
2266 const std::size_t s = class_idx.at(cd);
2267 for (
auto si = ck.value().begin(); si != ck.value().end(); ++si) {
2268 const std::size_t from = node_idx.at(si.key());
2269 for (
auto di = si.value().begin(); di != si.value().end(); ++di)
2270 P.
set(r, s, from, node_idx.at(di.key()),
2283 if (model.contains(
"routingStrategies")) {
2284 const json& rs = model.at(
"routingStrategies");
2285 for (
auto ni = rs.begin(); ni != rs.end(); ++ni) {
2286 auto nit = node_idx.find(ni.key());
2287 for (
auto ci = ni.value().begin(); ci != ni.value().end(); ++ci) {
2289 detail::routing_from_json(ci.value().get<std::string>());
2297 if (nit == node_idx.end()) {
2299 throw InputError(
"network_reader: routingStrategies names node '" + ni.key() +
2300 "', which the model does not declare");
2303 auto cit = class_idx.find(ci.key());
2304 if (cit == class_idx.end())
continue;
2309 if (model.contains(
"routingWeights")) {
2310 const json& rw = model.at(
"routingWeights");
2311 for (
auto ni = rw.begin(); ni != rw.end(); ++ni) {
2312 auto nit = node_idx.find(ni.key());
2313 if (nit == node_idx.end())
continue;
2314 for (
auto ci = ni.value().begin(); ci != ni.value().end(); ++ci) {
2315 auto cit = class_idx.find(ci.key());
2316 if (cit == class_idx.end())
continue;
2317 std::map<std::size_t, double> w;
2318 for (
auto di = ci.value().begin(); di != ci.value().end(); ++di) {
2319 auto dit = node_idx.find(di.key());
2320 if (dit != node_idx.end()) w[dit->second] = di.value().get<
double>();
2326 if (model.contains(
"routingParams")) {
2327 const json& rp = model.at(
"routingParams");
2328 for (
auto ni = rp.begin(); ni != rp.end(); ++ni) {
2329 auto nit = node_idx.find(ni.key());
2330 if (nit == node_idx.end())
continue;
2331 for (
auto ci = ni.value().begin(); ci != ni.value().end(); ++ci) {
2332 auto cit = class_idx.find(ci.key());
2333 if (cit == class_idx.end() || !ci.value().contains(
"d"))
continue;
2348 if (model.contains(
"stateDepRouting")) {
2349 const json& sd = model.at(
"stateDepRouting");
2350 for (
const char* k : {
"entry",
"departure",
"class",
"branches",
"level",
"C",
"d"})
2351 if (!sd.contains(k))
2352 throw InputError(std::string(
"network_reader: 'stateDepRouting' carries no '") + k +
2354 auto node_of = [&](
const std::string& nm) -> std::size_t {
2355 const std::map<std::string, std::size_t>::const_iterator it = node_idx.find(nm);
2356 if (it == node_idx.end())
2357 throw InputError(
"network_reader: 'stateDepRouting' names node '" + nm +
2358 "', which the model does not declare");
2361 const std::map<std::string, std::size_t>::const_iterator ci =
2362 class_idx.find(sd.at(
"class").get<std::string>());
2363 if (ci == class_idx.end())
2364 throw InputError(
"network_reader: 'stateDepRouting' names class '" +
2365 sd.at(
"class").get<std::string>() +
2366 "', which the model does not declare");
2367 std::vector<std::vector<std::size_t>> branches;
2368 for (
const json& b : sd.at(
"branches")) {
2369 std::vector<std::size_t> centres;
2370 for (
const json& nm : b) centres.push_back(node_of(nm.get<std::string>()));
2371 branches.push_back(centres);
2373 std::vector<std::size_t> level;
2374 for (
const json& v : sd.at(
"level")) level.push_back(
static_cast<std::size_t
>(v.get<
double>()));
2375 std::vector<double> C;
2376 for (
const json& v : sd.at(
"C")) C.push_back(v.get<
double>());
2377 const json& dj = sd.at(
"d");
2378 Matrix<double> d(dj.size(), dj.empty() ? 0 : dj.at(0).size(), 0.0);
2379 for (std::size_t t = 0; t < dj.size(); ++t) {
2380 if (dj.at(t).size() != d.
cols())
2381 throw InputError(
"network_reader: 'stateDepRouting' carries a ragged coefficient "
2383 for (std::size_t b = 0; b < d.
cols(); ++b) d(t, b) = dj.at(t).at(b).get<
double>();
2386 node_of(sd.at(
"departure").get<std::string>()), branches, level, C,
2398 if (model.contains(
"globalDependence")) {
2399 const json& blk = model.at(
"globalDependence");
2400 if (blk.value(
"type", std::string(
"globalDependent")) !=
"globalDependent")
2402 std::string(
"network_reader: 'globalDependence' declares type '") +
2403 blk.value(
"type", std::string(
"?")) +
"', which this reader does not implement");
2404 if (!blk.contains(
"scaling"))
2405 throw InputError(
"network_reader: 'globalDependence' carries no 'scaling'");
2408 std::vector<std::size_t> slot_st, slot_cl;
2409 if (blk.contains(
"slots"))
2410 for (
const json& sm : blk.at(
"slots")) {
2411 auto nit = node_idx.find(sm.at(
"station").get<std::string>());
2412 auto cit = class_idx.find(sm.at(
"class").get<std::string>());
2413 if (nit == node_idx.end() || cit == class_idx.end())
2415 "network_reader: 'globalDependence' names a station or class the model "
2416 "does not declare");
2417 slot_st.push_back(sn0.
nodes[nit->second - 1].station - 1);
2418 slot_cl.push_back(cit->second - 1);
2420 const std::size_t P = slot_st.size();
2421 std::vector<int> cuts(P, 0);
2422 if (blk.contains(
"cutoffs")) {
2423 const std::vector<double> cv = detail::num_vec_from_json<double>(blk.at(
"cutoffs"));
2424 for (std::size_t d = 0; d < P && d < cv.size(); ++d)
2425 cuts[d] =
static_cast<int>(std::lround(cv[d]));
2427 const int wcut = blk.value(
"cutoff", 10);
2428 std::map<std::string, std::vector<T>> tbl;
2429 for (
auto it = blk.at(
"scaling").begin(); it != blk.at(
"scaling").end(); ++it)
2430 tbl[it.key()] = detail::num_vec_from_json<T>(it.value());
2432 if (blk.contains(
"peak")) {
2433 const std::vector<T> pv = detail::num_vec_from_json<T>(blk.at(
"peak"));
2434 for (std::size_t j = 0; j < peak.size() && j < pv.size(); ++j) peak[j] = pv[j];
2438 [slot_st, slot_cl, cuts, tbl,
ones, K, P](
const std::vector<T>& n) {
2440 if (P == 0) key =
"0";
2442 for (std::size_t d = 0; d < P; ++d) {
2445 if (x > cuts[d]) x = cuts[d];
2447 key += std::to_string(x);
2449 typename std::map<std::string, std::vector<T>>::const_iterator it = tbl.find(key);
2450 return it == tbl.end() ?
ones : it->second;
2461 if (model.contains(
"finiteCapacityRegions")) {
2462 for (
const json& rj : model.at(
"finiteCapacityRegions")) {
2463 std::vector<std::size_t> members;
2467 std::vector<double> cap(classes.size(), -1.0);
2468 auto read_class_map = [&](
const json& blk, std::vector<double>& dst) {
2469 for (
auto it = blk.begin(); it != blk.end(); ++it) {
2470 auto cit = class_idx.find(it.key());
2471 if (cit == class_idx.end())
continue;
2472 const double v = it.value().get<
double>();
2473 double& slot = dst[cit->second - 1];
2474 slot = slot == -1.0 ? v : std::min(slot, v);
2477 if (rj.contains(
"classMaxJobs")) read_class_map(rj.at(
"classMaxJobs"), cap);
2478 for (
const json& sj : rj.at(
"stations")) {
2479 auto nit = node_idx.find(sj.at(
"node").get<std::string>());
2480 if (nit == node_idx.end())
2481 throw InputError(
"network_reader: finite capacity region '" +
2482 rj.value(
"name", std::string(
"?")) +
"' names node '" +
2483 sj.at(
"node").get<std::string>() +
2484 "', which the model does not declare");
2485 members.push_back(nit->second);
2486 if (sj.contains(
"classCap")) read_class_map(sj.at(
"classCap"), cap);
2488 std::vector<double> mem(classes.size(), -1.0);
2489 if (rj.contains(
"classMaxMemory")) read_class_map(rj.at(
"classMaxMemory"), mem);
2492 for (
const json& sj : rj.at(
"stations")) {
2493 if (sj.contains(
"classSize"))
2494 for (
auto it = sj.at(
"classSize").begin(); it != sj.at(
"classSize").end(); ++it) {
2495 auto cit = class_idx.find(it.key());
2496 if (cit != class_idx.end())
2497 size[cit->second - 1] =
2500 if (sj.contains(
"classWeight"))
2501 for (
auto it = sj.at(
"classWeight").begin(); it != sj.at(
"classWeight").end();
2503 auto cit = class_idx.find(it.key());
2504 if (cit != class_idx.end())
2505 weight[cit->second - 1] =
2510 if (rj.contains(
"dropRule"))
2511 for (
auto it = rj.at(
"dropRule").begin(); it != rj.at(
"dropRule").end(); ++it) {
2512 auto cit = class_idx.find(it.key());
2513 if (cit != class_idx.end())
2514 rule[cit->second - 1] = detail::drop_from_json(it.value().get<std::string>());
2516 const std::size_t
reg =
2517 net.
add_region(members, cap, rj.value(
"globalMaxJobs", -1.0), rule, mem, size,
2518 rj.value(
"globalMaxMemory", -1.0),
2519 rj.value(
"name", std::string()));
2521 if (rj.contains(
"constraintA") && rj.contains(
"constraintB"))
2523 detail::num_vec_from_json<T>(rj.at(
"constraintB")));
2536 if (model.contains(
"rewards")) {
2537 const std::size_t K = classes.size();
2538 for (
const json& rw : model.at(
"rewards")) {
2539 const std::string nm = rw.at(
"name").get<std::string>();
2540 const std::string kind = rw.at(
"type").get<std::string>();
2541 const std::string node_name = rw.at(
"node").get<std::string>();
2542 auto nit = node_idx.find(node_name);
2543 if (nit == node_idx.end())
2544 throw InputError(
"network_reader: reward '" + nm +
"' names node '" + node_name +
2545 "', which the model does not declare");
2548 std::size_t ist = 0;
2549 for (std::size_t s = 0; s < net.
get_struct().station_to_node.size(); ++s)
2550 if (net.
get_struct().station_to_node[s] == nit->second) ist = s + 1;
2552 throw UnsupportedError(
"network_reader: reward '" + nm +
"' is declared at node '" +
2554 "', which is not a station and so has no job count");
2558 std::size_t cls = 0;
2559 if (rw.contains(
"class")) {
2560 auto cit = class_idx.find(rw.at(
"class").get<std::string>());
2561 if (cit == class_idx.end())
2562 throw InputError(
"network_reader: reward '" + nm +
"' names class '" +
2563 rw.at(
"class").get<std::string>() +
2564 "', which the model does not declare");
2567 const double nservers = net.
get_struct().stations[ist - 1].nservers;
2568 const double cap = net.
get_struct().stations[ist - 1].cap;
2569 const std::size_t base = (ist - 1) * K;
2573 if (kind ==
"QLen") {
2574 net.
set_reward(nm, [base, K, cls](
const std::vector<T>& n) {
2575 if (cls)
return n[base + cls - 1];
2577 for (std::size_t k = 0; k < K; ++k) s = T(s + n[base + k]);
2579 }, kind, nit->second, cls);
2580 }
else if (kind ==
"Util") {
2583 net.
set_reward(nm, [base, K, cls, nservers](
const std::vector<T>& n) {
2585 if (cls) s = n[base + cls - 1];
2587 for (std::size_t k = 0; k < K; ++k) s = T(s + n[base + k]);
2590 }, kind, nit->second, cls);
2591 }
else if (kind ==
"Blocking") {
2592 net.
set_reward(nm, [base, K, cap](
const std::vector<T>& n) {
2594 for (std::size_t k = 0; k < K; ++k) s = T(s + n[base + k]);
2597 }, kind, nit->second, cls);
2600 "network_reader: reward '" + nm +
"' is of type '" + kind +
2601 "', and the reproducible templates are QLen, Util and Blocking; a Custom "
2602 "reward wraps an arbitrary function and is not on the wire at all");
2612 std::ifstream in(path.c_str());
2613 if (!in)
throw InputError(
"network_reader: cannot open " + path);
2617 }
catch (
const detail::json::parse_error& e) {
2618 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.
@ MPH
The MARKED families, MATLAB's ProcessType.m:36-38.
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.
void put(Matrix< double > &A, std::size_t r0, std::size_t c0, const Matrix< double > &S)
A(r0:, c0:) = S.
Conservation laws of a layered queueing network, enumerated from its structure.
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 mmapt(const std::vector< T > &breakpoints, const std::vector< Matrix< T > > &D0segs, const std::vector< std::vector< Matrix< T > > > &Dmarksegs, bool cyclic)
MMAPt(breakpoints, {D0_k}, {{D1^(c)_k}}, cyclic): the marked schedule.
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 bmmapt(const std::vector< T > &breakpoints, const std::vector< Matrix< T > > &D0segs, const std::vector< std::vector< std::vector< Matrix< T > > > > &Dbatchsegs, bool cyclic)
BMMAPt(breakpoints, {D0_k}, {{{D^(c,b)_k}}}, cyclic): the BATCH marked schedule.
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 mpht(const std::vector< T > &breakpoints, const std::vector< std::vector< T > > &alphas, const std::vector< Matrix< T > > &Ssegs, const std::vector< std::vector< std::vector< T > > > &exits, bool cyclic)
MPHt(breakpoints, {alpha_k}, {S_k}, {{s^(c)_k}}, cyclic), stored LOWERED to MMAPt form segment by seg...
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