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");
1041lang::CdScaling<T> cd_scaling_from_json(
const json& tbl,
const std::vector<int>& cutoffs,
1043 std::map<std::string, std::vector<T> > table;
1044 for (
auto it = tbl.begin(); it != tbl.end(); ++it)
1045 table[it.key()] = num_vec_from_json<T>(it.value());
1046 const std::vector<int> cut = cutoffs;
1047 return [table, cut, K](
const std::vector<T>& n) {
1049 for (std::size_t r = 0; r < K; ++r) {
1050 int v = r < n.size()
1051 ?
static_cast<int>(std::lround(num_traits<T>::to_double(n[r])))
1054 if (r < cut.size() && v > cut[r]) v = cut[r];
1056 key += std::to_string(v);
1058 std::vector<T> out(K, num_traits<T>::from_int(1));
1059 typename std::map<std::string, std::vector<T> >::const_iterator it = table.find(key);
1060 if (it == table.end())
return out;
1061 for (std::size_t r = 0; r < K && r < it->second.size(); ++r) out[r] = it->second[r];
1089std::function<T(
const std::vector<std::size_t>&)> oi_rate_from_json(
const json& tbl,
1090 const std::vector<int>& cutoffs,
1092 std::map<std::string, T> table;
1093 for (
auto it = tbl.begin(); it != tbl.end(); ++it)
1094 table[it.key()] = num_traits<T>::from_double(it.value().get<
double>());
1095 const std::vector<int> cut = cutoffs;
1096 return [table, cut, K](
const std::vector<std::size_t>& micro) {
1097 std::vector<int> cnt(K, 0);
1098 for (std::size_t j = 0; j < micro.size(); ++j)
1099 if (micro[j] >= 1 && micro[j] <= K) ++cnt[micro[j] - 1];
1101 for (std::size_t r = 0; r < K; ++r) {
1103 if (r < cut.size() && v > cut[r]) v = cut[r];
1105 key += std::to_string(v);
1107 typename std::map<std::string, T>::const_iterator it = table.find(key);
1108 return it == table.end() ? num_traits<T>::from_int(0) : it->second;
1124std::vector<T> cd_peak_from_json(
const json& blk,
const json& tbl) {
1125 if (blk.contains(
"peak") && !blk.at(
"peak").
empty())
1126 return num_vec_from_json<T>(blk.at(
"peak"));
1128 for (
auto it = tbl.begin(); it != tbl.end(); ++it) {
1129 if (it.key().find_first_not_of(
"0,") == std::string::npos)
continue;
1130 const std::vector<double> row = num_vec_from_json<double>(it.value());
1131 for (
double x : row)
1132 if (std::isfinite(x) && x > bmax) bmax = x;
1134 return std::vector<T>(1, num_traits<T>::from_double(bmax));
1145inline void reject_unconsumed_model_keys(
const json& model) {
1146 static const char* kModelKeys[] = {
"name",
"type",
"nodes",
1147 "classes",
"routing",
"format",
1148 "version",
"rewards",
"finiteCapacityRegions",
1149 "routingStrategies",
"routingWeights",
"routingParams",
1150 "logPath",
"globalDependence",
"stateDepRouting"};
1157 static const char* kNodeKeys[] = {
1158 "name",
"type",
"scheduling",
"servers",
"service",
1159 "buffer",
"capacity",
"dropRule",
"schedParams",
"pollingType",
1160 "pollingPar",
"classSwitchMatrix",
"csMatrix",
"forkNode",
1161 "tasksPerLink",
"fanOutByDest",
"fanOutDist",
"fanOutProb",
1162 "items",
"numItems",
"itemLevelCap",
"popularity",
"replacementStrategy",
1163 "itemSizes",
"costCaps",
"accessProb",
"admissionProb",
"accessGraph",
1165 "cache",
"initialState",
1166 "retrievalSystem",
"queues",
"hitClass",
"missClass",
1167 "immediateFeedback",
"loadDependence",
1168 "classDependence",
"jointDependence",
1169 "modes",
"classCap",
"departureDiscipline",
1170 "oiServiceRate",
"oiCutoffs",
"swapGraph",
1171 "arrivalBatch",
"markedClasses",
1172 "stateSpace",
"statePrior",
"joinStrategy",
"joinQuorum",
1173 "setupTime",
"delayOffTime",
"switchoverTimes",
"breakdown",
1174 "serverTypes",
"heteroSchedPolicy",
"serverParallelism",
1175 "balking",
"retrial",
"patience",
"orbitImpatience",
1178 "fileName",
"filePath",
"startTime",
"loggerName",
"timestamp",
1179 "jobID",
"jobClass",
"timeSameClass",
"timeAnyClass"};
1184 static const char* kClassKeys[] = {
1185 "name",
"type",
"population",
"refNode",
"priority",
1186 "openOrClosed",
"signalType",
"targetClass",
"removalPolicy",
"removalDistribution",
1187 "isReferenceClass",
"deadline",
"patience",
"impatienceType",
1188 "spawnClass",
"replySignalClass",
"immediateFeedback"};
1189 auto known = [](
const char*
const* tab, std::size_t n,
const std::string& k) {
1190 for (std::size_t i = 0; i < n; ++i)
1191 if (k == tab[i])
return true;
1194 const std::string why =
1195 "', which this reader does not implement. Refusing rather than dropping it: a "
1196 "constraint silently discarded here would make every solver return a confident "
1197 "answer for a different model";
1198 for (
auto it = model.begin(); it != model.end(); ++it)
1199 if (!known(kModelKeys,
sizeof(kModelKeys) /
sizeof(*kModelKeys), it.key()))
1200 throw UnsupportedError(
"network_reader: the model carries '" + it.key() + why);
1201 if (model.contains(
"classes"))
1202 for (
const json& cl : model.at(
"classes"))
1203 for (
auto it = cl.begin(); it != cl.end(); ++it)
1204 if (!known(kClassKeys,
sizeof(kClassKeys) /
sizeof(*kClassKeys), it.key()))
1206 cl.value(
"name", std::string(
"?")) +
"' carries '" +
1208 if (!model.contains(
"nodes"))
return;
1209 for (
const json& nd : model.at(
"nodes"))
1210 for (
auto it = nd.begin(); it != nd.end(); ++it)
1211 if (!known(kNodeKeys,
sizeof(kNodeKeys) /
sizeof(*kNodeKeys), it.key()))
1213 nd.value(
"name", std::string(
"?")) +
"' carries '" +
1230 const json& model = root.contains(
"model") ? root.at(
"model") : root;
1231 const std::string mtype = model.value(
"type", std::string(
"Network"));
1232 if (mtype !=
"Network") {
1236 if (mtype ==
"Environment")
1238 "network_reader: this is an Environment model (a network per stage plus the "
1239 "stage transitions); solve it with -s env, which reads it through "
1240 "environment_reader.h");
1242 "' is not a Network; only Network models are solved by this path");
1255 detail::reject_unconsumed_model_keys(model);
1258 net.
set_log_path(model.value(
"logPath", std::string()));
1266 const json& nodes = model.at(
"nodes");
1267 std::map<std::string, std::size_t> node_idx;
1268 std::vector<std::string> node_type(nodes.size());
1269 for (std::size_t i = 0; i < nodes.size(); ++i)
1270 node_type[i] = nodes[i].at(
"type").get<std::string>();
1273 for (std::size_t i = 0; i < nodes.size(); ++i) {
1274 const json& nd = nodes[i];
1275 const std::string& type = node_type[i];
1281 if (type ==
"ClassSwitch" || type ==
"Cache" || type ==
"Transition")
continue;
1282 const std::string name = nd.at(
"name").get<std::string>();
1283 std::size_t idx = 0;
1284 if (type ==
"Source") {
1286 }
else if (type ==
"Sink") {
1288 }
else if (type ==
"Delay") {
1290 }
else if (type ==
"Queue") {
1292 detail::sched_from_json(nd.value(
"scheduling", std::string(
"FCFS"))));
1293 }
else if (type ==
"Router") {
1295 }
else if (type ==
"Logger" || type ==
"LogTunnel") {
1296 idx = net.
add_logger(name, nd.value(
"fileName", std::string()));
1298 if (nd.contains(
"filePath")) lg.
file_path = nd.at(
"filePath").get<std::string>();
1306 }
else if (type ==
"Place") {
1315 detail::sched_from_json(nd.value(
"scheduling", std::string(
"INF"))));
1316 }
else if (type ==
"Join") {
1318 }
else if (type ==
"Fork") {
1319 idx = net.
add_fork(name, detail::num_value(nd,
"tasksPerLink", 1.0));
1321 throw UnsupportedError(
"network_reader: unsupported node type '" + type +
"' at node '" +
1324 node_idx[name] = idx;
1328 const json& classes = model.at(
"classes");
1329 std::map<std::string, std::size_t> class_idx;
1332 std::vector<std::size_t> signal_classes;
1333 for (std::size_t r = 0; r < classes.size(); ++r) {
1334 const json& cl = classes[r];
1335 const std::string name = cl.at(
"name").get<std::string>();
1336 const std::string type = cl.at(
"type").get<std::string>();
1337 std::size_t idx = 0;
1338 if (type ==
"Open") {
1340 }
else if (type ==
"Closed" || type ==
"SelfLooping") {
1345 const double pop = cl.at(
"population").get<
double>();
1346 const std::string ref = cl.at(
"refNode").get<std::string>();
1347 auto it = node_idx.find(ref);
1348 if (it == node_idx.end())
1349 throw InputError(
"network_reader: class '" + name +
"' references unknown node '" +
1351 idx = type ==
"SelfLooping"
1354 }
else if (type ==
"Signal") {
1360 const std::string kind = cl.value(
"openOrClosed", std::string(
"Open"));
1361 if (kind ==
"Closed") {
1362 const std::string ref = cl.at(
"refNode").get<std::string>();
1363 auto it = node_idx.find(ref);
1364 if (it == node_idx.end())
1365 throw InputError(
"network_reader: signal class '" + name +
1366 "' references unknown node '" + ref +
"'");
1373 cl.value(
"priority", 0));
1377 signal_classes.push_back(r);
1380 "' for class '" + name +
"'");
1391 if (cl.contains(
"deadline")) {
1392 const double due = cl.at(
"deadline").get<
double>();
1397 if (cl.contains(
"patience")) {
1398 const json& pt = cl.at(
"patience");
1403 cl.contains(
"impatienceType")
1404 ? detail::impatience_from_json(cl.at(
"impatienceType").get<std::string>())
1407 class_idx[name] = idx;
1411 for (std::size_t r = 0; r < classes.size(); ++r) {
1412 const json& cl = classes[r];
1413 const std::size_t idx = class_idx.at(cl.at(
"name").get<std::string>());
1414 if (cl.contains(
"spawnClass")) {
1415 const std::string sp = cl.at(
"spawnClass").get<std::string>();
1416 auto sit = class_idx.find(sp);
1417 if (sit == class_idx.end())
1418 throw InputError(
"network_reader: class '" + cl.at(
"name").get<std::string>() +
1419 "' spawns class '" + sp +
"', which the model does not declare");
1424 if (cl.contains(
"replySignalClass")) {
1425 const std::string rp = cl.at(
"replySignalClass").get<std::string>();
1426 auto rit = class_idx.find(rp);
1427 if (rit == class_idx.end())
1428 throw InputError(
"network_reader: class '" + cl.at(
"name").get<std::string>() +
1429 "' replies with class '" + rp +
1430 "', which the model does not declare");
1435 for (std::size_t si = 0; si < signal_classes.size(); ++si) {
1436 const json& cl = classes[signal_classes[si]];
1437 const std::string name = cl.at(
"name").get<std::string>();
1438 const std::string st = cl.value(
"signalType", std::string(
"negative"));
1442 else if (st !=
"negative" && st !=
"NEGATIVE")
1443 throw UnsupportedError(
"network_reader: signal class '" + name +
"' is of type '" + st +
1444 "', and the kinds on the wire are negative, catastrophe and "
1447 const std::string rp = cl.value(
"removalPolicy", std::string(
"RANDOM"));
1450 std::size_t target = 0;
1451 if (cl.contains(
"targetClass")) {
1452 auto tit = class_idx.find(cl.at(
"targetClass").get<std::string>());
1453 if (tit == class_idx.end())
1454 throw InputError(
"network_reader: signal class '" + name +
"' targets class '" +
1455 cl.at(
"targetClass").get<std::string>() +
1456 "', which the model does not declare");
1457 target = tit->second;
1459 std::vector<T> remdist;
1460 if (cl.contains(
"removalDistribution"))
1461 remdist = detail::removal_pmf_from_json<T>(cl.at(
"removalDistribution"));
1462 net.
set_signal(class_idx.at(name), kind, pol, target, remdist);
1464 const std::size_t K = class_idx.size();
1468 for (std::size_t i = 0; i < nodes.size(); ++i) {
1469 const json& nd = nodes[i];
1470 const std::string& type = node_type[i];
1471 if (type !=
"ClassSwitch" && type !=
"Join" && type !=
"Cache" && type !=
"Fork")
continue;
1472 const std::string name = nd.at(
"name").get<std::string>();
1473 std::size_t idx = 0;
1474 if (type ==
"Fork") {
1480 if (!nd.contains(
"fanOutByDest") && !nd.contains(
"fanOutDist") &&
1481 !nd.contains(
"fanOutProb"))
1483 idx = node_idx.at(name);
1484 if (nd.contains(
"fanOutByDest")) {
1485 const json& ovs = nd.at(
"fanOutByDest");
1486 for (std::size_t e = 0; e < ovs.size(); ++e)
1488 ovs[e].at(
"value").get<
double>(),
1489 detail::fork_dest_index(ovs[e], node_idx, name));
1491 if (nd.contains(
"fanOutDist")) {
1492 const json& ovs = nd.at(
"fanOutDist");
1493 for (std::size_t e = 0; e < ovs.size(); ++e) {
1494 const std::vector<double> pv = ovs[e].at(
"p").get<std::vector<double> >();
1495 const std::vector<double> xv = ovs[e].at(
"x").get<std::vector<double> >();
1496 std::vector<T> p, x;
1497 for (std::size_t q = 0; q < pv.size(); ++q)
1499 for (std::size_t q = 0; q < xv.size(); ++q)
1503 detail::fork_dest_index(ovs[e], node_idx, name));
1506 if (nd.contains(
"fanOutProb")) {
1507 const json& ovs = nd.at(
"fanOutProb");
1508 for (std::size_t e = 0; e < ovs.size(); ++e)
1510 idx, ovs[e].at(
"class").get<std::size_t>(),
1511 detail::fork_dest_index(ovs[e], node_idx, name),
1512 ovs[e].at(
"value").get<
double>());
1516 if (type ==
"Cache") {
1524 const json empty_obj = json::object();
1525 const json& cj = nd.contains(
"cache") ? nd.at(
"cache") : empty_obj;
1527 cp.
nitems = detail::has_cache_key(nd, cj,
"numItems")
1528 ? detail::cache_key(nd, cj,
"numItems").get<std::size_t>()
1529 : cj.at(
"items").get<std::size_t>();
1530 cp.
itemcap = detail::has_cache_key(nd, cj,
"itemLevelCap")
1531 ? detail::cache_key(nd, cj,
"itemLevelCap").get<std::vector<int> >()
1532 : cj.at(
"capacity").get<std::vector<int> >();
1535 if (detail::has_cache_key(nd, cj,
"itemSizes"))
1536 cp.
itemsize = detail::cache_key(nd, cj,
"itemSizes").get<std::vector<int> >();
1537 if (detail::has_cache_key(nd, cj,
"costCaps")) {
1538 const json& cc = detail::cache_key(nd, cj,
"costCaps");
1539 if (cc.is_array()) {
1540 cp.
costcap = cc.get<std::vector<int> >();
1547 detail::has_cache_key(nd, cj,
"replacementStrategy")
1548 ? detail::cache_key(nd, cj,
"replacementStrategy").get<std::string>()
1549 : cj.value(
"replacement", std::string(
"RR")));
1550 cp.
pread.assign(K, std::vector<T>());
1555 if (detail::has_cache_key(nd, cj,
"popularity")) {
1556 const json& pop = detail::cache_key(nd, cj,
"popularity");
1557 for (
auto it = pop.begin(); it != pop.end(); ++it) {
1561 if (it.value().value(
"type", std::string()) ==
"Disabled")
continue;
1562 cp.
pread[class_idx.at(it.key()) - 1] =
1563 detail::pmf_from_json<T>(it.value(), cp.
nitems);
1564 cp.
preadkind[class_idx.at(it.key()) - 1] =
1565 detail::popularity_kind_from_json<T>(it.value(), cp.
nitems);
1568 auto fill_switch = [&](
const char* key, std::vector<std::size_t>& dst) {
1569 if (!detail::has_cache_key(nd, cj, key))
return;
1570 const json& blk = detail::cache_key(nd, cj, key);
1571 for (
auto it = blk.begin(); it != blk.end(); ++it)
1572 dst[class_idx.at(it.key()) - 1] = class_idx.at(it.value().get<std::string>());
1574 fill_switch(
"hitClass", cp.
hitclass);
1579 if (detail::has_cache_key(nd, cj,
"itemClass")) {
1580 const json& blk = detail::cache_key(nd, cj,
"itemClass");
1581 for (
auto it = blk.begin(); it != blk.end(); ++it)
1582 cp.
classitem[class_idx.at(it.key()) - 1] =
1583 static_cast<std::size_t
>(it.value().get<double>());
1594 if (detail::has_cache_key(nd, cj,
"admissionProb"))
1596 if (detail::has_cache_key(nd, cj,
"accessProb")) {
1597 const json& ap = detail::cache_key(nd, cj,
"accessProb");
1599 for (
const json& per_class : ap) {
1600 std::vector<Matrix<T> > row;
1601 for (
const json& g : per_class) {
1602 if (g.is_null() || g.empty()) {
1606 row.push_back(detail::mat_from_json<T>(g));
1608 cp.
accost.push_back(row);
1610 }
else if (detail::has_cache_key(nd, cj,
"accessGraph")) {
1616 const std::vector<Matrix<T> > shared =
1617 detail::mat_list_from_json<T>(detail::cache_key(nd, cj,
"accessGraph"));
1618 cp.
accost.assign(K, shared);
1622 if (detail::has_cache_key(nd, cj,
"initialState"))
1623 cp.
initstate = detail::num_vec_from_json<T>(detail::cache_key(nd, cj,
"initialState"));
1633 if (detail::has_cache_key(nd, cj,
"retrievalSystem")) {
1634 const json& rs = detail::cache_key(nd, cj,
"retrievalSystem");
1637 if (rs.contains(
"byClass")) {
1638 for (
auto rc = rs.at(
"byClass").begin(); rc != rs.at(
"byClass").end(); ++rc) {
1639 const std::size_t rdcls = class_idx.at(rc.key());
1640 std::vector<std::size_t> qnodes;
1641 for (
const auto&
qn : rc.value().at(
"queues"))
1642 qnodes.push_back(node_idx.at(
qn.get<std::string>()));
1644 for (
auto it2 = rc.value().at(
"items").begin();
1645 it2 != rc.value().at(
"items").end(); ++it2) {
1646 const std::size_t item = std::stoul(it2.key());
1649 class_idx.at(it2.value().get<std::string>());
1655 node_idx[name] = idx;
1658 if (type ==
"Join") {
1663 if (!nd.contains(
"forkNode"))
1664 throw InputError(
"network_reader: Join '" + name +
1665 "' names no 'forkNode': the document declares no "
1666 "Fork for it to close.");
1667 auto it = node_idx.find(nd.at(
"forkNode").get<std::string>());
1668 if (it == node_idx.end())
1669 throw InputError(
"network_reader: Join '" + name +
1670 "' references unknown fork '" +
1671 nd.at(
"forkNode").get<std::string>() +
"'");
1672 const std::size_t fork = it->second;
1675 idx = node_idx.at(name);
1680 if (nd.contains(
"joinStrategy") || nd.contains(
"joinQuorum")) {
1681 const std::string js = nd.value(
"joinStrategy", std::string(
"STD"));
1682 if (js !=
"STD" && js !=
"PARTIAL")
1683 throw UnsupportedError(
"network_reader: Join '" + name +
"' declares strategy '" +
1684 js +
"', and the rules on the wire are STD and PARTIAL");
1688 nd.value(
"joinQuorum", 0.0));
1697 if (nd.contains(
"classSwitchMatrix") || nd.contains(
"csMatrix")) {
1699 nd.contains(
"classSwitchMatrix") ? nd.at(
"classSwitchMatrix") : nd.at(
"csMatrix");
1700 for (
auto ri = csm.begin(); ri != csm.end(); ++ri) {
1701 const std::size_t rr = class_idx.at(ri.key()) - 1;
1703 for (
auto ci = ri.value().begin(); ci != ri.value().end(); ++ci)
1704 C(rr, class_idx.at(ci.key()) - 1) =
1710 node_idx[name] = idx;
1722 for (std::size_t i = 0; i < nodes.size(); ++i) {
1723 if (node_type[i] !=
"Transition")
continue;
1724 const json& nd = nodes[i];
1725 const std::string name = nd.at(
"name").get<std::string>();
1726 if (!nd.contains(
"modes") || nd.at(
"modes").empty())
1727 throw InputError(
"network_reader: transition '" + name +
"' declares no mode");
1728 const json& modes = nd.at(
"modes");
1730 tp.
nmodes = modes.size();
1731 const std::size_t nn = nodes.size();
1732 const double inf = std::numeric_limits<double>::infinity();
1733 for (std::size_t m = 0; m < modes.size(); ++m) {
1734 const json& mj = modes[m];
1735 tp.
modenames.push_back(mj.value(
"name", std::string(
"Mode") + std::to_string(m + 1)));
1744 tp.
firingdep.push_back(std::function<T(
const std::vector<T>&)>());
1745 if (mj.contains(
"firingRateDependence")) {
1746 const json& frm = mj.at(
"firingRateDependence");
1747 if (!frm.contains(
"slots") || !frm.contains(
"scaling"))
1748 throw InputError(
"network_reader: the marking-dependent firing rate of mode " +
1749 std::to_string(m + 1) +
" of transition '" + name +
1750 "' carries no 'slots'/'scaling' lattice");
1751 std::vector<std::size_t> slot_node;
1752 for (
const json& sm : frm.at(
"slots")) {
1753 const std::string on = sm.at(
"node").get<std::string>();
1754 const std::map<std::string, std::size_t>::const_iterator ni = node_idx.find(on);
1755 if (ni == node_idx.end())
1756 throw InputError(
"network_reader: the marking-dependent firing rate of "
1758 name +
"' reads node '" + on +
1759 "', which the model does not declare");
1760 slot_node.push_back(ni->second - 1);
1762 std::vector<long> cutoffs;
1763 if (frm.contains(
"cutoffs"))
1764 for (
const json& c : frm.at(
"cutoffs")) cutoffs.push_back(c.get<
long>());
1765 std::map<std::string, double> table;
1766 const json& sc = frm.at(
"scaling");
1767 for (json::const_iterator it = sc.begin(); it != sc.end(); ++it)
1768 table[it.key()] = it.value().get<
double>();
1769 tp.
firingdep.back() = [slot_node, cutoffs,
1770 table](
const std::vector<T>& mk) -> T {
1772 for (std::size_t s = 0; s < slot_node.size(); ++s) {
1773 long c = slot_node[s] < mk.size()
1774 ?
static_cast<long>(std::llround(
1778 if (s < cutoffs.size() && c > cutoffs[s]) c = cutoffs[s];
1780 key += std::to_string(c);
1782 const std::map<std::string, double>::const_iterator hit = table.find(key);
1789 const bool immediate =
1790 mj.value(
"timingStrategy", std::string(
"TIMED")) ==
"IMMEDIATE";
1794 if (mj.contains(
"distribution")) proc = detail::dist_from_json<T>(mj.at(
"distribution"));
1798 tp.
nmodeservers.push_back(detail::num_value(mj,
"numServers", 1.0));
1806 tp.
firingprio.push_back(mj.value(
"firingPriority", 1.0));
1808 const std::size_t K = classes.size();
1812 const char* kArcKey[3] = {
"enablingConditions",
"inhibitingConditions",
1814 for (
int which = 0; which < 3; ++which) {
1815 if (!mj.contains(kArcKey[which]))
continue;
1816 for (
const json& arc : mj.at(kArcKey[which])) {
1817 const std::string on = arc.at(
"node").get<std::string>();
1818 const std::map<std::string, std::size_t>::const_iterator ni = node_idx.find(on);
1819 if (ni == node_idx.end())
1820 throw InputError(
"network_reader: transition '" + name +
"' names node '" +
1821 on +
"', which the model does not declare");
1827 if (arc.contains(
"class")) {
1828 const std::string cn = arc.at(
"class").get<std::string>();
1829 const std::map<std::string, std::size_t>::const_iterator ci =
1831 if (ci == class_idx.end())
1832 throw InputError(
"network_reader: transition '" + name +
1833 "' names class '" + cn +
1834 "', which the model does not declare");
1838 const std::size_t q = ni->second - 1;
1839 if (which == 0) enab(q, rr - 1) = enab(q, rr - 1) + cnt;
1840 else if (which == 1) inhib(q, rr - 1) = cnt;
1841 else fire(q, rr - 1) = fire(q, rr - 1) + cnt;
1846 tp.
firing.push_back(fire);
1852 for (std::size_t i = 0; i < nodes.size(); ++i) {
1853 const json& nd = nodes[i];
1854 const std::string& type = node_type[i];
1855 const std::size_t idx = node_idx.at(nd.at(
"name").get<std::string>());
1860 if (nd.contains(
"servers") && (type ==
"Queue" || type ==
"Place"))
1863 if (nd.contains(
"service")) {
1864 const json& svc = nd.at(
"service");
1865 for (
auto it = svc.begin(); it != svc.end(); ++it) {
1866 auto cit = class_idx.find(it.key());
1867 if (cit == class_idx.end())
1868 throw InputError(
"network_reader: service names unknown class '" + it.key() +
1869 "' at node '" + nd.at(
"name").get<std::string>() +
"'");
1871 if (type ==
"Source") net.
set_arrival(idx, cit->second, d);
1876 if (type ==
"Queue" && nd.contains(
"scheduling") &&
1877 nd.at(
"scheduling").get<std::string>() ==
"POLLING") {
1878 if (nd.contains(
"pollingType"))
1879 net.
set_polling_type(idx, detail::polling_from_json(nd.at(
"pollingType").get<std::string>()),
1880 nd.value(
"pollingPar", 0));
1885 if (nd.contains(
"schedParams") && (type ==
"Queue" || type ==
"Delay")) {
1886 const json& sp = nd.at(
"schedParams");
1887 for (
auto it = sp.begin(); it != sp.end(); ++it) {
1888 auto cit = class_idx.find(it.key());
1889 if (cit != class_idx.end())
1901 const bool station_node =
1902 type ==
"Queue" || type ==
"Delay" || type ==
"Join" || type ==
"Place";
1903 if (nd.contains(
"dropRule") && station_node) {
1904 const json& dr = nd.at(
"dropRule");
1905 for (
auto it = dr.begin(); it != dr.end(); ++it) {
1906 auto cit = class_idx.find(it.key());
1907 if (cit != class_idx.end())
1909 detail::drop_from_json(it.value().get<std::string>()));
1912 if (nd.contains(
"buffer") && station_node)
1913 net.
set_capacity(idx, detail::num_from_json(nd.at(
"buffer")));
1915 if (nd.contains(
"classCap")) {
1916 const json& cc = nd.at(
"classCap");
1917 for (
auto it = cc.begin(); it != cc.end(); ++it) {
1918 auto cit = class_idx.find(it.key());
1919 if (cit != class_idx.end())
1931 if (nd.contains(
"patience")) {
1932 const json& pt = nd.at(
"patience");
1933 for (
auto it = pt.begin(); it != pt.end(); ++it) {
1934 auto cit = class_idx.find(it.key());
1935 if (cit == class_idx.end())
continue;
1936 const json& pj = it.value();
1938 pj.contains(
"impatienceType")
1939 ? detail::impatience_from_json(pj.at(
"impatienceType").get<std::string>())
1941 net.
set_patience(idx, cit->second, detail::dist_from_json<T>(pj.at(
"distribution")),
1945 if (nd.contains(
"orbitImpatience")) {
1946 const json& oi = nd.at(
"orbitImpatience");
1947 for (
auto it = oi.begin(); it != oi.end(); ++it) {
1948 auto cit = class_idx.find(it.key());
1949 if (cit != class_idx.end())
1953 if (nd.contains(
"batchRejectProb")) {
1954 const json& br = nd.at(
"batchRejectProb");
1955 for (
auto it = br.begin(); it != br.end(); ++it) {
1956 auto cit = class_idx.find(it.key());
1957 if (cit != class_idx.end())
1962 if (nd.contains(
"balking")) {
1963 const json& bk = nd.at(
"balking");
1964 for (
auto it = bk.begin(); it != bk.end(); ++it) {
1965 auto cit = class_idx.find(it.key());
1966 if (cit == class_idx.end())
continue;
1967 const json& bj = it.value();
1968 std::vector<typename qn::Station<T>::BalkingThreshold> ths;
1969 if (bj.contains(
"thresholds"))
1970 for (
const json& tj : bj.at(
"thresholds")) {
1972 th.
min_jobs = tj.value(
"minJobs", 0.0);
1976 th.
max_jobs = tj.value(
"maxJobs", -1.0);
1981 detail::balking_from_json(bj.at(
"strategy").get<std::string>()), ths);
1984 if (nd.contains(
"retrial")) {
1985 const json& rt = nd.at(
"retrial");
1986 for (
auto it = rt.begin(); it != rt.end(); ++it) {
1987 auto cit = class_idx.find(it.key());
1988 if (cit == class_idx.end())
continue;
1989 const lang::Distrib<T> delay = detail::dist_from_json<T>(it.value().at(
"delay"));
1994 int(detail::num_value(it.value(),
"maxAttempts", 0.0)));
1999 if (nd.contains(
"setupTime")) {
2000 if (!nd.contains(
"delayOffTime"))
2002 "network_reader: node '" + nd.at(
"name").get<std::string>() +
2003 "' declares a setup time with no delay-off time; a server that never powers "
2004 "down never pays the setup, so the pair is meaningless alone");
2005 const json& su = nd.at(
"setupTime");
2006 const json& doff = nd.at(
"delayOffTime");
2007 for (
auto it = su.begin(); it != su.end(); ++it) {
2008 auto cit = class_idx.find(it.key());
2009 if (cit == class_idx.end() || !doff.contains(it.key()))
continue;
2011 detail::dist_from_json<T>(doff.at(it.key())));
2020 if (nd.contains(
"breakdown")) {
2021 const json& bd = nd.at(
"breakdown");
2022 if (!bd.contains(
"failure") || !bd.contains(
"repair"))
2024 "network_reader: node '" + nd.at(
"name").get<std::string>() +
2025 "' declares a breakdown without both a failure and a repair time; a server "
2026 "that never recovers is an absorbing model, not a breakdown");
2027 std::vector<lang::Distrib<T> > down(class_idx.size(),
2029 if (bd.contains(
"downService")) {
2030 const json& ds = bd.at(
"downService");
2031 for (
auto it = ds.begin(); it != ds.end(); ++it) {
2032 auto cit = class_idx.find(it.key());
2033 if (cit == class_idx.end())
continue;
2034 if (cit->second >= 1 && cit->second <= down.size())
2035 down[cit->second - 1] = detail::dist_from_json<T>(it.value());
2038 net.
set_breakdown(idx, detail::dist_from_json<T>(bd.at(
"failure")),
2039 detail::dist_from_json<T>(bd.at(
"repair")), down);
2041 if (nd.contains(
"switchoverTimes")) {
2055 const bool polling = type ==
"Queue" &&
2056 nd.value(
"scheduling", std::string()) ==
"POLLING";
2057 for (
const json& so : nd.at(
"switchoverTimes")) {
2058 auto cit = class_idx.find(so.at(
"from").get<std::string>());
2059 if (cit == class_idx.end())
continue;
2060 const std::string to = so.value(
"to", std::string());
2064 "network_reader: node '" + nd.at(
"name").get<std::string>() +
2065 "' is POLLING-scheduled but declares a switchover that names the "
2066 "class moved TO; a polling switchover is the walk out of one buffer");
2068 detail::dist_from_json<T>(so.at(
"distribution")));
2073 "network_reader: node '" + nd.at(
"name").get<std::string>() +
2074 "' declares a switchover without the class moved TO, which only a "
2075 "POLLING station's per-buffer walk may omit");
2076 auto tit = class_idx.find(to);
2077 if (tit == class_idx.end())
continue;
2079 detail::dist_from_json<T>(so.at(
"distribution")));
2082 if (nd.contains(
"serverTypes")) {
2083 for (
const json& st : nd.at(
"serverTypes")) {
2085 stype.
name = st.value(
"name", std::string());
2086 stype.
count = st.value(
"count", 1.0);
2087 if (st.contains(
"compatibleClasses")) {
2088 stype.
compatible.assign(classes.size(),
false);
2089 for (
const json& cn : st.at(
"compatibleClasses")) {
2090 auto cit = class_idx.find(cn.get<std::string>());
2091 if (cit != class_idx.end()) stype.
compatible[cit->second - 1] =
true;
2094 if (st.contains(
"service")) {
2096 const json& sv = st.at(
"service");
2097 for (
auto it = sv.begin(); it != sv.end(); ++it) {
2098 auto cit = class_idx.find(it.key());
2099 if (cit != class_idx.end())
2100 stype.
service[cit->second - 1] = detail::dist_from_json<T>(it.value());
2105 if (nd.contains(
"heteroSchedPolicy"))
2107 idx, detail::hetero_from_json(nd.at(
"heteroSchedPolicy").get<std::string>()));
2111 if (nd.contains(
"serverParallelism")) {
2112 const json& sp = nd.at(
"serverParallelism");
2113 for (
auto it = sp.begin(); it != sp.end(); ++it) {
2114 auto cit = class_idx.find(it.key());
2115 if (cit != class_idx.end())
2121 if (nd.contains(
"arrivalBatch")) {
2122 const json& ab = nd.at(
"arrivalBatch");
2123 for (
auto it = ab.begin(); it != ab.end(); ++it) {
2124 auto cit = class_idx.find(it.key());
2125 if (cit != class_idx.end())
2129 if (nd.contains(
"markedClasses")) {
2130 std::vector<std::size_t> marks;
2131 for (
const json& cn : nd.at(
"markedClasses")) {
2132 auto cit = class_idx.find(cn.get<std::string>());
2133 if (cit == class_idx.end())
2134 throw InputError(
"network_reader: node '" + nd.at(
"name").get<std::string>() +
2135 "' binds mark " + std::to_string(marks.size() + 1) +
2136 " to class '" + cn.get<std::string>() +
2137 "', which the model does not declare");
2138 marks.push_back(cit->second);
2152 if (nd.contains(
"oiServiceRate")) {
2153 std::vector<int> cut;
2154 if (nd.contains(
"oiCutoffs")) {
2155 const std::vector<double> cv = detail::num_vec_from_json<double>(nd.at(
"oiCutoffs"));
2156 for (std::size_t r = 0; r < classes.size() && r < cv.size(); ++r)
2157 cut.push_back(
static_cast<int>(std::lround(cv[r])));
2159 std::vector<std::vector<bool> > swap;
2160 if (nd.contains(
"swapGraph")) {
2161 const Matrix<T> sg = detail::mat_from_json<T>(nd.at(
"swapGraph"));
2162 swap.assign(sg.
rows(), std::vector<bool>(sg.
cols(),
false));
2163 for (std::size_t a = 0; a < sg.
rows(); ++a)
2164 for (std::size_t b = 0; b < sg.
cols(); ++b)
2168 detail::oi_rate_from_json<T>(nd.at(
"oiServiceRate"), cut, classes.size()),
2173 if (nd.contains(
"initialState") && type ==
"Place") {
2175 }
else if (nd.contains(
"initialState") && !nd.contains(
"stateSpace")) {
2184 const std::vector<T> row = detail::num_vec_from_json<T>(nd.at(
"initialState"));
2187 for (std::size_t k = 0; k < row.size(); ++k) space(0, k) = row[k];
2191 if (nd.contains(
"statePrior") || nd.contains(
"stateSpace")) {
2192 if (!nd.contains(
"statePrior") || !nd.contains(
"stateSpace"))
2194 "network_reader: node '" + nd.at(
"name").get<std::string>() +
2195 "' declares one of stateSpace / statePrior without the other; the prior is a "
2196 "distribution over the ROWS of that space and means nothing alone");
2197 net.
set_state_prior(idx, detail::mat_from_json<T>(nd.at(
"stateSpace")),
2198 detail::num_vec_from_json<T>(nd.at(
"statePrior")));
2200 if (nd.contains(
"departureDiscipline")) {
2201 const json& dd = nd.at(
"departureDiscipline");
2202 for (
auto it = dd.begin(); it != dd.end(); ++it) {
2203 auto cit = class_idx.find(it.key());
2204 if (cit != class_idx.end())
2206 idx, cit->second, detail::departure_from_json(it.value().get<std::string>()));
2214 for (
int kind = 0; kind < 3; ++kind) {
2215 static const char* kKeys[] = {
"loadDependence",
"classDependence",
2217 static const char* kTypes[] = {
"loadDependent",
"classDependent",
2219 if (!nd.contains(kKeys[kind]))
continue;
2220 if (type !=
"Queue" && type !=
"Delay")
2222 std::string(
"network_reader: node '") + nd.at(
"name").get<std::string>() +
2223 "' carries '" + kKeys[kind] +
2224 "', which scales a SERVICE rate and is only meaningful at a Queue or a "
2226 const detail::json& blk = nd.at(kKeys[kind]);
2227 if (blk.value(
"type", std::string(kTypes[kind])) != kTypes[kind])
2229 "' at node '" + nd.at(
"name").get<std::string>() +
2230 "' declares type '" +
2231 blk.value(
"type", std::string(
"?")) +
2232 "', which this reader does not implement");
2233 if (!blk.contains(
"scaling"))
2234 throw InputError(std::string(
"network_reader: '") + kKeys[kind] +
2235 "' at node '" + nd.at(
"name").get<std::string>() +
2236 "' carries no 'scaling'");
2246 std::vector<int> cut;
2247 if (blk.contains(
"cutoffs")) {
2248 const std::vector<double> cv = detail::num_vec_from_json<double>(blk.at(
"cutoffs"));
2249 for (std::size_t r = 0; r < classes.size() && r < cv.size(); ++r)
2250 cut.push_back(
static_cast<int>(std::lround(cv[r])));
2253 detail::cd_scaling_from_json<T>(blk.at(
"scaling"), cut, classes.size());
2254 const std::vector<T> peak = detail::cd_peak_from_json<T>(blk, blk.at(
"scaling"));
2261 if (nd.contains(
"immediateFeedback")) {
2262 const json& imf = nd.at(
"immediateFeedback");
2263 for (
auto it = imf.begin(); it != imf.end(); ++it) {
2264 auto cit = class_idx.find(it.key());
2265 if (cit != class_idx.end() && it.value().get<
bool>())
2272 const json& routing = model.at(
"routing");
2274 const json& mat = routing.at(
"matrix");
2275 for (
auto ck = mat.begin(); ck != mat.end(); ++ck) {
2277 const std::string key = ck.key();
2278 const std::size_t comma = key.find(
',');
2279 const std::string cs = comma == std::string::npos ? key : key.substr(0, comma);
2280 const std::string cd = comma == std::string::npos ? key : key.substr(comma + 1);
2281 const std::size_t r = class_idx.at(cs);
2282 const std::size_t s = class_idx.at(cd);
2283 for (
auto si = ck.value().begin(); si != ck.value().end(); ++si) {
2284 const std::size_t from = node_idx.at(si.key());
2285 for (
auto di = si.value().begin(); di != si.value().end(); ++di)
2286 P.
set(r, s, from, node_idx.at(di.key()),
2299 if (model.contains(
"routingStrategies")) {
2300 const json& rs = model.at(
"routingStrategies");
2301 for (
auto ni = rs.begin(); ni != rs.end(); ++ni) {
2302 auto nit = node_idx.find(ni.key());
2303 for (
auto ci = ni.value().begin(); ci != ni.value().end(); ++ci) {
2305 detail::routing_from_json(ci.value().get<std::string>());
2313 if (nit == node_idx.end()) {
2315 throw InputError(
"network_reader: routingStrategies names node '" + ni.key() +
2316 "', which the model does not declare");
2319 auto cit = class_idx.find(ci.key());
2320 if (cit == class_idx.end())
continue;
2325 if (model.contains(
"routingWeights")) {
2326 const json& rw = model.at(
"routingWeights");
2327 for (
auto ni = rw.begin(); ni != rw.end(); ++ni) {
2328 auto nit = node_idx.find(ni.key());
2329 if (nit == node_idx.end())
continue;
2330 for (
auto ci = ni.value().begin(); ci != ni.value().end(); ++ci) {
2331 auto cit = class_idx.find(ci.key());
2332 if (cit == class_idx.end())
continue;
2333 std::map<std::size_t, double> w;
2334 for (
auto di = ci.value().begin(); di != ci.value().end(); ++di) {
2335 auto dit = node_idx.find(di.key());
2336 if (dit != node_idx.end()) w[dit->second] = di.value().get<
double>();
2342 if (model.contains(
"routingParams")) {
2343 const json& rp = model.at(
"routingParams");
2344 for (
auto ni = rp.begin(); ni != rp.end(); ++ni) {
2345 auto nit = node_idx.find(ni.key());
2346 if (nit == node_idx.end())
continue;
2347 for (
auto ci = ni.value().begin(); ci != ni.value().end(); ++ci) {
2348 auto cit = class_idx.find(ci.key());
2349 if (cit == class_idx.end() || !ci.value().contains(
"d"))
continue;
2364 if (model.contains(
"stateDepRouting")) {
2365 const json& sd = model.at(
"stateDepRouting");
2366 for (
const char* k : {
"entry",
"departure",
"class",
"branches",
"level",
"C",
"d"})
2367 if (!sd.contains(k))
2368 throw InputError(std::string(
"network_reader: 'stateDepRouting' carries no '") + k +
2370 auto node_of = [&](
const std::string& nm) -> std::size_t {
2371 const std::map<std::string, std::size_t>::const_iterator it = node_idx.find(nm);
2372 if (it == node_idx.end())
2373 throw InputError(
"network_reader: 'stateDepRouting' names node '" + nm +
2374 "', which the model does not declare");
2377 const std::map<std::string, std::size_t>::const_iterator ci =
2378 class_idx.find(sd.at(
"class").get<std::string>());
2379 if (ci == class_idx.end())
2380 throw InputError(
"network_reader: 'stateDepRouting' names class '" +
2381 sd.at(
"class").get<std::string>() +
2382 "', which the model does not declare");
2383 std::vector<std::vector<std::size_t>> branches;
2384 for (
const json& b : sd.at(
"branches")) {
2385 std::vector<std::size_t> centres;
2386 for (
const json& nm : b) centres.push_back(node_of(nm.get<std::string>()));
2387 branches.push_back(centres);
2389 std::vector<std::size_t> level;
2390 for (
const json& v : sd.at(
"level")) level.push_back(
static_cast<std::size_t
>(v.get<
double>()));
2391 std::vector<double> C;
2392 for (
const json& v : sd.at(
"C")) C.push_back(v.get<
double>());
2393 const json& dj = sd.at(
"d");
2394 Matrix<double> d(dj.size(), dj.empty() ? 0 : dj.at(0).size(), 0.0);
2395 for (std::size_t t = 0; t < dj.size(); ++t) {
2396 if (dj.at(t).size() != d.
cols())
2397 throw InputError(
"network_reader: 'stateDepRouting' carries a ragged coefficient "
2399 for (std::size_t b = 0; b < d.
cols(); ++b) d(t, b) = dj.at(t).at(b).get<
double>();
2402 node_of(sd.at(
"departure").get<std::string>()), branches, level, C,
2414 if (model.contains(
"globalDependence")) {
2415 const json& blk = model.at(
"globalDependence");
2416 if (blk.value(
"type", std::string(
"globalDependent")) !=
"globalDependent")
2418 std::string(
"network_reader: 'globalDependence' declares type '") +
2419 blk.value(
"type", std::string(
"?")) +
"', which this reader does not implement");
2420 if (!blk.contains(
"scaling"))
2421 throw InputError(
"network_reader: 'globalDependence' carries no 'scaling'");
2424 std::vector<std::size_t> slot_st, slot_cl;
2425 if (blk.contains(
"slots"))
2426 for (
const json& sm : blk.at(
"slots")) {
2427 auto nit = node_idx.find(sm.at(
"station").get<std::string>());
2428 auto cit = class_idx.find(sm.at(
"class").get<std::string>());
2429 if (nit == node_idx.end() || cit == class_idx.end())
2431 "network_reader: 'globalDependence' names a station or class the model "
2432 "does not declare");
2433 slot_st.push_back(sn0.
nodes[nit->second - 1].station - 1);
2434 slot_cl.push_back(cit->second - 1);
2436 const std::size_t P = slot_st.size();
2437 std::vector<int> cuts(P, 0);
2438 if (blk.contains(
"cutoffs")) {
2439 const std::vector<double> cv = detail::num_vec_from_json<double>(blk.at(
"cutoffs"));
2440 for (std::size_t d = 0; d < P && d < cv.size(); ++d)
2441 cuts[d] =
static_cast<int>(std::lround(cv[d]));
2443 const int wcut = blk.value(
"cutoff", 10);
2444 std::map<std::string, std::vector<T>> tbl;
2445 for (
auto it = blk.at(
"scaling").begin(); it != blk.at(
"scaling").end(); ++it)
2446 tbl[it.key()] = detail::num_vec_from_json<T>(it.value());
2448 if (blk.contains(
"peak")) {
2449 const std::vector<T> pv = detail::num_vec_from_json<T>(blk.at(
"peak"));
2450 for (std::size_t j = 0; j < peak.size() && j < pv.size(); ++j) peak[j] = pv[j];
2454 [slot_st, slot_cl, cuts, tbl,
ones, K, P](
const std::vector<T>& n) {
2456 if (P == 0) key =
"0";
2458 for (std::size_t d = 0; d < P; ++d) {
2461 if (x > cuts[d]) x = cuts[d];
2463 key += std::to_string(x);
2465 typename std::map<std::string, std::vector<T>>::const_iterator it = tbl.find(key);
2466 return it == tbl.end() ?
ones : it->second;
2477 if (model.contains(
"finiteCapacityRegions")) {
2478 for (
const json& rj : model.at(
"finiteCapacityRegions")) {
2479 std::vector<std::size_t> members;
2483 std::vector<double> cap(classes.size(), -1.0);
2484 auto read_class_map = [&](
const json& blk, std::vector<double>& dst) {
2485 for (
auto it = blk.begin(); it != blk.end(); ++it) {
2486 auto cit = class_idx.find(it.key());
2487 if (cit == class_idx.end())
continue;
2488 const double v = it.value().get<
double>();
2489 double& slot = dst[cit->second - 1];
2490 slot = slot == -1.0 ? v : std::min(slot, v);
2493 if (rj.contains(
"classMaxJobs")) read_class_map(rj.at(
"classMaxJobs"), cap);
2494 for (
const json& sj : rj.at(
"stations")) {
2495 auto nit = node_idx.find(sj.at(
"node").get<std::string>());
2496 if (nit == node_idx.end())
2497 throw InputError(
"network_reader: finite capacity region '" +
2498 rj.value(
"name", std::string(
"?")) +
"' names node '" +
2499 sj.at(
"node").get<std::string>() +
2500 "', which the model does not declare");
2501 members.push_back(nit->second);
2502 if (sj.contains(
"classCap")) read_class_map(sj.at(
"classCap"), cap);
2504 std::vector<double> mem(classes.size(), -1.0);
2505 if (rj.contains(
"classMaxMemory")) read_class_map(rj.at(
"classMaxMemory"), mem);
2508 for (
const json& sj : rj.at(
"stations")) {
2509 if (sj.contains(
"classSize"))
2510 for (
auto it = sj.at(
"classSize").begin(); it != sj.at(
"classSize").end(); ++it) {
2511 auto cit = class_idx.find(it.key());
2512 if (cit != class_idx.end())
2513 size[cit->second - 1] =
2516 if (sj.contains(
"classWeight"))
2517 for (
auto it = sj.at(
"classWeight").begin(); it != sj.at(
"classWeight").end();
2519 auto cit = class_idx.find(it.key());
2520 if (cit != class_idx.end())
2521 weight[cit->second - 1] =
2526 if (rj.contains(
"dropRule"))
2527 for (
auto it = rj.at(
"dropRule").begin(); it != rj.at(
"dropRule").end(); ++it) {
2528 auto cit = class_idx.find(it.key());
2529 if (cit != class_idx.end())
2530 rule[cit->second - 1] = detail::drop_from_json(it.value().get<std::string>());
2532 const std::size_t
reg =
2533 net.
add_region(members, cap, rj.value(
"globalMaxJobs", -1.0), rule, mem, size,
2534 rj.value(
"globalMaxMemory", -1.0),
2535 rj.value(
"name", std::string()));
2537 if (rj.contains(
"constraintA") && rj.contains(
"constraintB"))
2539 detail::num_vec_from_json<T>(rj.at(
"constraintB")));
2552 if (model.contains(
"rewards")) {
2553 const std::size_t K = classes.size();
2554 for (
const json& rw : model.at(
"rewards")) {
2555 const std::string nm = rw.at(
"name").get<std::string>();
2556 const std::string kind = rw.at(
"type").get<std::string>();
2557 const std::string node_name = rw.at(
"node").get<std::string>();
2558 auto nit = node_idx.find(node_name);
2559 if (nit == node_idx.end())
2560 throw InputError(
"network_reader: reward '" + nm +
"' names node '" + node_name +
2561 "', which the model does not declare");
2564 std::size_t ist = 0;
2565 for (std::size_t s = 0; s < net.
get_struct().station_to_node.size(); ++s)
2566 if (net.
get_struct().station_to_node[s] == nit->second) ist = s + 1;
2568 throw UnsupportedError(
"network_reader: reward '" + nm +
"' is declared at node '" +
2570 "', which is not a station and so has no job count");
2574 std::size_t cls = 0;
2575 if (rw.contains(
"class")) {
2576 auto cit = class_idx.find(rw.at(
"class").get<std::string>());
2577 if (cit == class_idx.end())
2578 throw InputError(
"network_reader: reward '" + nm +
"' names class '" +
2579 rw.at(
"class").get<std::string>() +
2580 "', which the model does not declare");
2583 const double nservers = net.
get_struct().stations[ist - 1].nservers;
2584 const double cap = net.
get_struct().stations[ist - 1].cap;
2585 const std::size_t base = (ist - 1) * K;
2589 if (kind ==
"QLen") {
2590 net.
set_reward(nm, [base, K, cls](
const std::vector<T>& n) {
2591 if (cls)
return n[base + cls - 1];
2593 for (std::size_t k = 0; k < K; ++k) s = T(s + n[base + k]);
2595 }, kind, nit->second, cls);
2596 }
else if (kind ==
"Util") {
2599 net.
set_reward(nm, [base, K, cls, nservers](
const std::vector<T>& n) {
2601 if (cls) s = n[base + cls - 1];
2603 for (std::size_t k = 0; k < K; ++k) s = T(s + n[base + k]);
2606 }, kind, nit->second, cls);
2607 }
else if (kind ==
"Blocking") {
2608 net.
set_reward(nm, [base, K, cap](
const std::vector<T>& n) {
2610 for (std::size_t k = 0; k < K; ++k) s = T(s + n[base + k]);
2613 }, kind, nit->second, cls);
2616 "network_reader: reward '" + nm +
"' is of type '" + kind +
2617 "', and the reproducible templates are QLen, Util and Blocking; a Custom "
2618 "reward wraps an arbitrary function and is not on the wire at all");
2628 std::ifstream in(path.c_str());
2629 if (!in)
throw InputError(
"network_reader: cannot open " + path);
2633 }
catch (
const detail::json::parse_error& e) {
2634 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