5#ifndef LINE_IO_NETWORK_READER_H
6#define LINE_IO_NETWORK_READER_H
54using json = nlohmann::json;
84inline double num_from_json(
const json& v) {
85 if (v.is_null())
return std::numeric_limits<double>::quiet_NaN();
87 const std::string s = v.get<std::string>();
88 if (s ==
"Infinity" || s ==
"inf" || s ==
"Inf")
89 return std::numeric_limits<double>::infinity();
90 if (s ==
"-Infinity" || s ==
"-inf" || s ==
"-Inf")
91 return -std::numeric_limits<double>::infinity();
92 if (s ==
"NaN" || s ==
"nan")
return std::numeric_limits<double>::quiet_NaN();
95 const char* begin = s.c_str();
97 const double parsed = std::strtod(begin, &end);
98 if (end == begin || *end !=
'\0')
99 throw InputError(ErrorKind{
"Format"},
"network_reader: expected a number, got the string '" + s +
"'");
102 if (v.is_boolean())
return v.get<
bool>() ? 1.0 : 0.0;
105 return v.get<
double>();
109inline double num_value(
const json& obj,
const char* key,
double def) {
110 return obj.contains(key) ? num_from_json(obj.at(key)) : def;
119inline std::function<double(
double)> fluiddep_table_to_fun(
const json& lv,
const json& vv,
const std::string& who) {
120 std::vector<double> levels, values;
121 for (
const json& x : lv) levels.push_back(num_from_json(x));
122 for (
const json& x : vv) values.push_back(num_from_json(x));
123 if (levels.empty() || levels.size() != values.size())
124 throw InputError(ErrorKind{
"Format"},
"network_reader: the fluid rate dependence of " + who +
125 " needs as many 'values' as 'levels', and at least one");
126 for (std::size_t i = 0; i < levels.size(); ++i) {
127 if (!std::isfinite(levels[i]) || !std::isfinite(values[i]) || (i > 0 && levels[i] <= levels[i - 1]))
128 throw InputError(ErrorKind{
"Format"},
"network_reader: the fluid rate dependence of " + who +
129 " needs finite values over strictly increasing finite levels");
131 if (levels.size() == 1) {
132 const double v = values[0];
133 return [v](double) {
return v; };
135 return [levels, values](
double f) {
136 const double x = std::min(std::max(f, levels.front()), levels.back());
137 std::size_t hi =
static_cast<std::size_t
>(std::upper_bound(levels.begin(), levels.end(), x) - levels.begin());
138 if (hi >= levels.size()) hi = levels.size() - 1;
140 const std::size_t lo = hi - 1;
141 const double w = (x - levels[lo]) / (levels[hi] - levels[lo]);
142 return values[lo] + w * (values[hi] - values[lo]);
154inline std::size_t fork_dest_index(
const json& ov,
155 const std::map<std::string, std::size_t>& node_idx,
156 const std::string& fork_name) {
157 if (!ov.contains(
"dest"))
return 0;
158 const std::string d = ov.at(
"dest").get<std::string>();
159 if (d.empty())
return 0;
160 const std::map<std::string, std::size_t>::const_iterator it = node_idx.find(d);
161 if (it == node_idx.end())
162 throw InputError(ErrorKind{
"Format"},
"network_reader: the Fork node '" + fork_name +
163 "' declares an override towards '" + d +
"', which is not a node");
170 if (s ==
"INF" || s ==
"inf")
return S::INF;
171 if (s ==
"FCFS" || s ==
"fcfs")
return S::FCFS;
172 if (s ==
"PS" || s ==
"ps")
return S::PS;
173 if (s ==
"LCFS" || s ==
"lcfs")
return S::LCFS;
174 if (s ==
"LCFSPR" || s ==
"lcfspr")
return S::LCFSPR;
175 if (s ==
"SIRO" || s ==
"siro")
return S::SIRO;
176 if (s ==
"HOL" || s ==
"hol")
return S::HOL;
177 if (s ==
"DPS" || s ==
"dps")
return S::DPS;
178 if (s ==
"GPS" || s ==
"gps")
return S::GPS;
179 if (s ==
"SEPT" || s ==
"sept")
return S::SEPT;
180 if (s ==
"LEPT" || s ==
"lept")
return S::LEPT;
181 if (s ==
"SJF" || s ==
"sjf")
return S::SJF;
182 if (s ==
"LJF" || s ==
"ljf")
return S::LJF;
183 if (s ==
"SRPT" || s ==
"srpt")
return S::SRPT;
184 if (s ==
"LPS" || s ==
"lps")
return S::LPS;
185 if (s ==
"POLLING" || s ==
"polling")
return S::POLLING;
191 if (s ==
"FCFSPR" || s ==
"fcfspr")
return S::FCFSPR;
192 if (s ==
"FCFSPI" || s ==
"fcfspi")
return S::FCFSPI;
193 if (s ==
"FCFSPRIO" || s ==
"fcfsprio")
return S::HOL;
194 if (s ==
"FCFSPRPRIO" || s ==
"fcfsprprio")
return S::FCFSPRPRIO;
195 if (s ==
"FCFSPIPRIO" || s ==
"fcfspiprio")
return S::FCFSPIPRIO;
196 if (s ==
"LCFSPI" || s ==
"lcfspi")
return S::LCFSPI;
197 if (s ==
"LCFSPRIO" || s ==
"lcfsprio")
return S::LCFSPRIO;
198 if (s ==
"LCFSPRPRIO" || s ==
"lcfsprprio")
return S::LCFSPRPRIO;
199 if (s ==
"LCFSPIPRIO" || s ==
"lcfspiprio")
return S::LCFSPIPRIO;
200 if (s ==
"PSPRIO" || s ==
"psprio")
return S::PSPRIO;
201 if (s ==
"DPSPRIO" || s ==
"dpsprio")
return S::DPSPRIO;
202 if (s ==
"GPSPRIO" || s ==
"gpsprio")
return S::GPSPRIO;
203 if (s ==
"SRPT" || s ==
"srpt")
return S::SRPT;
204 if (s ==
"SRPTPRIO" || s ==
"srptprio")
return S::SRPTPRIO;
205 if (s ==
"PSJF" || s ==
"psjf")
return S::PSJF;
206 if (s ==
"FB" || s ==
"fb")
return S::FB;
207 if (s ==
"LAS" || s ==
"las")
return S::LAS;
208 if (s ==
"LRPT" || s ==
"lrpt")
return S::LRPT;
209 if (s ==
"SETF" || s ==
"setf")
return S::SETF;
210 if (s ==
"FSP" || s ==
"fsp")
return S::FSP;
211 if (s ==
"EDD" || s ==
"edd")
return S::EDD;
212 if (s ==
"EDF" || s ==
"edf")
return S::EDF;
213 if (s ==
"PAS" || s ==
"pas")
return S::PAS;
214 if (s ==
"OI" || s ==
"oi")
return S::OI;
215 if (s ==
"REF" || s ==
"ref")
return S::REF;
216 if (s ==
"EXT" || s ==
"ext")
return S::EXT;
217 throw UnsupportedError(ErrorKind{
"Import"},
"network_reader: unsupported scheduling discipline '" + s +
"'");
224 if (s ==
"drop")
return D::DROP;
225 if (s ==
"waitingQueue")
return D::WAITQ;
226 if (s ==
"blockingAfterService")
return D::BAS;
227 if (s ==
"retrial")
return D::RETRIAL;
228 if (s ==
"retrialWithLimit")
return D::RETRIAL_WITH_LIMIT;
234 if (s ==
"RR" || s ==
"rr")
return R::RR;
235 if (s ==
"FIFO" || s ==
"fifo")
return R::FIFO;
236 if (s ==
"SFIFO" || s ==
"sfifo")
return R::SFIFO;
237 if (s ==
"LRU" || s ==
"lru")
return R::LRU;
238 if (s ==
"HLRU" || s ==
"hlru")
return R::HLRU;
239 if (s ==
"CLIMB" || s ==
"climb")
return R::CLIMB;
240 if (s ==
"QLRU" || s ==
"qlru")
return R::QLRU;
241 throw UnsupportedError(ErrorKind{
"Import"},
"network_reader: unsupported cache replacement strategy '" + s +
"'");
246 if (s ==
"GATED" || s ==
"gated")
return P::GATED;
247 if (s ==
"EXHAUSTIVE" || s ==
"exhaustive")
return P::EXHAUSTIVE;
248 if (s ==
"KLIMITED" || s ==
"klimited" || s ==
"K-LIMITED")
return P::KLIMITED;
249 if (s ==
"DECREMENTING" || s ==
"decrementing")
return P::DECREMENTING;
250 throw UnsupportedError(ErrorKind{
"Import"},
"network_reader: unsupported polling type '" + s +
"'");
260lang::Distrib<T> hyperexp_fit_mean_scv(
double mean,
double scv) {
262 throw InputError(ErrorKind{
"Format"},
"network_reader: HyperExp fitMeanAndSCV needs SCV >= 1, got " +
263 std::to_string(scv));
264 const double p = 0.5 * (1.0 + std::sqrt((scv - 1.0) / (scv + 1.0)));
265 const double mu1 = 2.0 * p / mean;
266 const double mu2 = 2.0 * (1.0 - p) / mean;
268 num_traits<T>::from_double(mu1),
269 num_traits<T>::from_double(mu2));
282inline bool has_cache_key(
const json& nd,
const json& cj,
const char* key) {
283 return nd.contains(key) || cj.contains(key);
285inline const json& cache_key(
const json& nd,
const json& cj,
const char* key) {
286 return nd.contains(key) ? nd.at(key) : cj.at(key);
292 if (s ==
"PROB")
return R::PROB;
293 if (s ==
"RAND")
return R::RAND;
294 if (s ==
"RROBIN")
return R::RROBIN;
295 if (s ==
"WRROBIN")
return R::WRROBIN;
296 if (s ==
"JSQ")
return R::JSQ;
297 if (s ==
"SQ" || s ==
"KCHOICES")
return R::SQ;
300 if (s ==
"SDR")
return R::SDR;
301 if (s ==
"FIRING")
return R::FIRING;
302 if (s ==
"DISABLED")
return R::DISABLED;
303 throw UnsupportedError(ErrorKind{
"Import"},
"network_reader: unsupported routing strategy '" + s +
"'");
309 if (s ==
"RENEGING")
return I::RENEGING;
310 if (s ==
"BALKING")
return I::BALKING;
311 if (s ==
"RETRIAL")
return I::RETRIAL;
312 throw UnsupportedError(ErrorKind{
"Import"},
"network_reader: unsupported impatience type '" + s +
"'");
318 if (s ==
"QUEUE_LENGTH")
return B::QUEUE_LENGTH;
319 if (s ==
"EXPECTED_WAIT")
return B::EXPECTED_WAIT;
320 if (s ==
"COMBINED")
return B::COMBINED;
321 throw UnsupportedError(ErrorKind{
"Import"},
"network_reader: unsupported balking strategy '" + s +
"'");
327 if (s ==
"ORDER")
return H::ORDER;
328 if (s ==
"ALIS")
return H::ALIS;
329 if (s ==
"ALFS")
return H::ALFS;
330 if (s ==
"FAIRNESS")
return H::FAIRNESS;
331 if (s ==
"FSF")
return H::FSF;
332 if (s ==
"RAIS")
return H::RAIS;
333 throw UnsupportedError(ErrorKind{
"Import"},
"network_reader: unsupported heterogeneous scheduling policy '" + s +
"'");
340 for (
char& ch : u) ch =
static_cast<char>(std::toupper(
static_cast<unsigned char>(ch)));
343 throw UnsupportedError(ErrorKind{
"Import"},
"network_reader: unsupported departure discipline '" + s +
"'");
348std::vector<T> num_vec_from_json(
const json& v) {
355 for (
const json& x : v) out.push_back(num_traits<T>::from_double(num_from_json(x)));
357 out.push_back(num_traits<T>::from_double(num_from_json(v)));
363Matrix<T> mat_from_json(
const json& v) {
365 throw InputError(ErrorKind{
"Format"},
"network_reader: expected an array of rows");
366 Matrix<T> M(v.size(), v.empty() ? 0 : v.at(0).size());
367 for (std::size_t i = 0; i < v.size(); ++i) {
368 const json& row = v.at(i);
369 for (std::size_t j = 0; j < row.size(); ++j)
370 M(i, j) = num_traits<T>::from_double(num_from_json(row.at(j)));
377std::vector<Matrix<T> > mat_list_from_json(
const json& v) {
378 std::vector<Matrix<T> > out;
379 for (
const json& m : v) out.push_back(mat_from_json<T>(m));
385lang::Distrib<T> dist_from_fit(
const std::string& type,
const json& fit) {
386 const std::string method = fit.at(
"method").get<std::string>();
387 if (method ==
"fitMean" || method ==
"fitMeanAndOrder") {
388 const double mean = fit.at(
"mean").get<
double>();
390 if (type ==
"Erlang") {
391 const long order = fit.contains(
"order") ? fit.at(
"order").get<
long>() : 1L;
392 const T rate = num_traits<T>::from_double(
double(order) / mean);
398 if (method ==
"fitMeanAndSCV") {
399 const double mean = fit.at(
"mean").get<
double>();
400 const double scv = fit.at(
"scv").get<
double>();
401 if (type ==
"Erlang")
403 num_traits<T>::from_double(scv));
404 if (type ==
"HyperExp")
return hyperexp_fit_mean_scv<T>(mean, scv);
405 if (std::fabs(scv - 1.0) < 1e-12)
407 throw UnsupportedError(ErrorKind{
"Import"},
"network_reader: fitMeanAndSCV for family '" + type +
408 "' is not reconstructed; use explicit params");
410 throw UnsupportedError(ErrorKind{
"Import"},
"network_reader: unsupported fit method '" + method +
"' for '" + type +
416lang::Distrib<T> dist_from_json(
const json& obj) {
417 const std::string type = obj.at(
"type").get<std::string>();
431 if (type ==
"Prior") {
432 const std::string kind =
433 obj.contains(
"kind") ? obj.at(
"kind").get<std::string>() : std::string(
"discrete");
434 if (kind !=
"discrete" && kind !=
"continuous")
435 throw InputError(ErrorKind{
"Format"},
"network_reader: a Prior's 'kind' is 'discrete' or 'continuous', got '" +
437 if (kind ==
"continuous") {
438 if (!obj.contains(
"paramDist") || !obj.contains(
"factory"))
440 "network_reader: a continuous Prior carries 'paramDist' and 'factory' (a "
441 "template distribution plus the parameter slots it fills)");
442 const json& fac = obj.at(
"factory");
443 if (!fac.contains(
"template") || !fac.contains(
"slots"))
445 "network_reader: a continuous Prior's 'factory' carries 'template' and "
447 const json tmpl = fac.at(
"template");
448 std::vector<std::string> slots;
449 for (
const json& s : fac.at(
"slots")) slots.push_back(s.get<std::string>());
452 "network_reader: a continuous Prior's factory names no parameter slot, so the "
453 "parameter would not reach the distribution it builds");
454 if (!tmpl.contains(
"params"))
456 "network_reader: a continuous Prior's factory template carries its parameters "
457 "as 'params'; a fitted or phase-type representation has no named slot");
458 for (std::size_t l = 0; l < slots.size(); ++l)
459 if (!tmpl.at(
"params").contains(slots[l]))
460 throw InputError(ErrorKind{
"Format"},
"network_reader: a continuous Prior's factory names '" +
461 slots[l] +
"' as a parameter slot, but its template has no "
467 const std::function<lang::Distrib<T>(
const T&)> factory =
468 [tmpl, slots](
const T& theta) {
470 for (std::size_t l = 0; l < slots.size(); ++l)
471 j[
"params"][slots[l]] = num_traits<T>::to_double(theta);
472 return dist_from_json<T>(j);
476 if (!obj.contains(
"distributions") || !obj.contains(
"probabilities"))
477 throw InputError(ErrorKind{
"Format"},
478 "network_reader: a discrete Prior carries 'distributions' and 'probabilities'");
479 std::vector<lang::Distrib<T> > alts;
480 for (
const json& a : obj.at(
"distributions")) alts.push_back(dist_from_json<T>(a));
481 std::vector<T> probs;
482 for (
const json& p : obj.at(
"probabilities"))
483 probs.push_back(num_traits<T>::from_double(p.get<
double>()));
491 if (type ==
"NHPP" || type ==
"MAPt" || type ==
"PHt" || type ==
"MMAPt" ||
492 type ==
"MPHt" || type ==
"BMMAPt") {
493 const json& p = obj.at(
"params");
495 for (
const json& b : p.at(
"breakpoints"))
496 bp.push_back(num_traits<T>::from_double(b.get<
double>()));
502 const bool cyc = !p.contains(
"cyclic") || p.at(
"cyclic").get<
bool>();
512 auto read_seg = [&](
const json& seg) {
513 if (seg.is_number()) {
515 M(0, 0) = num_traits<T>::from_double(num_from_json(seg));
519 throw InputError(ErrorKind{
"Format"},
"network_reader: a schedule segment must be a matrix, "
520 "a row of numbers, or a single number");
521 if (!seg.empty() && seg.at(0).is_number()) {
522 Matrix<T> M(1, seg.size());
523 for (std::size_t b = 0; b < seg.size(); ++b)
524 M(0, b) = num_traits<T>::from_double(num_from_json(seg.at(b)));
527 return mat_from_json<T>(seg);
529 auto read_mats = [&](
const char* key) {
530 std::vector<Matrix<T> > out;
531 for (
const json& seg : p.at(key)) out.push_back(read_seg(seg));
534 if (type ==
"NHPP") {
535 std::vector<T> rates;
536 for (
const json& r : p.at(
"rates"))
537 rates.push_back(num_traits<T>::from_double(r.get<
double>()));
545 auto read_mark_mats = [&](
const char* key) {
546 std::vector<std::vector<Matrix<T> > > out;
547 for (
const json& per_mark : p.at(key)) {
548 if (!per_mark.is_array())
549 throw InputError(ErrorKind{
"Format"},
"network_reader: the marked schedule key '" +
550 std::string(key) +
"' must nest marks over segments");
551 std::vector<Matrix<T> > segs;
552 for (
const json& seg : per_mark) segs.push_back(read_seg(seg));
559 if (type ==
"BMMAPt") {
563 std::vector<std::vector<std::vector<Matrix<T> > > > blocks;
564 for (
const json& per_mark : p.at(
"D1kb")) {
565 if (!per_mark.is_array())
566 throw InputError(ErrorKind{
"Format"},
"network_reader: 'D1kb' must nest marks over batch sizes "
568 std::vector<std::vector<Matrix<T> > > per_batch;
569 for (
const json& batch : per_mark) {
570 if (!batch.is_array())
571 throw InputError(ErrorKind{
"Format"},
"network_reader: 'D1kb' must nest marks over batch "
572 "sizes over segments");
573 std::vector<Matrix<T> > segs;
574 for (
const json& seg : batch) segs.push_back(read_seg(seg));
575 per_batch.push_back(segs);
577 blocks.push_back(per_batch);
581 std::vector<std::vector<T> > alphas;
582 for (
const json& a : p.at(
"alpha")) {
583 const std::vector<double> row = a.get<std::vector<double> >();
585 for (
double v : row) av.push_back(num_traits<T>::from_double(v));
586 alphas.push_back(av);
588 if (type ==
"MPHt") {
591 const std::vector<std::vector<Matrix<T> > > ex = read_mark_mats(
"exit");
592 std::vector<std::vector<std::vector<T> > > exits;
593 for (
const std::vector<Matrix<T> >& per_mark : ex) {
594 std::vector<std::vector<T> > segs;
595 for (
const Matrix<T>& M : per_mark) {
597 for (std::size_t i = 0; i < M.rows(); ++i)
598 for (std::size_t j = 0; j < M.cols(); ++j) v.push_back(M(i, j));
601 exits.push_back(segs);
614 if (type ==
"Replayer" || type ==
"Trace") {
615 if (obj.contains(
"params") && obj.at(
"params").contains(
"fileName")) {
616 const std::string path = obj.at(
"params").at(
"fileName").get<std::string>();
617 std::ifstream tr(path.c_str());
619 std::vector<T> samples;
621 while (tr >> v) samples.push_back(num_traits<T>::from_double(v));
622 if (!samples.empty()) {
629 if (obj.contains(
"ph")) {
630 const json& ph = obj.at(
"ph");
631 const std::vector<double> a = ph.at(
"alpha").get<std::vector<double> >();
632 const std::vector<std::vector<double> > rows =
633 ph.at(
"T").get<std::vector<std::vector<double> > >();
634 std::vector<T> alpha;
635 for (
double x : a) alpha.push_back(num_traits<T>::from_double(x));
636 Matrix<T> A(rows.size(), rows.empty() ? 0 : rows[0].size());
637 for (std::size_t i = 0; i < rows.size(); ++i)
638 for (std::size_t j = 0; j < rows[i].size(); ++j)
639 A(i, j) = num_traits<T>::from_double(rows[i][j]);
642 if (obj.contains(
"params") && obj.at(
"params").contains(
"mean"))
644 num_traits<T>::from_double(obj.at(
"params").at(
"mean").get<
double>()));
646 "network_reader: a Replayer carries neither a readable trace file, nor the APH fit "
647 "the writers add beside it, nor a mean; there is nothing to reconstruct");
652 if (type ==
"MMPP2") {
653 const json& p = obj.at(
"params");
654 const double l0 = p.at(
"lambda0").get<
double>(), l1 = p.at(
"lambda1").get<
double>();
655 const double s0 = p.at(
"sigma0").get<
double>(), s1 = p.at(
"sigma1").get<
double>();
656 Matrix<T> D0(2, 2), D1(2, 2);
657 D1(0, 0) = num_traits<T>::from_double(l0);
658 D1(1, 1) = num_traits<T>::from_double(l1);
659 D0(0, 0) = num_traits<T>::from_double(-(l0 + s0));
660 D0(0, 1) = num_traits<T>::from_double(s0);
661 D0(1, 0) = num_traits<T>::from_double(s1);
662 D0(1, 1) = num_traits<T>::from_double(-(l1 + s1));
665 if (type ==
"MAP" || type ==
"MMPP") {
666 if (!obj.contains(
"map"))
667 throw InputError(ErrorKind{
"Format"},
"network_reader: " + type +
" carries no 'map' (D0, D1) object");
668 const json& mp = obj.at(
"map");
670 mat_from_json<T>(mp.at(
"D1")),
679 if (type ==
"MPH" || type ==
"MarkedPH") {
680 if (!obj.contains(
"mph"))
681 throw InputError(ErrorKind{
"Format"},
"network_reader: " + type +
682 " carries no 'mph' (alpha, S, exit) object");
683 const json& mp = obj.at(
"mph");
684 const Matrix<T> alpha_m = mat_from_json<T>(mp.at(
"alpha"));
685 const Matrix<T> S = mat_from_json<T>(mp.at(
"S"));
686 const std::size_t H = S.rows();
687 if (alpha_m.rows() * alpha_m.cols() != H)
688 throw InputError(ErrorKind{
"Format"},
"network_reader: an MPH's alpha and S disagree in order");
689 std::vector<T> alpha;
690 for (std::size_t i = 0; i < alpha_m.rows(); ++i)
691 for (std::size_t j = 0; j < alpha_m.cols(); ++j) alpha.push_back(alpha_m(i, j));
692 const std::vector<Matrix<T> > ex = mat_list_from_json<T>(mp.at(
"exit"));
693 std::vector<std::vector<T> > exits;
694 for (
const Matrix<T>& sk : ex) {
695 if (sk.rows() * sk.cols() != H)
696 throw InputError(ErrorKind{
"Format"},
"network_reader: an MPH exit vector and S disagree in order");
698 for (std::size_t i = 0; i < sk.rows(); ++i)
699 for (std::size_t j = 0; j < sk.cols(); ++j) sv.push_back(sk(i, j));
706 if (type ==
"MMAP" || type ==
"MarkedMAP") {
707 if (!obj.contains(
"mmap"))
708 throw InputError(ErrorKind{
"Format"},
"network_reader: " + type +
" carries no 'mmap' (D0, D1k) object");
709 const json& mp = obj.at(
"mmap");
711 mat_list_from_json<T>(mp.at(
"D1k")));
716 if (type ==
"BMAP" || type ==
"MarkedMMPP") {
717 const json& p = obj.at(
"params");
718 const std::vector<Matrix<T> > D = mat_list_from_json<T>(p.at(
"D"));
721 throw InputError(ErrorKind{
"Format"},
"network_reader: a MarkedMMPP carries D0 and at least one marked block");
724 if (type ==
"DMAP") {
725 const json& p = obj.at(
"params");
729 const json& p = obj.at(
"params");
734 if (type ==
"ME" || type ==
"CME") {
735 const json& p = obj.at(
"params");
736 const std::vector<double> a = p.at(
"alpha").get<std::vector<double> >();
737 std::vector<T> alpha;
738 for (
double v : a) alpha.push_back(num_traits<T>::from_double(v));
750 if (obj.contains(
"ph")) {
751 const json& ph = obj.at(
"ph");
752 const std::vector<double> a = ph.at(
"alpha").get<std::vector<double> >();
753 const std::vector<std::vector<double> > rows =
754 ph.at(
"T").get<std::vector<std::vector<double> > >();
755 if (a.empty() || rows.size() != a.size())
756 throw InputError(ErrorKind{
"Format"},
"network_reader: distribution '" + type +
757 "' has a 'ph' block whose alpha and T disagree in order");
758 std::vector<T> alpha;
759 alpha.reserve(a.size());
760 for (
double v : a) alpha.push_back(num_traits<T>::from_double(v));
761 Matrix<T> A(rows.size(), rows.empty() ? 0 : rows[0].size());
762 for (std::size_t i = 0; i < rows.size(); ++i)
763 for (std::size_t j = 0; j < rows[i].size(); ++j)
764 A(i, j) = num_traits<T>::from_double(rows[i][j]);
772 if (!obj.contains(
"params") && obj.contains(
"fit"))
773 return dist_from_fit<T>(type, obj.at(
"fit"));
774 if (!obj.contains(
"params"))
775 throw InputError(ErrorKind{
"Format"},
"network_reader: distribution '" + type +
776 "' has neither params nor a fit block");
777 const json& p = obj.at(
"params");
779 const double lam = p.contains(
"lambda") ? p.at(
"lambda").get<
double>()
780 : p.at(
"rate").
get<double>();
783 if (type ==
"Det")
return lang::Distrib<T>::det(num_traits<T>::from_double(p.at(
"value").get<
double>()));
784 if (type ==
"Erlang") {
785 const double lam = p.at(
"lambda").get<
double>();
786 const long k = p.at(
"k").get<
long>();
789 if (type ==
"HyperExp") {
790 const std::vector<T> pv = num_vec_from_json<T>(p.at(
"p"));
791 const std::vector<T> lv = num_vec_from_json<T>(p.at(
"lambda"));
794 if (pv.size() == 2 && lv.size() == 2)
798 if (type ==
"Coxian")
800 num_vec_from_json<T>(p.at(
"phi")));
803 num_traits<T>::from_double(p.at(
"mu2").get<
double>()),
804 num_traits<T>::from_double(p.at(
"phi1").get<
double>()));
805 if (type ==
"Uniform")
807 num_traits<T>::from_double(p.at(
"b").get<
double>()));
811 num_traits<T>::from_double(p.at(
"beta").get<
double>()));
812 if (type ==
"Lognormal")
814 num_traits<T>::from_double(p.at(
"sigma").get<
double>()));
820 if (type ==
"Normal")
822 num_traits<T>::from_double(p.at(
"sigma").get<
double>()));
826 if (type ==
"Pareto")
828 num_traits<T>::from_double(p.at(
"scale").get<
double>()));
829 if (type ==
"Weibull")
831 num_traits<T>::from_double(p.at(
"beta").get<
double>()));
832 if (type ==
"DiscreteUniform")
834 num_traits<T>::from_double(p.at(
"min").get<
double>()),
835 num_traits<T>::from_double(p.at(
"max").get<
double>()));
836 if (type ==
"Bernoulli")
838 if (type ==
"Binomial")
840 num_traits<T>::from_double(p.at(
"p").get<
double>()));
841 if (type ==
"Poisson")
843 if (type ==
"Geometric")
847 std::size_t(p.at(
"n").get<
long>()));
848 if (type ==
"DiscreteSampler") {
849 const std::vector<T> pv = num_vec_from_json<T>(p.at(
"p"));
850 const std::vector<T> xv =
851 p.contains(
"x") ? num_vec_from_json<T>(p.at(
"x")) : std::vector<T>();
854 if (type ==
"EmpiricalCDF" || type ==
"EmpiricalCdf")
856 num_vec_from_json<T>(p.at(
"F")));
867 if (p.contains(
"mean") && !p.contains(
"scv"))
869 if (p.contains(
"mean") && p.contains(
"scv")) {
870 const double mean = p.at(
"mean").get<
double>();
871 const double scv = p.at(
"scv").get<
double>();
872 if (std::fabs(scv - 1.0) < 1e-12)
876 num_traits<T>::from_double(scv));
877 return hyperexp_fit_mean_scv<T>(mean, scv);
879 throw UnsupportedError(ErrorKind{
"Import"},
"network_reader: unsupported distribution family '" + type +
891typename qn::CacheParam<T>::Popularity popularity_kind_from_json(
const json& obj,
892 std::size_t nitems) {
893 typename qn::CacheParam<T>::Popularity k;
894 const std::string type = obj.value(
"type", std::string());
895 const json& p = obj.contains(
"params") ? obj.at(
"params") : obj;
896 if (type ==
"Zipf") {
898 k.s = p.at(
"s").get<
double>();
899 k.n = p.contains(
"n") ? std::size_t(p.at(
"n").get<
long>()) : nitems;
900 }
else if (type ==
"DiscreteSampler" || p.contains(
"p")) {
902 k.n = p.contains(
"p") ? p.at(
"p").size() : nitems;
918std::vector<T> pmf_from_json(
const json& obj, std::size_t nitems) {
919 const std::string type = obj.value(
"type", std::string());
920 const json& p = obj.contains(
"params") ? obj.at(
"params") : obj;
926 if (type ==
"DiscreteSampler" || (p.contains(
"p") && p.at(
"p").is_array())) {
927 for (
const json& v : p.at(
"p")) out.push_back(num_traits<T>::from_double(v.get<
double>()));
930 if (type ==
"Zipf") {
931 const double s = p.at(
"s").get<
double>();
932 const std::size_t n = p.contains(
"n") ? std::size_t(p.at(
"n").get<
long>()) : nitems;
934 for (std::size_t k = 1; k <= n; ++k) h += std::pow(
double(k), -s);
935 for (std::size_t k = 1; k <= n; ++k)
936 out.push_back(num_traits<T>::from_double(std::pow(
double(k), -s) / h));
940 "network_reader: a cache popularity is written as '" + type +
941 "', and the discrete families carrying an item pmf are DiscreteSampler and Zipf");
954constexpr std::size_t REMOVAL_PMF_MAX_TERMS = 4096;
976std::vector<T> removal_pmf_from_json(
const json& obj) {
977 const std::string type = obj.value(
"type", std::string());
978 const json& p = obj.contains(
"params") ? obj.at(
"params") : obj;
980 auto put = [&out](std::size_t k,
double v) {
981 if (out.size() <= k) out.resize(k + 1, num_traits<T>::from_int(0));
982 out[k] = num_traits<T>::from_double(num_traits<T>::to_double(out[k]) + v);
984 if (type ==
"DiscreteSampler" || (p.contains(
"p") && p.at(
"p").is_array())) {
988 const json& pv = p.at(
"p");
989 const bool has_x = p.contains(
"x") && p.at(
"x").is_array();
990 for (std::size_t i = 0; i < pv.size(); ++i) {
992 has_x ? p.at(
"x").at(i).get<
double>() : static_cast<double>(i + 1);
993 if (xi < 0)
throw InputError(ErrorKind{
"Format"},
"network_reader: a batch size cannot be negative");
994 put(
static_cast<std::size_t
>(xi + 0.5), pv.at(i).get<
double>());
998 if (type ==
"Bernoulli") {
999 const double q = p.at(
"p").get<
double>();
1004 if (type ==
"Binomial") {
1005 const double q = p.at(
"p").get<
double>();
1006 const std::size_t n =
static_cast<std::size_t
>(p.at(
"n").get<double>() + 0.5);
1007 double term = std::pow(1.0 - q,
static_cast<double>(n));
1008 for (std::size_t k = 0; k <= n; ++k) {
1010 if (k < n && q < 1.0)
1011 term *= (
static_cast<double>(n - k) /
static_cast<double>(k + 1)) * q / (1.0 - q);
1015 if (type ==
"Poisson") {
1016 const double lam = p.at(
"lambda").get<
double>();
1017 double term = std::exp(-lam), acc = 0.0;
1018 for (std::size_t k = 0; k < REMOVAL_PMF_MAX_TERMS; ++k) {
1021 if (acc > 1.0 - 1e-15)
break;
1022 term *= lam /
static_cast<double>(k + 1);
1026 if (type ==
"Geometric") {
1029 const double q = p.at(
"p").get<
double>();
1030 if (q <= 0.0 || q > 1.0)
throw InputError(ErrorKind{
"Format"},
"network_reader: Geometric(p) needs 0 < p <= 1");
1032 double term = q, acc = 0.0;
1033 for (std::size_t k = 1; k <= REMOVAL_PMF_MAX_TERMS; ++k) {
1036 if (acc > 1.0 - 1e-15)
break;
1041 if (type ==
"DiscreteUniform") {
1042 const long lo =
static_cast<long>(p.at(
"min").get<double>());
1043 const long hi =
static_cast<long>(p.at(
"max").get<double>());
1044 if (hi < lo || lo < 0)
1045 throw InputError(ErrorKind{
"Format"},
"network_reader: DiscreteUniform(min,max) needs 0 <= min <= max");
1046 const double w = 1.0 /
static_cast<double>(hi - lo + 1);
1047 for (
long k = lo; k <= hi; ++k) put(static_cast<std::size_t>(k), w);
1050 if (type ==
"Det") {
1051 const double v = p.contains(
"t") ? p.at(
"t").get<
double>() : p.at(
"mean").
get<double>();
1052 if (v < 0)
throw InputError(ErrorKind{
"Format"},
"network_reader: a batch size cannot be negative");
1053 put(
static_cast<std::size_t
>(v + 0.5), 1.0);
1057 "network_reader: a signal removal law is written as '" + type +
1058 "', and the discrete families a batch size can be drawn from are DiscreteSampler, "
1059 "Bernoulli, Binomial, Poisson, Geometric, DiscreteUniform and Det");
1077lang::CdScaling<T> cd_scaling_from_json(
const json& tbl,
const std::vector<int>& cutoffs,
1079 std::map<std::string, std::vector<T> >
table;
1080 for (
auto it = tbl.begin(); it != tbl.end(); ++it)
1081 table[it.key()] = num_vec_from_json<T>(it.value());
1082 const std::vector<int> cut = cutoffs;
1083 return [
table, cut, K](
const std::vector<T>& n) {
1085 for (std::size_t r = 0; r < K; ++r) {
1086 int v = r < n.size()
1087 ?
static_cast<int>(std::lround(num_traits<T>::to_double(n[r])))
1090 if (r < cut.size() && v > cut[r]) v = cut[r];
1092 key += std::to_string(v);
1094 std::vector<T> out(K, num_traits<T>::from_int(1));
1095 typename std::map<std::string, std::vector<T> >::const_iterator it =
table.find(key);
1096 if (it ==
table.end())
return out;
1097 for (std::size_t r = 0; r < K && r < it->second.size(); ++r) out[r] = it->second[r];
1125std::function<T(
const std::vector<std::size_t>&)> oi_rate_from_json(
const json& tbl,
1126 const std::vector<int>& cutoffs,
1128 std::map<std::string, T>
table;
1129 for (
auto it = tbl.begin(); it != tbl.end(); ++it)
1130 table[it.key()] = num_traits<T>::from_double(it.value().get<
double>());
1131 const std::vector<int> cut = cutoffs;
1132 return [
table, cut, K](
const std::vector<std::size_t>& micro) {
1133 std::vector<int> cnt(K, 0);
1134 for (std::size_t j = 0; j < micro.size(); ++j)
1135 if (micro[j] >= 1 && micro[j] <= K) ++cnt[micro[j] - 1];
1137 for (std::size_t r = 0; r < K; ++r) {
1139 if (r < cut.size() && v > cut[r]) v = cut[r];
1141 key += std::to_string(v);
1143 typename std::map<std::string, T>::const_iterator it =
table.find(key);
1144 return it ==
table.end() ? num_traits<T>::from_int(0) : it->second;
1160std::vector<T> cd_peak_from_json(
const json& blk,
const json& tbl) {
1161 if (blk.contains(
"peak") && !blk.at(
"peak").
empty())
1162 return num_vec_from_json<T>(blk.at(
"peak"));
1164 for (
auto it = tbl.begin(); it != tbl.end(); ++it) {
1165 if (it.key().find_first_not_of(
"0,") == std::string::npos)
continue;
1166 const std::vector<double> row = num_vec_from_json<double>(it.value());
1167 for (
double x : row)
1168 if (std::isfinite(x) && x > bmax) bmax = x;
1170 return std::vector<T>(1, num_traits<T>::from_double(bmax));
1181inline void reject_unconsumed_model_keys(
const json& model) {
1182 static const char* kModelKeys[] = {
"name",
"type",
"nodes",
1183 "classes",
"routing",
"format",
1184 "version",
"rewards",
"finiteCapacityRegions",
1185 "routingStrategies",
"routingWeights",
"routingParams",
1186 "logPath",
"globalDependence",
"stateDepRouting"};
1193 static const char* kNodeKeys[] = {
1194 "name",
"type",
"scheduling",
"servers",
"service",
1195 "buffer",
"capacity",
"dropRule",
"schedParams",
"pollingType",
1196 "pollingPar",
"classSwitchMatrix",
"csMatrix",
"forkNode",
1197 "tasksPerLink",
"fanOutByDest",
"fanOutDist",
"fanOutProb",
1198 "items",
"numItems",
"itemLevelCap",
"popularity",
"replacementStrategy",
1199 "itemSizes",
"costCaps",
"accessProb",
"admissionProb",
"accessGraph",
1201 "cache",
"initialState",
1202 "retrievalSystem",
"queues",
"hitClass",
"missClass",
1203 "immediateFeedback",
"loadDependence",
1204 "classDependence",
"jointDependence",
1205 "modes",
"classCap",
"departureDiscipline",
1206 "oiServiceRate",
"oiCutoffs",
"swapGraph",
1207 "arrivalBatch",
"markedClasses",
1208 "stateSpace",
"statePrior",
"joinStrategy",
"joinQuorum",
1209 "setupTime",
"delayOffTime",
"switchoverTimes",
"breakdown",
1210 "serverTypes",
"heteroSchedPolicy",
"serverParallelism",
1211 "fluid",
"fluidCapacity",
"fluidLevel",
1212 "balking",
"retrial",
"patience",
"orbitImpatience",
1215 "fileName",
"filePath",
"startTime",
"loggerName",
"timestamp",
1216 "jobID",
"jobClass",
"timeSameClass",
"timeAnyClass"};
1221 static const char* kClassKeys[] = {
1222 "name",
"type",
"population",
"refNode",
"priority",
1223 "openOrClosed",
"signalType",
"targetClass",
"removalPolicy",
"removalDistribution",
1224 "isReferenceClass",
"deadline",
"patience",
"impatienceType",
1225 "spawnClass",
"replySignalClass",
"immediateFeedback"};
1226 auto known = [](
const char*
const* tab, std::size_t n,
const std::string& k) {
1227 for (std::size_t i = 0; i < n; ++i)
1228 if (k == tab[i])
return true;
1231 const std::string why =
1232 "', which this reader does not implement. Refusing rather than dropping it: a "
1233 "constraint silently discarded here would make every solver return a confident "
1234 "answer for a different model";
1235 for (
auto it = model.begin(); it != model.end(); ++it)
1236 if (!known(kModelKeys,
sizeof(kModelKeys) /
sizeof(*kModelKeys), it.key()))
1237 throw UnsupportedError(ErrorKind{
"Import"},
"network_reader: the model carries '" + it.key() + why);
1238 if (model.contains(
"classes"))
1239 for (
const json& cl : model.at(
"classes"))
1240 for (
auto it = cl.begin(); it != cl.end(); ++it)
1241 if (!known(kClassKeys,
sizeof(kClassKeys) /
sizeof(*kClassKeys), it.key()))
1243 cl.value(
"name", std::string(
"?")) +
"' carries '" +
1245 if (!model.contains(
"nodes"))
return;
1246 for (
const json& nd : model.at(
"nodes"))
1247 for (
auto it = nd.begin(); it != nd.end(); ++it)
1248 if (!known(kNodeKeys,
sizeof(kNodeKeys) /
sizeof(*kNodeKeys), it.key()))
1250 nd.value(
"name", std::string(
"?")) +
"' carries '" +
1267 const json& model = root.contains(
"model") ? root.at(
"model") : root;
1268 const std::string mtype = model.value(
"type", std::string(
"Network"));
1269 if (mtype !=
"Network") {
1273 if (mtype ==
"Environment")
1275 "network_reader: this is an Environment model (a network per stage plus the "
1276 "stage transitions); solve it with -s env, which reads it through "
1277 "environment_reader.h");
1279 "' is not a Network; only Network models are solved by this path");
1292 detail::reject_unconsumed_model_keys(model);
1295 net.
set_log_path(model.value(
"logPath", std::string()));
1303 const json& nodes = model.at(
"nodes");
1304 std::map<std::string, std::size_t> node_idx;
1305 std::vector<std::string> node_type(nodes.size());
1306 for (std::size_t i = 0; i < nodes.size(); ++i)
1307 node_type[i] = nodes[i].at(
"type").get<std::string>();
1310 for (std::size_t i = 0; i < nodes.size(); ++i) {
1311 const json& nd = nodes[i];
1312 const std::string& type = node_type[i];
1318 if (type ==
"ClassSwitch" || type ==
"Cache" || type ==
"Transition")
continue;
1319 const std::string name = nd.at(
"name").get<std::string>();
1320 std::size_t idx = 0;
1321 if (type ==
"Source") {
1323 }
else if (type ==
"Sink") {
1325 }
else if (type ==
"Delay") {
1327 }
else if (type ==
"Queue") {
1329 detail::sched_from_json(nd.value(
"scheduling", std::string(
"FCFS"))));
1330 }
else if (type ==
"Router") {
1332 }
else if (type ==
"Logger" || type ==
"LogTunnel") {
1333 idx = net.
add_logger(name, nd.value(
"fileName", std::string()));
1335 if (nd.contains(
"filePath")) lg.
file_path = nd.at(
"filePath").get<std::string>();
1343 }
else if (type ==
"Place") {
1352 detail::sched_from_json(nd.value(
"scheduling", std::string(
"INF"))));
1354 if (nd.contains(
"fluid") && !nd.at(
"fluid").is_null() && detail::num_from_json(nd.at(
"fluid")) != 0.0) {
1356 if (nd.contains(
"fluidCapacity") && !nd.at(
"fluidCapacity").is_null())
1358 if (nd.contains(
"fluidLevel") && !nd.at(
"fluidLevel").is_null())
1361 }
else if (type ==
"Join") {
1363 }
else if (type ==
"Fork") {
1364 idx = net.
add_fork(name, detail::num_value(nd,
"tasksPerLink", 1.0));
1369 node_idx[name] = idx;
1373 const json& classes = model.at(
"classes");
1374 std::map<std::string, std::size_t> class_idx;
1377 std::vector<std::size_t> signal_classes;
1378 for (std::size_t r = 0; r < classes.size(); ++r) {
1379 const json& cl = classes[r];
1380 const std::string name = cl.at(
"name").get<std::string>();
1381 const std::string type = cl.at(
"type").get<std::string>();
1382 std::size_t idx = 0;
1383 if (type ==
"Open") {
1385 }
else if (type ==
"Closed" || type ==
"SelfLooping") {
1390 const double pop = cl.at(
"population").get<
double>();
1391 const std::string ref = cl.at(
"refNode").get<std::string>();
1392 auto it = node_idx.find(ref);
1393 if (it == node_idx.end())
1394 throw InputError(
ErrorKind{
"Format"},
"network_reader: class '" + name +
"' references unknown node '" +
1396 idx = type ==
"SelfLooping"
1399 }
else if (type ==
"Signal") {
1405 const std::string kind = cl.value(
"openOrClosed", std::string(
"Open"));
1406 if (kind ==
"Closed") {
1407 const std::string ref = cl.at(
"refNode").get<std::string>();
1408 auto it = node_idx.find(ref);
1409 if (it == node_idx.end())
1411 "' references unknown node '" + ref +
"'");
1418 cl.value(
"priority", 0));
1422 signal_classes.push_back(r);
1425 "' for class '" + name +
"'");
1436 if (cl.contains(
"deadline")) {
1437 const double due = cl.at(
"deadline").get<
double>();
1442 if (cl.contains(
"patience")) {
1443 const json& pt = cl.at(
"patience");
1448 cl.contains(
"impatienceType")
1449 ? detail::impatience_from_json(cl.at(
"impatienceType").get<std::string>())
1452 class_idx[name] = idx;
1456 for (std::size_t r = 0; r < classes.size(); ++r) {
1457 const json& cl = classes[r];
1458 const std::size_t idx = class_idx.at(cl.at(
"name").get<std::string>());
1459 if (cl.contains(
"spawnClass")) {
1460 const std::string sp = cl.at(
"spawnClass").get<std::string>();
1461 auto sit = class_idx.find(sp);
1462 if (sit == class_idx.end())
1463 throw InputError(
ErrorKind{
"Format"},
"network_reader: class '" + cl.at(
"name").get<std::string>() +
1464 "' spawns class '" + sp +
"', which the model does not declare");
1469 if (cl.contains(
"replySignalClass")) {
1470 const std::string rp = cl.at(
"replySignalClass").get<std::string>();
1471 auto rit = class_idx.find(rp);
1472 if (rit == class_idx.end())
1473 throw InputError(
ErrorKind{
"Format"},
"network_reader: class '" + cl.at(
"name").get<std::string>() +
1474 "' replies with class '" + rp +
1475 "', which the model does not declare");
1480 for (std::size_t si = 0; si < signal_classes.size(); ++si) {
1481 const json& cl = classes[signal_classes[si]];
1482 const std::string name = cl.at(
"name").get<std::string>();
1483 const std::string st = cl.value(
"signalType", std::string(
"negative"));
1487 else if (st !=
"negative" && st !=
"NEGATIVE")
1489 "', and the kinds on the wire are negative, catastrophe and "
1492 const std::string rp = cl.value(
"removalPolicy", std::string(
"RANDOM"));
1495 std::size_t target = 0;
1496 if (cl.contains(
"targetClass")) {
1497 auto tit = class_idx.find(cl.at(
"targetClass").get<std::string>());
1498 if (tit == class_idx.end())
1499 throw InputError(
ErrorKind{
"Format"},
"network_reader: signal class '" + name +
"' targets class '" +
1500 cl.at(
"targetClass").get<std::string>() +
1501 "', which the model does not declare");
1502 target = tit->second;
1504 std::vector<T> remdist;
1505 if (cl.contains(
"removalDistribution"))
1506 remdist = detail::removal_pmf_from_json<T>(cl.at(
"removalDistribution"));
1507 net.
set_signal(class_idx.at(name), kind, pol, target, remdist);
1509 const std::size_t K = class_idx.size();
1513 for (std::size_t i = 0; i < nodes.size(); ++i) {
1514 const json& nd = nodes[i];
1515 const std::string& type = node_type[i];
1516 if (type !=
"ClassSwitch" && type !=
"Join" && type !=
"Cache" && type !=
"Fork")
continue;
1517 const std::string name = nd.at(
"name").get<std::string>();
1518 std::size_t idx = 0;
1519 if (type ==
"Fork") {
1525 if (!nd.contains(
"fanOutByDest") && !nd.contains(
"fanOutDist") &&
1526 !nd.contains(
"fanOutProb"))
1528 idx = node_idx.at(name);
1529 if (nd.contains(
"fanOutByDest")) {
1530 const json& ovs = nd.at(
"fanOutByDest");
1531 for (std::size_t e = 0; e < ovs.size(); ++e)
1533 ovs[e].at(
"value").get<
double>(),
1534 detail::fork_dest_index(ovs[e], node_idx, name));
1536 if (nd.contains(
"fanOutDist")) {
1537 const json& ovs = nd.at(
"fanOutDist");
1538 for (std::size_t e = 0; e < ovs.size(); ++e) {
1539 const std::vector<double> pv = ovs[e].at(
"p").get<std::vector<double> >();
1540 const std::vector<double> xv = ovs[e].at(
"x").get<std::vector<double> >();
1541 std::vector<T> p, x;
1542 for (std::size_t q = 0; q < pv.size(); ++q)
1544 for (std::size_t q = 0; q < xv.size(); ++q)
1548 detail::fork_dest_index(ovs[e], node_idx, name));
1551 if (nd.contains(
"fanOutProb")) {
1552 const json& ovs = nd.at(
"fanOutProb");
1553 for (std::size_t e = 0; e < ovs.size(); ++e)
1555 idx, ovs[e].at(
"class").get<std::size_t>(),
1556 detail::fork_dest_index(ovs[e], node_idx, name),
1557 ovs[e].at(
"value").get<
double>());
1561 if (type ==
"Cache") {
1569 const json empty_obj = json::object();
1570 const json& cj = nd.contains(
"cache") ? nd.at(
"cache") : empty_obj;
1572 cp.
nitems = detail::has_cache_key(nd, cj,
"numItems")
1573 ? detail::cache_key(nd, cj,
"numItems").get<std::size_t>()
1574 : cj.at(
"items").get<std::size_t>();
1575 cp.
itemcap = detail::has_cache_key(nd, cj,
"itemLevelCap")
1576 ? detail::cache_key(nd, cj,
"itemLevelCap").get<std::vector<int> >()
1577 : cj.at(
"capacity").get<std::vector<int> >();
1580 if (detail::has_cache_key(nd, cj,
"itemSizes"))
1581 cp.
itemsize = detail::cache_key(nd, cj,
"itemSizes").get<std::vector<int> >();
1582 if (detail::has_cache_key(nd, cj,
"costCaps")) {
1583 const json& cc = detail::cache_key(nd, cj,
"costCaps");
1584 if (cc.is_array()) {
1585 cp.
costcap = cc.get<std::vector<int> >();
1592 detail::has_cache_key(nd, cj,
"replacementStrategy")
1593 ? detail::cache_key(nd, cj,
"replacementStrategy").get<std::string>()
1594 : cj.value(
"replacement", std::string(
"RR")));
1595 cp.
pread.assign(K, std::vector<T>());
1600 if (detail::has_cache_key(nd, cj,
"popularity")) {
1601 const json& pop = detail::cache_key(nd, cj,
"popularity");
1602 for (
auto it = pop.begin(); it != pop.end(); ++it) {
1606 if (it.value().value(
"type", std::string()) ==
"Disabled")
continue;
1607 cp.
pread[class_idx.at(it.key()) - 1] =
1608 detail::pmf_from_json<T>(it.value(), cp.
nitems);
1609 cp.
preadkind[class_idx.at(it.key()) - 1] =
1610 detail::popularity_kind_from_json<T>(it.value(), cp.
nitems);
1613 auto fill_switch = [&](
const char* key, std::vector<std::size_t>& dst) {
1614 if (!detail::has_cache_key(nd, cj, key))
return;
1615 const json& blk = detail::cache_key(nd, cj, key);
1616 for (
auto it = blk.begin(); it != blk.end(); ++it)
1617 dst[class_idx.at(it.key()) - 1] = class_idx.at(it.value().get<std::string>());
1619 fill_switch(
"hitClass", cp.
hitclass);
1624 if (detail::has_cache_key(nd, cj,
"itemClass")) {
1625 const json& blk = detail::cache_key(nd, cj,
"itemClass");
1626 for (
auto it = blk.begin(); it != blk.end(); ++it)
1627 cp.
classitem[class_idx.at(it.key()) - 1] =
1628 static_cast<std::size_t
>(it.value().get<double>());
1639 if (detail::has_cache_key(nd, cj,
"admissionProb"))
1641 if (detail::has_cache_key(nd, cj,
"accessProb")) {
1642 const json& ap = detail::cache_key(nd, cj,
"accessProb");
1644 for (
const json& per_class : ap) {
1645 std::vector<Matrix<T> > row;
1646 for (
const json& g : per_class) {
1647 if (g.is_null() || g.empty()) {
1651 row.push_back(detail::mat_from_json<T>(g));
1653 cp.
accost.push_back(row);
1655 }
else if (detail::has_cache_key(nd, cj,
"accessGraph")) {
1661 const std::vector<Matrix<T> > shared =
1662 detail::mat_list_from_json<T>(detail::cache_key(nd, cj,
"accessGraph"));
1663 cp.
accost.assign(K, shared);
1667 if (detail::has_cache_key(nd, cj,
"initialState"))
1668 cp.
initstate = detail::num_vec_from_json<T>(detail::cache_key(nd, cj,
"initialState"));
1678 if (detail::has_cache_key(nd, cj,
"retrievalSystem")) {
1679 const json& rs = detail::cache_key(nd, cj,
"retrievalSystem");
1682 if (rs.contains(
"byClass")) {
1683 for (
auto rc = rs.at(
"byClass").begin(); rc != rs.at(
"byClass").end(); ++rc) {
1684 const std::size_t rdcls = class_idx.at(rc.key());
1685 std::vector<std::size_t> qnodes;
1686 for (
const auto&
qn : rc.value().at(
"queues"))
1687 qnodes.push_back(node_idx.at(
qn.get<std::string>()));
1690 if (!rc.value().contains(
"items"))
continue;
1691 for (
auto it2 = rc.value().at(
"items").begin();
1692 it2 != rc.value().at(
"items").end(); ++it2) {
1693 const std::size_t item = std::stoul(it2.key());
1696 class_idx.at(it2.value().get<std::string>());
1702 node_idx[name] = idx;
1705 if (type ==
"Join") {
1710 if (!nd.contains(
"forkNode"))
1712 "' names no 'forkNode': the document declares no "
1713 "Fork for it to close.");
1714 auto it = node_idx.find(nd.at(
"forkNode").get<std::string>());
1715 if (it == node_idx.end())
1717 "' references unknown fork '" +
1718 nd.at(
"forkNode").get<std::string>() +
"'");
1719 const std::size_t fork = it->second;
1722 idx = node_idx.at(name);
1727 if (nd.contains(
"joinStrategy") || nd.contains(
"joinQuorum")) {
1728 const std::string js = nd.value(
"joinStrategy", std::string(
"STD"));
1729 if (js !=
"STD" && js !=
"PARTIAL")
1731 js +
"', and the rules on the wire are STD and PARTIAL");
1735 nd.value(
"joinQuorum", 0.0));
1744 if (nd.contains(
"classSwitchMatrix") || nd.contains(
"csMatrix")) {
1746 nd.contains(
"classSwitchMatrix") ? nd.at(
"classSwitchMatrix") : nd.at(
"csMatrix");
1747 for (
auto ri = csm.begin(); ri != csm.end(); ++ri) {
1748 const std::size_t rr = class_idx.at(ri.key()) - 1;
1750 for (
auto ci = ri.value().begin(); ci != ri.value().end(); ++ci)
1751 C(rr, class_idx.at(ci.key()) - 1) =
1757 node_idx[name] = idx;
1769 for (std::size_t i = 0; i < nodes.size(); ++i) {
1770 if (node_type[i] !=
"Transition")
continue;
1771 const json& nd = nodes[i];
1772 const std::string name = nd.at(
"name").get<std::string>();
1773 if (!nd.contains(
"modes") || nd.at(
"modes").empty())
1774 throw InputError(
ErrorKind{
"Format"},
"network_reader: transition '" + name +
"' declares no mode");
1775 const json& modes = nd.at(
"modes");
1777 tp.
nmodes = modes.size();
1778 const std::size_t nn = nodes.size();
1779 const double inf = std::numeric_limits<double>::infinity();
1780 for (std::size_t m = 0; m < modes.size(); ++m) {
1781 const json& mj = modes[m];
1782 tp.
modenames.push_back(mj.value(
"name", std::string(
"Mode") + std::to_string(m + 1)));
1791 tp.
firingdep.push_back(std::function<T(
const std::vector<T>&)>());
1792 if (mj.contains(
"firingRateDependence")) {
1793 const json& frm = mj.at(
"firingRateDependence");
1794 if (!frm.contains(
"slots") || !frm.contains(
"scaling"))
1795 throw InputError(
ErrorKind{
"Format"},
"network_reader: the marking-dependent firing rate of mode " +
1796 std::to_string(m + 1) +
" of transition '" + name +
1797 "' carries no 'slots'/'scaling' lattice");
1798 std::vector<std::size_t> slot_pos;
1799 for (
const json& sm : frm.at(
"slots")) {
1800 const std::string on = sm.at(
"node").get<std::string>();
1801 const std::map<std::string, std::size_t>::const_iterator ni = node_idx.find(on);
1802 if (ni == node_idx.end())
1803 throw InputError(
ErrorKind{
"Format"},
"network_reader: the marking-dependent firing rate of "
1805 name +
"' reads node '" + on +
1806 "', which the model does not declare");
1809 if (sm.contains(
"class")) {
1810 const std::string scn = sm.at(
"class").get<std::string>();
1811 const std::map<std::string, std::size_t>::const_iterator sci = class_idx.find(scn);
1812 if (sci == class_idx.end())
1813 throw InputError(
ErrorKind{
"Format"},
"network_reader: the marking-dependent firing rate of "
1815 name +
"' reads class '" + scn +
1816 "', which the model does not declare");
1819 slot_pos.push_back((ni->second - 1) * K + (sr - 1));
1821 std::vector<long> cutoffs;
1822 if (frm.contains(
"cutoffs"))
1823 for (
const json& c : frm.at(
"cutoffs")) cutoffs.push_back(c.get<
long>());
1824 std::map<std::string, double> table;
1825 const json& sc = frm.at(
"scaling");
1826 for (json::const_iterator it = sc.begin(); it != sc.end(); ++it)
1827 table[it.key()] = it.value().get<
double>();
1828 tp.
firingdep.back() = [slot_pos, cutoffs,
1829 table](
const std::vector<T>& mk) -> T {
1831 for (std::size_t s = 0; s < slot_pos.size(); ++s) {
1832 long c = slot_pos[s] < mk.size()
1833 ?
static_cast<long>(std::llround(
1837 if (s < cutoffs.size() && c > cutoffs[s]) c = cutoffs[s];
1839 key += std::to_string(c);
1841 const std::map<std::string, double>::const_iterator hit = table.find(key);
1848 const bool immediate =
1849 mj.value(
"timingStrategy", std::string(
"TIMED")) ==
"IMMEDIATE";
1853 if (mj.contains(
"distribution")) proc = detail::dist_from_json<T>(mj.at(
"distribution"));
1857 tp.
nmodeservers.push_back(detail::num_value(mj,
"numServers", 1.0));
1865 tp.
firingprio.push_back(mj.value(
"firingPriority", 1.0));
1867 const std::size_t K = classes.size();
1871 const char* kArcKey[3] = {
"enablingConditions",
"inhibitingConditions",
1873 for (
int which = 0; which < 3; ++which) {
1874 if (!mj.contains(kArcKey[which]))
continue;
1875 for (
const json& arc : mj.at(kArcKey[which])) {
1876 const std::string on = arc.at(
"node").get<std::string>();
1877 const std::map<std::string, std::size_t>::const_iterator ni = node_idx.find(on);
1878 if (ni == node_idx.end())
1879 throw InputError(
ErrorKind{
"Format"},
"network_reader: transition '" + name +
"' names node '" +
1880 on +
"', which the model does not declare");
1886 if (arc.contains(
"class")) {
1887 const std::string cn = arc.at(
"class").get<std::string>();
1888 const std::map<std::string, std::size_t>::const_iterator ci =
1890 if (ci == class_idx.end())
1892 "' names class '" + cn +
1893 "', which the model does not declare");
1897 const std::size_t q = ni->second - 1;
1898 if (which == 0) enab(q, rr - 1) = enab(q, rr - 1) + cnt;
1899 else if (which == 1) inhib(q, rr - 1) = cnt;
1900 else fire(q, rr - 1) = fire(q, rr - 1) + cnt;
1905 tp.
firing.push_back(fire);
1913 const std::string mlabel =
"mode " + std::to_string(m + 1) +
" of transition '" + name +
"'";
1914 auto fluid_place = [&](
const json& e,
const char* key) -> std::size_t {
1915 const std::string on = e.at(
"node").get<std::string>();
1916 const std::map<std::string, std::size_t>::const_iterator ni = node_idx.find(on);
1917 bool is_place =
false;
1918 for (std::size_t j = 0; j < nodes.size() && !is_place; ++j)
1919 is_place = node_type[j] ==
"Place" && nodes[j].at(
"name").get<std::string>() == on;
1920 if (ni == node_idx.end() || !is_place)
1921 throw InputError(
ErrorKind{
"Format"},
"network_reader: the " + std::string(key) +
" of " +
1922 mlabel +
" names '" + on +
"', which is not a Place of the model");
1925 auto finite_num = [&](
const json& e,
const char* field,
const char* key) ->
double {
1926 const double x = detail::num_from_json(e.at(field));
1927 if (!std::isfinite(x))
1928 throw InputError(
ErrorKind{
"Format"},
"network_reader: the " + std::string(key) +
" of " +
1929 mlabel +
" carries a non-finite '" + field +
"'");
1932 if (mj.contains(
"fluidRates"))
1933 for (
const json& e : mj.at(
"fluidRates")) {
1935 a.
node = fluid_place(e,
"fluidRates");
1936 a.
value = finite_num(e,
"rate",
"fluidRates");
1939 if (mj.contains(
"fluidGuards"))
1940 for (
const json& e : mj.at(
"fluidGuards")) {
1942 g.
node = fluid_place(e,
"fluidGuards");
1943 const std::string op = e.at(
"op").get<std::string>();
1944 if (op !=
">=" && op !=
"<=")
1946 " has operator '" + op +
"'; the operators are '>=' and '<='");
1947 g.
sgn = op ==
">=" ? 1 : -1;
1948 g.
thr = finite_num(e,
"threshold",
"fluidGuards");
1951 " has a negative threshold");
1954 if (mj.contains(
"fluidImpulses"))
1955 for (
const json& e : mj.at(
"fluidImpulses")) {
1957 a.
node = fluid_place(e,
"fluidImpulses");
1958 a.
value = finite_num(e,
"delta",
"fluidImpulses");
1961 if (mj.contains(
"fluidRateDependence"))
1962 for (
const json& e : mj.at(
"fluidRateDependence")) {
1964 d.
node = fluid_place(e,
"fluidRateDependence");
1967 " is IMMEDIATE and declares a fluid rate dependence");
1970 " declares a fluid rate dependence on a non-exponential firing law");
1971 d.
fun = detail::fluiddep_table_to_fun(e.at(
"levels"), e.at(
"values"), mlabel);
1978 if (mj.contains(
"fluidFlowDependence")) {
1979 const std::size_t KF = classes.size();
1980 for (
const json& e : mj.at(
"fluidFlowDependence")) {
1982 fd.
node = fluid_place(e,
"fluidFlowDependence");
1983 if (e.contains(
"read")) {
1984 const json& rd = e.at(
"read");
1985 const json rl = rd.is_array() ? rd : json::array({rd});
1986 for (
const json& rn : rl) {
1989 fd.
read.push_back(fluid_place(probe,
"fluidFlowDependence read"));
1991 std::sort(fd.
read.begin(), fd.
read.end());
1995 if (!e.contains(
"slots") || !e.contains(
"scaling"))
1996 throw InputError(
ErrorKind{
"Format"},
"network_reader: the fluid flow dependence of " + mlabel +
1997 " carries no 'slots'/'scaling' lattice");
1998 std::vector<std::size_t> sflat;
1999 for (
const json& sm : e.at(
"slots")) {
2000 const std::string on = sm.at(
"node").get<std::string>();
2001 const std::map<std::string, std::size_t>::const_iterator ni = node_idx.find(on);
2002 if (ni == node_idx.end())
2004 mlabel +
" reads node '" + on +
"', which the model does not declare");
2006 if (sm.contains(
"class")) {
2007 const std::string cn = sm.at(
"class").get<std::string>();
2008 const std::map<std::string, std::size_t>::const_iterator ci = class_idx.find(cn);
2009 if (ci == class_idx.end())
2011 mlabel +
" names class '" + cn +
2012 "', which the model does not declare");
2015 sflat.push_back((ni->second - 1) * KF + (rr - 1));
2017 std::vector<long> cutoffs;
2018 if (e.contains(
"cutoffs"))
2019 for (
const json& c : e.at(
"cutoffs")) cutoffs.push_back(c.get<
long>());
2020 std::map<std::string, double> table;
2021 const json& sc = e.at(
"scaling");
2022 for (json::const_iterator it = sc.begin(); it != sc.end(); ++it)
2023 table[it.key()] = it.value().get<
double>();
2024 fd.
fun = [sflat, cutoffs, table](
const std::vector<double>& mk) ->
double {
2026 for (std::size_t s = 0; s < sflat.size(); ++s) {
2027 long c = sflat[s] < mk.size() ?
static_cast<long>(std::llround(mk[sflat[s]])) : 0L;
2029 if (s < cutoffs.size() && c > cutoffs[s]) c = cutoffs[s];
2031 key += std::to_string(c);
2033 const std::map<std::string, double>::const_iterator hit = table.find(key);
2034 return hit == table.end() ? 1.0 : hit->second;
2044 for (std::size_t i = 0; i < nodes.size(); ++i) {
2045 const json& nd = nodes[i];
2046 const std::string& type = node_type[i];
2047 const std::size_t idx = node_idx.at(nd.at(
"name").get<std::string>());
2052 if (nd.contains(
"servers") && (type ==
"Queue" || type ==
"Place"))
2055 if (nd.contains(
"service")) {
2056 const json& svc = nd.at(
"service");
2057 for (
auto it = svc.begin(); it != svc.end(); ++it) {
2058 auto cit = class_idx.find(it.key());
2059 if (cit == class_idx.end())
2060 throw InputError(
ErrorKind{
"Format"},
"network_reader: service names unknown class '" + it.key() +
2061 "' at node '" + nd.at(
"name").get<std::string>() +
"'");
2063 if (type ==
"Source") net.
set_arrival(idx, cit->second, d);
2068 if (type ==
"Queue" && nd.contains(
"scheduling") &&
2069 nd.at(
"scheduling").get<std::string>() ==
"POLLING") {
2070 if (nd.contains(
"pollingType"))
2071 net.
set_polling_type(idx, detail::polling_from_json(nd.at(
"pollingType").get<std::string>()),
2072 nd.value(
"pollingPar", 0));
2077 if (nd.contains(
"schedParams") && (type ==
"Queue" || type ==
"Delay")) {
2078 const json& sp = nd.at(
"schedParams");
2079 for (
auto it = sp.begin(); it != sp.end(); ++it) {
2080 auto cit = class_idx.find(it.key());
2081 if (cit != class_idx.end())
2093 const bool station_node =
2094 type ==
"Queue" || type ==
"Delay" || type ==
"Join" || type ==
"Place";
2095 if (nd.contains(
"dropRule") && station_node) {
2096 const json& dr = nd.at(
"dropRule");
2097 for (
auto it = dr.begin(); it != dr.end(); ++it) {
2098 auto cit = class_idx.find(it.key());
2099 if (cit != class_idx.end())
2101 detail::drop_from_json(it.value().get<std::string>()));
2104 if (nd.contains(
"buffer") && station_node)
2105 net.
set_capacity(idx, detail::num_from_json(nd.at(
"buffer")));
2107 if (nd.contains(
"classCap")) {
2108 const json& cc = nd.at(
"classCap");
2109 for (
auto it = cc.begin(); it != cc.end(); ++it) {
2110 auto cit = class_idx.find(it.key());
2111 if (cit != class_idx.end())
2123 if (nd.contains(
"patience")) {
2124 const json& pt = nd.at(
"patience");
2125 for (
auto it = pt.begin(); it != pt.end(); ++it) {
2126 auto cit = class_idx.find(it.key());
2127 if (cit == class_idx.end())
continue;
2128 const json& pj = it.value();
2130 pj.contains(
"impatienceType")
2131 ? detail::impatience_from_json(pj.at(
"impatienceType").get<std::string>())
2133 net.
set_patience(idx, cit->second, detail::dist_from_json<T>(pj.at(
"distribution")),
2137 if (nd.contains(
"orbitImpatience")) {
2138 const json& oi = nd.at(
"orbitImpatience");
2139 for (
auto it = oi.begin(); it != oi.end(); ++it) {
2140 auto cit = class_idx.find(it.key());
2141 if (cit != class_idx.end())
2145 if (nd.contains(
"batchRejectProb")) {
2146 const json& br = nd.at(
"batchRejectProb");
2147 for (
auto it = br.begin(); it != br.end(); ++it) {
2148 auto cit = class_idx.find(it.key());
2149 if (cit != class_idx.end())
2154 if (nd.contains(
"balking")) {
2155 const json& bk = nd.at(
"balking");
2156 for (
auto it = bk.begin(); it != bk.end(); ++it) {
2157 auto cit = class_idx.find(it.key());
2158 if (cit == class_idx.end())
continue;
2159 const json& bj = it.value();
2160 std::vector<typename qn::Station<T>::BalkingThreshold> ths;
2161 if (bj.contains(
"thresholds"))
2162 for (
const json& tj : bj.at(
"thresholds")) {
2164 th.
min_jobs = tj.value(
"minJobs", 0.0);
2168 th.
max_jobs = tj.value(
"maxJobs", -1.0);
2173 detail::balking_from_json(bj.at(
"strategy").get<std::string>()), ths);
2176 if (nd.contains(
"retrial")) {
2177 const json& rt = nd.at(
"retrial");
2178 for (
auto it = rt.begin(); it != rt.end(); ++it) {
2179 auto cit = class_idx.find(it.key());
2180 if (cit == class_idx.end())
continue;
2181 const lang::Distrib<T> delay = detail::dist_from_json<T>(it.value().at(
"delay"));
2186 int(detail::num_value(it.value(),
"maxAttempts", 0.0)));
2189 if (it.value().contains(
"policy") || it.value().contains(
"maxOrbit")) {
2191 if (it.value().contains(
"policy")) {
2192 const std::string p = it.value().at(
"policy").get<std::string>();
2193 if (p ==
"linear" || p ==
"per-customer" || p ==
"falin") policy = 1;
2194 else if (p ==
"constant" || p ==
"fixed" || p ==
"controller") policy = 2;
2197 "network_reader: unrecognized retrial policy '" + p +
"'");
2200 int(detail::num_value(it.value(),
"maxOrbit", -1.0)));
2206 if (nd.contains(
"setupTime")) {
2207 if (!nd.contains(
"delayOffTime"))
2209 "network_reader: node '" + nd.at(
"name").get<std::string>() +
2210 "' declares a setup time with no delay-off time; a server that never powers "
2211 "down never pays the setup, so the pair is meaningless alone");
2212 const json& su = nd.at(
"setupTime");
2213 const json& doff = nd.at(
"delayOffTime");
2214 for (
auto it = su.begin(); it != su.end(); ++it) {
2215 auto cit = class_idx.find(it.key());
2216 if (cit == class_idx.end() || !doff.contains(it.key()))
continue;
2218 detail::dist_from_json<T>(doff.at(it.key())));
2227 if (nd.contains(
"breakdown")) {
2228 const json& bd = nd.at(
"breakdown");
2229 if (!bd.contains(
"failure") || !bd.contains(
"repair"))
2231 "network_reader: node '" + nd.at(
"name").get<std::string>() +
2232 "' declares a breakdown without both a failure and a repair time; a server "
2233 "that never recovers is an absorbing model, not a breakdown");
2234 std::vector<lang::Distrib<T> > down(class_idx.size(),
2236 if (bd.contains(
"downService")) {
2237 const json& ds = bd.at(
"downService");
2238 for (
auto it = ds.begin(); it != ds.end(); ++it) {
2239 auto cit = class_idx.find(it.key());
2240 if (cit == class_idx.end())
continue;
2241 if (cit->second >= 1 && cit->second <= down.size())
2242 down[cit->second - 1] = detail::dist_from_json<T>(it.value());
2245 net.
set_breakdown(idx, detail::dist_from_json<T>(bd.at(
"failure")),
2246 detail::dist_from_json<T>(bd.at(
"repair")), down);
2248 if (nd.contains(
"switchoverTimes")) {
2262 const bool polling = type ==
"Queue" &&
2263 nd.value(
"scheduling", std::string()) ==
"POLLING";
2264 for (
const json& so : nd.at(
"switchoverTimes")) {
2265 auto cit = class_idx.find(so.at(
"from").get<std::string>());
2266 if (cit == class_idx.end())
continue;
2267 const std::string to = so.value(
"to", std::string());
2271 "network_reader: node '" + nd.at(
"name").get<std::string>() +
2272 "' is POLLING-scheduled but declares a switchover that names the "
2273 "class moved TO; a polling switchover is the walk out of one buffer");
2275 detail::dist_from_json<T>(so.at(
"distribution")));
2280 "network_reader: node '" + nd.at(
"name").get<std::string>() +
2281 "' declares a switchover without the class moved TO, which only a "
2282 "POLLING station's per-buffer walk may omit");
2283 auto tit = class_idx.find(to);
2284 if (tit == class_idx.end())
continue;
2286 detail::dist_from_json<T>(so.at(
"distribution")));
2289 if (nd.contains(
"serverTypes")) {
2290 for (
const json& st : nd.at(
"serverTypes")) {
2292 stype.
name = st.value(
"name", std::string());
2293 stype.
count = st.value(
"count", 1.0);
2294 if (st.contains(
"compatibleClasses")) {
2295 stype.
compatible.assign(classes.size(),
false);
2296 for (
const json& cn : st.at(
"compatibleClasses")) {
2297 auto cit = class_idx.find(cn.get<std::string>());
2298 if (cit != class_idx.end()) stype.
compatible[cit->second - 1] =
true;
2301 if (st.contains(
"service")) {
2303 const json& sv = st.at(
"service");
2304 for (
auto it = sv.begin(); it != sv.end(); ++it) {
2305 auto cit = class_idx.find(it.key());
2306 if (cit != class_idx.end())
2307 stype.
service[cit->second - 1] = detail::dist_from_json<T>(it.value());
2312 if (nd.contains(
"heteroSchedPolicy"))
2314 idx, detail::hetero_from_json(nd.at(
"heteroSchedPolicy").get<std::string>()));
2318 if (nd.contains(
"serverParallelism")) {
2319 const json& sp = nd.at(
"serverParallelism");
2320 for (
auto it = sp.begin(); it != sp.end(); ++it) {
2321 auto cit = class_idx.find(it.key());
2322 if (cit != class_idx.end())
2328 if (nd.contains(
"arrivalBatch")) {
2329 const json& ab = nd.at(
"arrivalBatch");
2330 for (
auto it = ab.begin(); it != ab.end(); ++it) {
2331 auto cit = class_idx.find(it.key());
2332 if (cit != class_idx.end())
2336 if (nd.contains(
"markedClasses")) {
2337 std::vector<std::size_t> marks;
2338 for (
const json& cn : nd.at(
"markedClasses")) {
2339 auto cit = class_idx.find(cn.get<std::string>());
2340 if (cit == class_idx.end())
2341 throw InputError(
ErrorKind{
"Format"},
"network_reader: node '" + nd.at(
"name").get<std::string>() +
2342 "' binds mark " + std::to_string(marks.size() + 1) +
2343 " to class '" + cn.get<std::string>() +
2344 "', which the model does not declare");
2345 marks.push_back(cit->second);
2359 if (nd.contains(
"oiServiceRate")) {
2360 std::vector<int> cut;
2361 if (nd.contains(
"oiCutoffs")) {
2362 const std::vector<double> cv = detail::num_vec_from_json<double>(nd.at(
"oiCutoffs"));
2363 for (std::size_t r = 0; r < classes.size() && r < cv.size(); ++r)
2364 cut.push_back(
static_cast<int>(std::lround(cv[r])));
2366 std::vector<std::vector<bool> > swap;
2367 if (nd.contains(
"swapGraph")) {
2368 const Matrix<T> sg = detail::mat_from_json<T>(nd.at(
"swapGraph"));
2369 swap.assign(sg.
rows(), std::vector<bool>(sg.
cols(),
false));
2370 for (std::size_t a = 0; a < sg.
rows(); ++a)
2371 for (std::size_t b = 0; b < sg.
cols(); ++b)
2375 detail::oi_rate_from_json<T>(nd.at(
"oiServiceRate"), cut, classes.size()),
2380 if (nd.contains(
"initialState") && type ==
"Place") {
2382 }
else if (nd.contains(
"initialState") && !nd.contains(
"stateSpace")) {
2391 const std::vector<T> row = detail::num_vec_from_json<T>(nd.at(
"initialState"));
2394 for (std::size_t k = 0; k < row.size(); ++k) space(0, k) = row[k];
2398 if (nd.contains(
"statePrior") || nd.contains(
"stateSpace")) {
2399 if (!nd.contains(
"statePrior") || !nd.contains(
"stateSpace"))
2401 "network_reader: node '" + nd.at(
"name").get<std::string>() +
2402 "' declares one of stateSpace / statePrior without the other; the prior is a "
2403 "distribution over the ROWS of that space and means nothing alone");
2404 net.
set_state_prior(idx, detail::mat_from_json<T>(nd.at(
"stateSpace")),
2405 detail::num_vec_from_json<T>(nd.at(
"statePrior")));
2407 if (nd.contains(
"departureDiscipline")) {
2408 const json& dd = nd.at(
"departureDiscipline");
2409 for (
auto it = dd.begin(); it != dd.end(); ++it) {
2410 auto cit = class_idx.find(it.key());
2411 if (cit != class_idx.end())
2413 idx, cit->second, detail::departure_from_json(it.value().get<std::string>()));
2421 for (
int kind = 0; kind < 3; ++kind) {
2422 static const char* kKeys[] = {
"loadDependence",
"classDependence",
2424 static const char* kTypes[] = {
"loadDependent",
"classDependent",
2426 if (!nd.contains(kKeys[kind]))
continue;
2427 if (type !=
"Queue" && type !=
"Delay")
2429 std::string(
"network_reader: node '") + nd.at(
"name").get<std::string>() +
2430 "' carries '" + kKeys[kind] +
2431 "', which scales a SERVICE rate and is only meaningful at a Queue or a "
2433 const detail::json& blk = nd.at(kKeys[kind]);
2434 if (blk.value(
"type", std::string(kTypes[kind])) != kTypes[kind])
2436 "' at node '" + nd.at(
"name").get<std::string>() +
2437 "' declares type '" +
2438 blk.value(
"type", std::string(
"?")) +
2439 "', which this reader does not implement");
2440 if (!blk.contains(
"scaling"))
2442 "' at node '" + nd.at(
"name").get<std::string>() +
2443 "' carries no 'scaling'");
2453 std::vector<int> cut;
2454 if (blk.contains(
"cutoffs")) {
2455 const std::vector<double> cv = detail::num_vec_from_json<double>(blk.at(
"cutoffs"));
2456 for (std::size_t r = 0; r < classes.size() && r < cv.size(); ++r)
2457 cut.push_back(
static_cast<int>(std::lround(cv[r])));
2460 detail::cd_scaling_from_json<T>(blk.at(
"scaling"), cut, classes.size());
2461 const std::vector<T> peak = detail::cd_peak_from_json<T>(blk, blk.at(
"scaling"));
2468 if (nd.contains(
"immediateFeedback")) {
2469 const json& imf = nd.at(
"immediateFeedback");
2470 for (
auto it = imf.begin(); it != imf.end(); ++it) {
2471 auto cit = class_idx.find(it.key());
2472 if (cit != class_idx.end() && it.value().get<
bool>())
2479 const json& routing = model.at(
"routing");
2481 const json& mat = routing.at(
"matrix");
2482 for (
auto ck = mat.begin(); ck != mat.end(); ++ck) {
2484 const std::string key = ck.key();
2485 const std::size_t comma = key.find(
',');
2486 const std::string cs = comma == std::string::npos ? key : key.substr(0, comma);
2487 const std::string cd = comma == std::string::npos ? key : key.substr(comma + 1);
2488 const std::size_t r = class_idx.at(cs);
2489 const std::size_t s = class_idx.at(cd);
2490 for (
auto si = ck.value().begin(); si != ck.value().end(); ++si) {
2491 const std::size_t from = node_idx.at(si.key());
2492 for (
auto di = si.value().begin(); di != si.value().end(); ++di)
2493 P.set(r, s, from, node_idx.at(di.key()),
2506 if (model.contains(
"routingStrategies")) {
2507 const json& rs = model.at(
"routingStrategies");
2508 for (
auto ni = rs.begin(); ni != rs.end(); ++ni) {
2509 auto nit = node_idx.find(ni.key());
2510 for (
auto ci = ni.value().begin(); ci != ni.value().end(); ++ci) {
2512 detail::routing_from_json(ci.value().get<std::string>());
2520 if (nit == node_idx.end()) {
2522 throw InputError(
ErrorKind{
"Format"},
"network_reader: routingStrategies names node '" + ni.key() +
2523 "', which the model does not declare");
2526 auto cit = class_idx.find(ci.key());
2527 if (cit == class_idx.end())
continue;
2532 if (model.contains(
"routingWeights")) {
2533 const json& rw = model.at(
"routingWeights");
2534 for (
auto ni = rw.begin(); ni != rw.end(); ++ni) {
2535 auto nit = node_idx.find(ni.key());
2536 if (nit == node_idx.end())
continue;
2537 for (
auto ci = ni.value().begin(); ci != ni.value().end(); ++ci) {
2538 auto cit = class_idx.find(ci.key());
2539 if (cit == class_idx.end())
continue;
2540 std::map<std::size_t, double> w;
2541 for (
auto di = ci.value().begin(); di != ci.value().end(); ++di) {
2542 auto dit = node_idx.find(di.key());
2543 if (dit != node_idx.end()) w[dit->second] = di.value().get<
double>();
2549 if (model.contains(
"routingParams")) {
2550 const json& rp = model.at(
"routingParams");
2551 for (
auto ni = rp.begin(); ni != rp.end(); ++ni) {
2552 auto nit = node_idx.find(ni.key());
2553 if (nit == node_idx.end())
continue;
2554 for (
auto ci = ni.value().begin(); ci != ni.value().end(); ++ci) {
2555 auto cit = class_idx.find(ci.key());
2556 if (cit == class_idx.end() || !ci.value().contains(
"d"))
continue;
2571 if (model.contains(
"stateDepRouting")) {
2572 const json& sd = model.at(
"stateDepRouting");
2573 for (
const char* k : {
"entry",
"departure",
"class",
"branches",
"level",
"C",
"d"})
2574 if (!sd.contains(k))
2575 throw InputError(
ErrorKind{
"Format"}, std::string(
"network_reader: 'stateDepRouting' carries no '") + k +
2577 auto node_of = [&](
const std::string& nm) -> std::size_t {
2578 const std::map<std::string, std::size_t>::const_iterator it = node_idx.find(nm);
2579 if (it == node_idx.end())
2580 throw InputError(
ErrorKind{
"Format"},
"network_reader: 'stateDepRouting' names node '" + nm +
2581 "', which the model does not declare");
2584 const std::map<std::string, std::size_t>::const_iterator ci =
2585 class_idx.find(sd.at(
"class").get<std::string>());
2586 if (ci == class_idx.end())
2588 sd.at(
"class").get<std::string>() +
2589 "', which the model does not declare");
2590 std::vector<std::vector<std::size_t>> branches;
2591 for (
const json& b : sd.at(
"branches")) {
2592 std::vector<std::size_t> centres;
2593 for (
const json& nm : b) centres.push_back(node_of(nm.get<std::string>()));
2594 branches.push_back(centres);
2596 std::vector<std::size_t> level;
2597 for (
const json& v : sd.at(
"level")) level.push_back(
static_cast<std::size_t
>(v.get<
double>()));
2598 std::vector<double> C;
2599 for (
const json& v : sd.at(
"C")) C.push_back(v.get<
double>());
2600 const json& dj = sd.at(
"d");
2601 Matrix<double> d(dj.size(), dj.empty() ? 0 : dj.at(0).size(), 0.0);
2602 for (std::size_t t = 0; t < dj.size(); ++t) {
2603 if (dj.at(t).size() != d.
cols())
2604 throw InputError(
ErrorKind{
"Format"},
"network_reader: 'stateDepRouting' carries a ragged coefficient "
2606 for (std::size_t b = 0; b < d.
cols(); ++b) d(t, b) = dj.at(t).at(b).get<
double>();
2609 node_of(sd.at(
"departure").get<std::string>()), branches, level, C,
2621 if (model.contains(
"globalDependence")) {
2622 const json& blk = model.at(
"globalDependence");
2623 if (blk.value(
"type", std::string(
"globalDependent")) !=
"globalDependent")
2625 std::string(
"network_reader: 'globalDependence' declares type '") +
2626 blk.value(
"type", std::string(
"?")) +
"', which this reader does not implement");
2627 if (!blk.contains(
"scaling"))
2628 throw InputError(
ErrorKind{
"Format"},
"network_reader: 'globalDependence' carries no 'scaling'");
2630 const std::size_t M = sn0.nstations, K = sn0.nclasses;
2631 std::vector<std::size_t> slot_st, slot_cl;
2632 if (blk.contains(
"slots"))
2633 for (
const json& sm : blk.at(
"slots")) {
2634 auto nit = node_idx.find(sm.at(
"station").get<std::string>());
2635 auto cit = class_idx.find(sm.at(
"class").get<std::string>());
2636 if (nit == node_idx.end() || cit == class_idx.end())
2638 "network_reader: 'globalDependence' names a station or class the model "
2639 "does not declare");
2640 slot_st.push_back(sn0.nodes[nit->second - 1].station - 1);
2641 slot_cl.push_back(cit->second - 1);
2643 const std::size_t P = slot_st.size();
2644 std::vector<int> cuts(P, 0);
2645 if (blk.contains(
"cutoffs")) {
2646 const std::vector<double> cv = detail::num_vec_from_json<double>(blk.at(
"cutoffs"));
2647 for (std::size_t d = 0; d < P && d < cv.size(); ++d)
2648 cuts[d] =
static_cast<int>(std::lround(cv[d]));
2650 const int wcut = blk.value(
"cutoff", 10);
2651 std::map<std::string, std::vector<T>> tbl;
2652 for (
auto it = blk.at(
"scaling").begin(); it != blk.at(
"scaling").end(); ++it)
2653 tbl[it.key()] = detail::num_vec_from_json<T>(it.value());
2655 if (blk.contains(
"peak")) {
2656 const std::vector<T> pv = detail::num_vec_from_json<T>(blk.at(
"peak"));
2657 for (std::size_t j = 0; j < peak.size() && j < pv.size(); ++j) peak[j] = pv[j];
2661 [slot_st, slot_cl, cuts, tbl,
ones, K, P](
const std::vector<T>& n) {
2663 if (P == 0) key =
"0";
2665 for (std::size_t d = 0; d < P; ++d) {
2668 if (x > cuts[d]) x = cuts[d];
2670 key += std::to_string(x);
2672 typename std::map<std::string, std::vector<T>>::const_iterator it = tbl.find(key);
2673 return it == tbl.end() ?
ones : it->second;
2684 if (model.contains(
"finiteCapacityRegions")) {
2685 for (
const json& rj : model.at(
"finiteCapacityRegions")) {
2686 std::vector<std::size_t> members;
2690 std::vector<double> cap(classes.size(), -1.0);
2691 auto read_class_map = [&](
const json& blk, std::vector<double>& dst) {
2692 for (
auto it = blk.begin(); it != blk.end(); ++it) {
2693 auto cit = class_idx.find(it.key());
2694 if (cit == class_idx.end())
continue;
2695 const double v = it.value().get<
double>();
2696 double& slot = dst[cit->second - 1];
2697 slot = slot == -1.0 ? v : std::min(slot, v);
2700 if (rj.contains(
"classMaxJobs")) read_class_map(rj.at(
"classMaxJobs"), cap);
2701 for (
const json& sj : rj.at(
"stations")) {
2702 auto nit = node_idx.find(sj.at(
"node").get<std::string>());
2703 if (nit == node_idx.end())
2705 rj.value(
"name", std::string(
"?")) +
"' names node '" +
2706 sj.at(
"node").get<std::string>() +
2707 "', which the model does not declare");
2708 members.push_back(nit->second);
2709 if (sj.contains(
"classCap")) read_class_map(sj.at(
"classCap"), cap);
2711 std::vector<double> mem(classes.size(), -1.0);
2712 if (rj.contains(
"classMaxMemory")) read_class_map(rj.at(
"classMaxMemory"), mem);
2715 for (
const json& sj : rj.at(
"stations")) {
2716 if (sj.contains(
"classSize"))
2717 for (
auto it = sj.at(
"classSize").begin(); it != sj.at(
"classSize").end(); ++it) {
2718 auto cit = class_idx.find(it.key());
2719 if (cit != class_idx.end())
2720 size[cit->second - 1] =
2723 if (sj.contains(
"classWeight"))
2724 for (
auto it = sj.at(
"classWeight").begin(); it != sj.at(
"classWeight").end();
2726 auto cit = class_idx.find(it.key());
2727 if (cit != class_idx.end())
2728 weight[cit->second - 1] =
2733 if (rj.contains(
"dropRule"))
2734 for (
auto it = rj.at(
"dropRule").begin(); it != rj.at(
"dropRule").end(); ++it) {
2735 auto cit = class_idx.find(it.key());
2736 if (cit != class_idx.end())
2737 rule[cit->second - 1] = detail::drop_from_json(it.value().get<std::string>());
2739 const std::size_t
reg =
2740 net.
add_region(members, cap, rj.value(
"globalMaxJobs", -1.0), rule, mem, size,
2741 rj.value(
"globalMaxMemory", -1.0),
2742 rj.value(
"name", std::string()));
2744 if (rj.contains(
"constraintA") && rj.contains(
"constraintB"))
2746 detail::num_vec_from_json<T>(rj.at(
"constraintB")));
2759 if (model.contains(
"rewards")) {
2760 const std::size_t K = classes.size();
2761 for (
const json& rw : model.at(
"rewards")) {
2762 const std::string nm = rw.at(
"name").get<std::string>();
2763 const std::string kind = rw.at(
"type").get<std::string>();
2764 const std::string node_name = rw.at(
"node").get<std::string>();
2765 auto nit = node_idx.find(node_name);
2766 if (nit == node_idx.end())
2767 throw InputError(
ErrorKind{
"Format"},
"network_reader: reward '" + nm +
"' names node '" + node_name +
2768 "', which the model does not declare");
2771 std::size_t ist = 0;
2772 for (std::size_t s = 0; s < net.
get_struct().station_to_node.size(); ++s)
2773 if (net.
get_struct().station_to_node[s] == nit->second) ist = s + 1;
2777 "', which is not a station and so has no job count");
2781 std::size_t cls = 0;
2782 if (rw.contains(
"class")) {
2783 auto cit = class_idx.find(rw.at(
"class").get<std::string>());
2784 if (cit == class_idx.end())
2785 throw InputError(
ErrorKind{
"Format"},
"network_reader: reward '" + nm +
"' names class '" +
2786 rw.at(
"class").get<std::string>() +
2787 "', which the model does not declare");
2790 const double nservers = net.
get_struct().stations[ist - 1].nservers;
2791 const double cap = net.
get_struct().stations[ist - 1].cap;
2792 const std::size_t base = (ist - 1) * K;
2796 if (kind ==
"QLen") {
2797 net.
set_reward(nm, [base, K, cls](
const std::vector<T>& n) {
2798 if (cls)
return n[base + cls - 1];
2800 for (std::size_t k = 0; k < K; ++k) s = T(s + n[base + k]);
2802 }, kind, nit->second, cls);
2803 }
else if (kind ==
"Util") {
2806 net.
set_reward(nm, [base, K, cls, nservers](
const std::vector<T>& n) {
2808 if (cls) s = n[base + cls - 1];
2810 for (std::size_t k = 0; k < K; ++k) s = T(s + n[base + k]);
2813 }, kind, nit->second, cls);
2814 }
else if (kind ==
"Blocking") {
2815 net.
set_reward(nm, [base, K, cap](
const std::vector<T>& n) {
2817 for (std::size_t k = 0; k < K; ++k) s = T(s + n[base + k]);
2820 }, kind, nit->second, cls);
2823 "network_reader: reward '" + nm +
"' is of type '" + kind +
2824 "', and the reproducible templates are QLen, Util and Blocking; a Custom "
2825 "reward wraps an arbitrary function and is not on the wire at all");
2835 std::ifstream in(path.c_str());
2840 }
catch (
const detail::json::parse_error& e) {
2841 throw InputError(
ErrorKind{
"Format"},
"network_reader: malformed JSON in " + path +
": " + e.what());
UnsupportedError(const std::string &what)
A network plus its refreshed NetworkStruct.
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.
void set_orbit_policy(std::size_t node, std::size_t cls, int policy, int max_orbit)
The policy and bound of Queue.setOrbit(class, dist, policy, maxOrbit) on a class already declared by ...
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,...
void set_fluid_capacity(std::size_t node, double cap)
Place.setFluidCapacity(B): the level ceiling of a fluid place, Inf when unbounded.
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_fluid_level(std::size_t node, double level)
Place.setFluidLevel(x0): the initial level of a fluid place.
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_place_fluid(std::size_t node, bool fluid)
Place.setFluid(flag): the place holds one real level in [0, fluid capacity] instead of tokens,...
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.
The exception types the port throws.
Enumerations and the minimal distribution descriptor shared by the model layer of the C++ port.
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)
The (D0,D1) pair that reaches sn.proc.
SchedStrategy
Scheduling disciplines, with the values of MATLAB SchedStrategy.
DropStrategy
Blocking and loss rules, with the values of MATLAB DropStrategy.
@ IMMEDIATE
fires with zero delay, resolved by weight and priority
@ TIMED
fires after its firing distribution elapses
BalkingStrategy
Balking rules, with the values of MATLAB BalkingStrategy.
SignalType
G-network signal classes, with the values of MATLAB SignalType.
@ REPLY
completes a synchronous call, releasing a held server
@ NEGATIVE
removes a batch of jobs (Gelenbe's negative customer)
@ CATASTROPHE
removes EVERY job at the station
RoutingStrategy
Routing strategies, with the values of MATLAB RoutingStrategy.
DepartureDiscipline
When a Place releases a served token, MATLAB DepartureDiscipline.
Distrib< T > prior_continuous(const Distrib< T > ¶m_dist, const std::function< Distrib< T >(const T &)> &factory)
Prior(paramDist, distFactory): the continuous form.
RemovalPolicy
Which job a negative signal removes, with the values of MATLAB RemovalPolicy.
@ FCFS
the oldest waiting job; servers only once nobody waits
@ LCFS
the newest waiting job; servers only once nobody waits
@ RANDOM
uniform over waiting AND in-service jobs
HeteroSchedPolicy
How a heterogeneous station picks among its server types, MATLAB HeteroSchedPolicy.
PollingType
Polling service disciplines, with the values of MATLAB PollingType.
Distrib< T > prior_discrete(const std::vector< Distrib< T > > &alternatives, const std::vector< T > &probabilities)
Prior(distributions, probabilities): the discrete form.
std::function< std::vector< T >(const std::vector< T > &)> CdScaling
A class-dependent scaling map, sn.cdscaling.
ReplacementStrategy
Cache replacement policies, with the values of MATLAB ReplacementStrategy.
ImpatienceType
Impatience kinds, with the values of MATLAB ImpatienceType.
const std::string * get(const Element *el, const std::string &key)
el.get(key), with Python's None as a null pointer.
const std::map< std::string, std::vector< std::string > > & table()
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.
The <kind> part of a LINE:<category>:<kind> identifier (see _kb/17-error-vocabulary....
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 mph(const std::vector< T > &alpha, const Matrix< T > &S, const std::vector< std::vector< T > > &exits)
MPH(alpha, S, {s_k}): the marked phase-type law, a PH with K labelled exits.
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)
The same for any number of branches, which is what MATLAB HyperExp accepts and what the writers emit ...
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)
HyperExp(p, lambda1, lambda2): phase i chosen with probability p_i.
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 point mass at zero.
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.
The parameters of a Cache node, MATLAB's sn.nodeparam{ind} for a Cache.
int retrieval_capacity
Delayed-hit retrieval system (Cache.setRetrievalSystem).
T qlru
q-LRU admission probability; 1 admits every miss (plain LRU).
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
One fluid arc of a mode: node is the 1-based fluid place, value a rate or an impulse.
A fluid rate dependence: the firing rate is multiplied by fun(level(node)).
std::function< double(double)> fun
A marking-dependent fluid flow (setFluidFlowDependence): the flow of the mode on fluid place node is ...
std::vector< std::size_t > read
std::function< double(const std::vector< double > &)> fun
A fluid guard: the mode is enabled only while level(node) >= thr (sgn = +1) or <= thr (sgn = -1).
The parameters of a Transition node: MATLAB sn.nodeparam{ind} for an SPN transition,...
std::vector< std::vector< FluidDep > > fluiddep
std::vector< double > firingprio
firing priority per mode
std::vector< std::vector< FluidGuard > > fluidguard
std::vector< std::vector< FluidFlowDep > > fluidflowdep
std::vector< lang::TimingStrategy > timing
immediate or timed
std::vector< std::string > modenames
std::vector< std::vector< FluidArc > > fluidimpulse
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::vector< FluidArc > > fluidrate
FSPN arcs per mode, MATLAB sn.transparam{ind}.fluidrate/fluidguard/fluidimpulse/fluiddep.
std::vector< std::size_t > firingphases
phase count per mode, 0 when non-Markovian