5#ifndef LINE_IO_NETWORK_WRITER_H
6#define LINE_IO_NETWORK_WRITER_H
76 throw UnsupportedError(ErrorKind{
"Export"},
"network_writer: unnamed node type");
89 throw UnsupportedError(ErrorKind{
"Export"},
"network_writer: unnamed drop strategy");
102 throw UnsupportedError(ErrorKind{
"Export"},
"network_writer: unnamed replacement strategy");
112 throw UnsupportedError(ErrorKind{
"Export"},
"network_writer: unnamed polling type");
122 throw UnsupportedError(ErrorKind{
"Export"},
"network_writer: a patience block with no impatience type");
132 throw UnsupportedError(ErrorKind{
"Export"},
"network_writer: a balking block with no strategy");
155 throw UnsupportedError(ErrorKind{
"Export"},
"network_writer: unnamed routing strategy");
167 throw UnsupportedError(ErrorKind{
"Export"},
"network_writer: unnamed heterogeneous scheduling policy");
181 for (std::size_t i = 0; i < out.size(); ++i)
182 out[i] =
static_cast<char>(std::toupper(
static_cast<unsigned char>(out[i])));
188json mat_to_json(
const Matrix<T>& M) {
189 json rows = json::array();
190 for (std::size_t i = 0; i < M.rows(); ++i) {
191 json row = json::array();
192 for (std::size_t j = 0; j < M.cols(); ++j)
193 row.push_back(num_traits<T>::to_double(M(i, j)));
200json vec_to_json(
const std::vector<T>& v) {
201 json a = json::array();
202 for (
const T& x : v) a.push_back(num_traits<T>::to_double(x));
220inline void wire_nonfinite(json& j) {
221 if (j.is_object() || j.is_array()) {
222 for (json::iterator it = j.begin(); it != j.end(); ++it) wire_nonfinite(*it);
225 if (!j.is_number_float())
return;
226 const double v = j.get<
double>();
229 else if (std::isinf(v))
230 j = json(v > 0.0 ?
"Infinity" :
"-Infinity");
241inline json discrete_support(std::size_t n) {
242 json a = json::array();
243 for (std::size_t i = 1; i <= n; ++i) a.push_back(
static_cast<double>(i));
256json dist_to_json(
const lang::Distrib<T>& d) {
260 const std::vector<T>& p = d.params;
261 auto need = [&](std::size_t n,
const char* who) {
263 throw InputError(std::string(
"network_writer: a ") + who +
" carries " +
264 std::to_string(p.size()) +
" parameters, and " + std::to_string(n) +
265 " are needed to write it back");
268 case ProcessType::DISABLED:
269 case ProcessType::IMMEDIATE:
271 case ProcessType::EXP:
273 j[
"params"][
"lambda"] = num_traits<T>::to_double(p[0]);
275 case ProcessType::DET:
276 j[
"params"][
"value"] = num_traits<T>::to_double(d.mean);
278 case ProcessType::ERLANG:
280 j[
"params"][
"lambda"] = num_traits<T>::to_double(p[0]);
281 j[
"params"][
"k"] =
static_cast<long>(std::lround(num_traits<T>::to_double(p[1])));
283 case ProcessType::HYPEREXP: {
287 json pv = json::array(), lv = json::array();
289 pv.push_back(num_traits<T>::to_double(p[0]));
290 pv.push_back(1.0 - num_traits<T>::to_double(p[0]));
291 lv.push_back(num_traits<T>::to_double(p[1]));
292 lv.push_back(num_traits<T>::to_double(p[2]));
294 const std::size_t n = p.size() / 2;
295 for (std::size_t i = 0; i < n; ++i) pv.push_back(num_traits<T>::to_double(p[i]));
296 for (std::size_t i = 0; i < n; ++i) lv.push_back(num_traits<T>::to_double(p[n + i]));
298 j[
"params"][
"p"] = pv;
299 j[
"params"][
"lambda"] = lv;
302 case ProcessType::COXIAN:
303 case ProcessType::COX2: {
304 const std::size_t n = p.size() / 2;
305 json mu = json::array(), phi = json::array();
306 for (std::size_t i = 0; i < n; ++i) mu.push_back(num_traits<T>::to_double(p[i]));
307 for (std::size_t i = 0; i < n; ++i) phi.push_back(num_traits<T>::to_double(p[n + i]));
310 j[
"type"] =
"Coxian";
311 j[
"params"][
"mu"] = mu;
312 j[
"params"][
"phi"] = phi;
315 case ProcessType::PH:
316 case ProcessType::APH:
317 case ProcessType::ME: {
318 json alpha = json::array();
319 for (
const T& v : p) alpha.push_back(num_traits<T>::to_double(v));
320 if (d.type == ProcessType::ME) {
321 j[
"params"][
"alpha"] = alpha;
322 j[
"params"][
"A"] = mat_to_json(d.D0);
324 j[
"ph"][
"alpha"] = alpha;
325 j[
"ph"][
"T"] = mat_to_json(d.D0);
329 case ProcessType::MAP:
330 j[
"map"][
"D0"] = mat_to_json(d.D0);
331 j[
"map"][
"D1"] = mat_to_json(d.D1);
333 case ProcessType::RAP:
334 j[
"params"][
"H0"] = mat_to_json(d.D0);
335 j[
"params"][
"H1"] = mat_to_json(d.D1);
337 case ProcessType::DMAP:
338 j[
"params"][
"D0"] = mat_to_json(d.D0);
339 j[
"params"][
"D1"] = mat_to_json(d.D1);
341 case ProcessType::MMPP2: {
345 if (d.D0.rows() != 2)
346 throw InputError(
"network_writer: an MMPP2 must be of order two");
347 j[
"params"][
"lambda0"] = num_traits<T>::to_double(d.D1(0, 0));
348 j[
"params"][
"lambda1"] = num_traits<T>::to_double(d.D1(1, 1));
349 j[
"params"][
"sigma0"] = num_traits<T>::to_double(d.D0(0, 1));
350 j[
"params"][
"sigma1"] = num_traits<T>::to_double(d.D0(1, 0));
353 case ProcessType::MPH: {
359 const std::size_t H = d.D0.rows();
360 std::vector<double> alpha(H, 0.0);
361 for (std::size_t i = 0; i < H; ++i) {
363 for (std::size_t q = 0; q < H; ++q) row += num_traits<T>::to_double(d.D1(i, q));
365 for (std::size_t q = 0; q < H; ++q)
366 alpha[q] = num_traits<T>::to_double(d.D1(i, q)) / row;
370 j[
"mph"][
"alpha"] = alpha;
371 j[
"mph"][
"S"] = mat_to_json(d.D0);
372 json exits = json::array();
373 for (
const Matrix<T>& Dk : d.Dmark) {
374 std::vector<double> sk(H, 0.0);
375 for (std::size_t i = 0; i < H; ++i)
376 for (std::size_t q = 0; q < H; ++q)
377 sk[i] += num_traits<T>::to_double(Dk(i, q));
380 j[
"mph"][
"exit"] = exits;
383 case ProcessType::MMAP: {
384 j[
"mmap"][
"D0"] = mat_to_json(d.D0);
385 json blocks = json::array();
386 for (
const Matrix<T>& Dk : d.Dmark) blocks.push_back(mat_to_json(Dk));
387 j[
"mmap"][
"D1k"] = blocks;
390 case ProcessType::BMAP: {
391 json blocks = json::array();
392 blocks.push_back(mat_to_json(d.D0));
393 for (
const Matrix<T>& Dk : d.Dmark) blocks.push_back(mat_to_json(Dk));
394 j[
"params"][
"D"] = blocks;
397 case ProcessType::UNIFORM:
399 j[
"params"][
"a"] = num_traits<T>::to_double(p[0]);
400 j[
"params"][
"b"] = num_traits<T>::to_double(p[1]);
402 case ProcessType::PARETO:
404 j[
"params"][
"alpha"] = num_traits<T>::to_double(p[0]);
405 j[
"params"][
"scale"] = num_traits<T>::to_double(p[1]);
407 case ProcessType::GAMMA:
409 j[
"params"][
"alpha"] = num_traits<T>::to_double(p[0]);
410 j[
"params"][
"beta"] = num_traits<T>::to_double(p[1]);
412 case ProcessType::WEIBULL:
416 j[
"params"][
"alpha"] = num_traits<T>::to_double(p[0]);
417 j[
"params"][
"beta"] = num_traits<T>::to_double(p[1]);
419 case ProcessType::LOGNORMAL:
420 need(2,
"Lognormal");
421 j[
"params"][
"mu"] = num_traits<T>::to_double(p[0]);
422 j[
"params"][
"sigma"] = num_traits<T>::to_double(p[1]);
424 case ProcessType::DUNIFORM:
425 need(2,
"DiscreteUniform");
426 j[
"params"][
"min"] = num_traits<T>::to_double(p[0]);
427 j[
"params"][
"max"] = num_traits<T>::to_double(p[1]);
429 case ProcessType::BERNOULLI:
430 need(1,
"Bernoulli");
431 j[
"params"][
"p"] = num_traits<T>::to_double(p[0]);
433 case ProcessType::BINOMIAL:
435 j[
"params"][
"n"] =
static_cast<long>(std::lround(num_traits<T>::to_double(p[0])));
436 j[
"params"][
"p"] = num_traits<T>::to_double(p[1]);
438 case ProcessType::POISSON:
440 j[
"params"][
"lambda"] = num_traits<T>::to_double(p[0]);
442 case ProcessType::GEOMETRIC:
443 need(1,
"Geometric");
444 j[
"params"][
"p"] = num_traits<T>::to_double(p[0]);
446 case ProcessType::ZIPF:
448 j[
"params"][
"s"] = num_traits<T>::to_double(p[0]);
449 j[
"params"][
"n"] =
static_cast<long>(std::lround(num_traits<T>::to_double(p[1])));
451 case ProcessType::DISCRETESAMPLER:
452 j[
"params"][
"p"] = vec_to_json(p);
453 if (!d.trace.empty()) j[
"params"][
"x"] = vec_to_json(d.trace);
455 case ProcessType::EMPIRICALCDF:
456 j[
"params"][
"x"] = vec_to_json(d.trace);
457 j[
"params"][
"F"] = vec_to_json(p);
459 case ProcessType::REPLAYER:
466 if (!d.trace_file.empty()) j[
"params"][
"fileName"] = d.trace_file;
467 j[
"params"][
"mean"] = num_traits<T>::to_double(d.mean);
468 if (d.trace_file.empty())
469 j[
"params"][
"scv"] = num_traits<T>::to_double(d.scv);
471 case ProcessType::MMAPT:
472 case ProcessType::MPHT: {
473 j[
"params"][
"breakpoints"] = vec_to_json(d.sched_bp);
474 j[
"params"][
"cyclic"] = d.sched_cyclic;
475 json A = json::array();
476 for (
const Matrix<T>& M : d.sched_D0) A.push_back(mat_to_json(M));
477 json blocks = json::array();
478 for (
const std::vector<Matrix<T> >& per_mark : d.sched_Dmark) {
479 json segs = json::array();
480 for (
const Matrix<T>& M : per_mark) segs.push_back(mat_to_json(M));
481 blocks.push_back(segs);
488 j[
"params"][
"D0"] = A;
489 j[
"params"][
"D1k"] = blocks;
492 case ProcessType::BMMAPT: {
497 j[
"params"][
"breakpoints"] = vec_to_json(d.sched_bp);
498 j[
"params"][
"cyclic"] = d.sched_cyclic;
499 json A = json::array();
500 for (
const Matrix<T>& M : d.sched_D0) A.push_back(mat_to_json(M));
501 json blocks = json::array();
502 for (
const std::vector<std::vector<Matrix<T> > >& per_mark : d.sched_Dbatch) {
503 json per_batch = json::array();
504 for (
const std::vector<Matrix<T> >& batch : per_mark) {
505 json segs = json::array();
506 for (
const Matrix<T>& M : batch) segs.push_back(mat_to_json(M));
507 per_batch.push_back(segs);
509 blocks.push_back(per_batch);
511 j[
"params"][
"D0"] = A;
512 j[
"params"][
"D1kb"] = blocks;
520 case ProcessType::NHPP:
521 case ProcessType::MAPT:
522 case ProcessType::PHT: {
523 j[
"params"][
"breakpoints"] = vec_to_json(d.sched_bp);
524 j[
"params"][
"cyclic"] = d.sched_cyclic;
525 if (d.type == ProcessType::NHPP) {
527 json rates = json::array();
528 for (
const Matrix<T>& D1 : d.sched_D1)
529 rates.push_back(num_traits<T>::to_double(D1(0, 0)));
530 j[
"params"][
"rates"] = rates;
533 json A = json::array(), B = json::array();
534 for (
const Matrix<T>& M : d.sched_D0) A.push_back(mat_to_json(M));
535 for (
const Matrix<T>& M : d.sched_D1) B.push_back(mat_to_json(M));
539 j[
"params"][
"D0"] = A;
540 j[
"params"][
"D1"] = B;
543 case ProcessType::PRIOR:
545 "network_writer: a Prior is a set of alternative MODELS rather than one law; save "
546 "the design point SolverUQ built, not the design");
550 throw UnsupportedError(ErrorKind{
"Export"}, std::string(
"network_writer: distribution family '") +
563std::vector<int> lattice_cutoffs(
const qn::NetworkStruct<T>& sn) {
564 std::vector<int> cut(sn.classes.size(), 10);
565 for (std::size_t r = 0; r < sn.classes.size(); ++r)
567 std::isfinite(sn.classes[r].population))
568 cut[r] =
static_cast<int>(std::lround(sn.classes[r].population));
578void for_each_lattice_point(
const std::vector<int>& cut,
const F& visit,
bool with_empty =
false) {
579 std::size_t total = 1;
580 for (
int c : cut) total *=
static_cast<std::size_t
>(c + 1);
581 const std::size_t K = cut.size();
582 for (std::size_t i = 0; i < total; ++i) {
583 std::vector<int> cnt(K, 0);
585 for (std::size_t d = 0; d < K; ++d) {
586 cnt[d] =
static_cast<int>(li %
static_cast<std::size_t
>(cut[d] + 1));
587 li /=
static_cast<std::size_t
>(cut[d] + 1);
591 for (std::size_t d = 0; d < K; ++d) {
593 key += std::to_string(cnt[d]);
596 if (tot == 0 && !with_empty)
continue;
607void for_each_simplex_point(std::size_t K,
int cmax,
const F& visit) {
608 for (
int tot = 1;
tot <= cmax; ++
tot) {
609 std::vector<int> cnt(K, 0);
613 for (std::size_t d = 0; d < K; ++d) {
615 key += std::to_string(cnt[d]);
621 for (std::size_t d = 0; d + 1 < K; ++d)
627 const int v = cnt[j];
660 const std::function<std::vector<double>(
const std::vector<int>&)>& f_at, std::size_t K,
661 double cmax = std::numeric_limits<double>::infinity(),
int max_cutoff = 256,
664 out.
tau.assign(K, 1);
668 for (std::size_t r = 0; r < K; ++r) {
670 std::size_t total = 1;
671 for (std::size_t j = 0; j < K; ++j)
672 if (j != r) total *=
static_cast<std::size_t
>(out.
tau[j] + 1);
674 for (std::size_t li = 0; li < total && sat; ++li) {
675 std::vector<int> m(K, 0);
676 std::size_t rem = li;
678 for (std::size_t j = 0; j < K; ++j) {
682 m[j] =
static_cast<int>(rem %
static_cast<std::size_t
>(out.
tau[j] + 1));
683 rem /=
static_cast<std::size_t
>(out.
tau[j] + 1);
687 if (
static_cast<double>(
sum) + 1.0 > cmax)
continue;
688 std::vector<int> mp(m);
690 const std::vector<double> a = f_at(mp), b = f_at(m);
692 for (std::size_t k = 0; k < a.size() && k < b.size(); ++k)
693 d = std::max(d, std::fabs(a[k] - b[k]));
694 if (d > tol) sat =
false;
699 if (out.
tau[r] > max_cutoff) {
706 for (std::size_t r = 0; r < K; ++r)
707 if (!(
static_cast<double>(out.
tau[r]) < cmax)) out.
bounded =
false;
720 const std::function<T(
const std::vector<std::size_t>&)>& mu, std::size_t K,
721 double cmax = std::numeric_limits<double>::infinity()) {
723 [&mu, K](
const std::vector<int>& n) {
724 std::vector<std::size_t> order;
725 for (std::size_t r = 0; r < K; ++r)
726 for (
int c = 0; c < n[r]; ++c) order.push_back(r + 1);
728 return std::vector<double>(1, std::isfinite(v) ? v : 0.0);
737std::vector<double> cd_scaling_at(
const lang::CdScaling<T>& fun,
const std::vector<int>& cnt,
740 for (std::size_t r = 0; r < K; ++r) n[r] = num_traits<T>::from_int(
static_cast<long>(cnt[r]));
741 const std::vector<T> v = fun(n);
742 std::vector<double> out(K, 0.0);
743 for (std::size_t r = 0; r < K; ++r) {
747 out[r] = std::isfinite(x) ? x : 0.0;
766void cd_scaling_extent(
const lang::CdScaling<T>& fun, std::size_t K,
const std::vector<double>& bb,
767 double C, json& blk) {
768 const auto put = [&](json& tbl,
const std::vector<int>& cnt,
const std::string& key) {
769 tbl[key] = cd_scaling_at<T>(fun, cnt, K);
772 for (std::size_t r = 0; r < K; ++r) finite = finite && r < bb.size() && std::isfinite(bb[r]);
773 json tbl = json::object();
775 std::vector<int> maxc(K, 0);
777 for (std::size_t r = 0; r < K; ++r) {
778 maxc[r] =
static_cast<int>(std::floor(bb[r]));
781 const double cs = std::isfinite(C) ? std::floor(C) : C;
782 if (
static_cast<double>(tot) <= cs) {
783 for_each_lattice_point(maxc, [&](
const std::vector<int>& cnt,
const std::string& key) {
786 blk[
"cutoffs"] = maxc;
788 for_each_lattice_point(maxc, [&](
const std::vector<int>& cnt,
const std::string& key) {
790 for (
int c : cnt) s += c;
791 if (
static_cast<double>(s) <= cs)
put(tbl, cnt, key);
794 blk[
"scaling"] = tbl;
798 [&fun, K](
const std::vector<int>& n) {
return cd_scaling_at<T>(fun, n, K); }, K);
799 std::vector<int> maxc;
804 for (std::size_t r = 0; r < K; ++r)
805 if (r < bb.size() && std::isfinite(bb[r])) maxc[r] =
static_cast<int>(std::floor(bb[r]));
807 for_each_lattice_point(maxc, [&](
const std::vector<int>& cnt,
const std::string& key) {
810 blk[
"cutoffs"] = maxc;
811 blk[
"scaling"] = tbl;
826 const std::size_t K =
sn.classes.size();
827 const std::vector<int> cut = detail::lattice_cutoffs(
sn);
830 model[
"type"] =
"Network";
831 model[
"name"] =
sn.name;
834 if (!
sn.log_path.empty()) model[
"logPath"] =
sn.log_path;
837 json classes = json::array();
838 for (std::size_t r = 0; r < K; ++r) {
845 const bool sig = r <
sn.issignal.size() &&
sn.issignal[r];
847 cj[
"type"] =
"Signal";
854 if (
sn.signaltarget[r] >= 1 &&
sn.signaltarget[r] <= K)
855 cj[
"targetClass"] =
sn.classes[
sn.signaltarget[r] - 1].name;
859 if (!
sn.signalremdist[r].empty()) {
860 json pv = json::array();
862 json xv = json::array();
868 for (std::size_t b = 0; b < pv.size(); ++b) xv.push_back(
static_cast<double>(b));
870 rd[
"type"] =
"DiscreteSampler";
871 rd[
"params"][
"p"] = pv;
872 rd[
"params"][
"x"] = xv;
873 cj[
"removalDistribution"] = rd;
883 if (std::isfinite(pop) && pop == std::floor(pop) && std::fabs(pop) < 9.0e15)
884 cj[
"population"] =
static_cast<long long>(pop);
886 cj[
"population"] = pop;
889 "' names no reference station, which a closed class must have");
890 cj[
"refNode"] =
sn.nodes[
sn.station_to_node[c.
refstat - 1] - 1].name;
892 if (c.
prio != 0) cj[
"priority"] = c.
prio;
893 if (c.
immfeed) cj[
"immediateFeedback"] =
true;
896 if (c.
spawn >= 1 && c.
spawn <= K) cj[
"spawnClass"] =
sn.classes[c.
spawn - 1].name;
897 if (r <
sn.syncreply.size() &&
sn.syncreply[r] >= 1 &&
sn.syncreply[r] <= K)
898 cj[
"replySignalClass"] =
sn.classes[
sn.syncreply[r] - 1].name;
899 classes.push_back(cj);
901 model[
"classes"] = classes;
904 json nodes = json::array();
905 for (std::size_t i = 0; i <
sn.nodes.size(); ++i) {
907 const std::size_t ind = i + 1, ist = nd.
station;
909 nj[
"name"] = nd.
name;
926 json by_dest = json::array(), by_prob = json::array(), by_dist = json::array();
927 for (std::size_t k = 0; k < fp->
fan_out_link.rows(); ++k)
928 for (std::size_t r = 0; r < fp->
fan_out_link.cols() && r < K; ++r) {
930 if (p == 0.0)
continue;
932 rec[
"dest"] =
sn.nodes[k].name;
933 rec[
"class"] = r + 1;
935 by_dest.push_back(rec);
938 pr[
"dest"] =
sn.nodes[k].name;
941 by_prob.push_back(pr);
946 dr[
"dest"] =
sn.nodes[k].name;
948 json pv = json::array(), xv = json::array();
949 for (std::size_t e = 0; e < d.
params.size(); ++e) {
951 xv.push_back(d.
trace.empty()
952 ?
static_cast<double>(e + 1)
957 by_dist.push_back(dr);
960 if (!by_dest.empty()) nj[
"fanOutByDest"] = by_dest;
961 if (!by_prob.empty()) nj[
"fanOutProb"] = by_prob;
962 if (!by_dist.empty()) nj[
"fanOutDist"] = by_dist;
984 typename std::map<std::size_t, std::pair<std::size_t, std::size_t> >::const_iterator
fj;
985 for (std::size_t f = 0; f <
sn.fj.size(); ++f)
986 if (
sn.fj[f].second == ind)
987 nj[
"forkNode"] =
sn.nodes[
sn.fj[f].first - 1].name;
988 typename std::map<std::size_t, typename SN::JoinDecl>::const_iterator jd =
989 sn.joindecl.find(ind);
990 if (jd !=
sn.joindecl.end()) {
992 nj[
"joinStrategy"] =
"PARTIAL";
993 if (jd->second.quorum > 0) nj[
"joinQuorum"] = jd->second.quorum;
997 typename std::map<std::size_t, Matrix<T> >::const_iterator cs =
sn.csmatrix.find(ind);
998 if (cs !=
sn.csmatrix.end()) {
1000 for (std::size_t r = 0; r < K; ++r) {
1002 for (std::size_t s = 0; s < K; ++s) {
1004 if (v != 0.0) row[
sn.classes[s].name] = v;
1006 if (!row.empty()) csm[
sn.classes[r].name] = row;
1008 nj[
"classSwitchMatrix"] = csm;
1012 typename std::map<std::size_t, qn::CacheParam<T> >::const_iterator cp =
1013 sn.nodeparam.find(ind);
1014 if (cp !=
sn.nodeparam.end()) {
1016 nj[
"numItems"] = c.
nitems;
1017 nj[
"itemLevelCap"] = c.
itemcap;
1018 nj[
"replacementStrategy"] = detail::replacement_to_json(c.
replacestrat);
1024 else nj[
"costCaps"] = c.
costcap;
1026 json pop, hit, miss, itemcls;
1027 for (std::size_t r = 0; r < K && r < c.
pread.size(); ++r) {
1028 if (c.
pread[r].empty())
continue;
1030 pj[
"type"] =
"DiscreteSampler";
1031 pj[
"params"][
"p"] = detail::vec_to_json(c.
pread[r]);
1032 pj[
"params"][
"x"] = detail::discrete_support(c.
pread[r].size());
1033 pop[
sn.classes[r].name] = pj;
1035 for (std::size_t r = 0; r < K && r < c.
hitclass.size(); ++r)
1037 hit[
sn.classes[r].name] =
sn.classes[c.
hitclass[r] - 1].name;
1038 for (std::size_t r = 0; r < K && r < c.
missclass.size(); ++r)
1040 miss[
sn.classes[r].name] =
sn.classes[c.
missclass[r] - 1].name;
1041 for (std::size_t r = 0; r < K && r < c.
classitem.size(); ++r)
1043 itemcls[
sn.classes[r].name] =
static_cast<double>(c.
classitem[r]);
1044 if (!pop.empty()) nj[
"popularity"] = pop;
1045 if (!hit.empty()) nj[
"hitClass"] = hit;
1046 if (!miss.empty()) nj[
"missClass"] = miss;
1047 if (!itemcls.empty()) nj[
"itemClass"] = itemcls;
1049 json ap = json::array();
1051 json row = json::array();
1053 row.push_back(g.rows() == 0 ? json::array() : detail::mat_to_json(g));
1056 nj[
"accessProb"] = ap;
1065 json qs = json::array();
1066 for (std::size_t q : kv.second) qs.push_back(
sn.nodes[q - 1].name);
1067 entry[
"queues"] = qs;
1072 items[std::to_string(it)] =
1074 entry[
"items"] = items;
1075 by[
sn.classes[kv.first].name] = entry;
1078 nj[
"retrievalSystem"] = rs;
1083 typename std::map<std::size_t, std::vector<T> >::const_iterator im =
1084 sn.initmarking.find(ind);
1085 if (im !=
sn.initmarking.end()) nj[
"initialState"] = detail::vec_to_json(im->second);
1086 typename std::map<std::size_t, std::vector<T> >::const_iterator sp =
1087 sn.stateprior.find(ind);
1088 typename std::map<std::size_t, Matrix<T> >::const_iterator ss =
sn.statespace.find(ind);
1089 if (sp !=
sn.stateprior.end() && ss !=
sn.statespace.end()) {
1090 nj[
"stateSpace"] = detail::mat_to_json(ss->second);
1091 nj[
"statePrior"] = detail::vec_to_json(sp->second);
1099 nj[
"scheduling"] = detail::sched_to_json(st.
sched);
1107 if (std::isfinite(st.
cap) && st.
cap > 0) nj[
"buffer"] = st.
cap;
1116 json svc, cc, dr, sp, imf;
1117 for (std::size_t r = 0; r < K; ++r) {
1118 if (ist - 1 <
sn.service.size() && r <
sn.service[ist - 1].size() &&
1119 !
sn.service[ist - 1][r].disabled)
1120 svc[
sn.classes[r].name] = detail::dist_to_json(
sn.service[ist - 1][r]);
1128 dr[
sn.classes[r].name] =
1130 if (r < st.
immfeed.size() && st.
immfeed[r]) imf[
sn.classes[r].name] =
true;
1134 for (std::size_t r = 0; r < K && r < st.
schedparam.size(); ++r)
1136 if (!svc.empty()) nj[
"service"] = svc;
1137 if (!cc.empty()) nj[
"classCap"] = cc;
1138 if (!dr.empty()) nj[
"dropRule"] = dr;
1139 if (!sp.empty()) nj[
"schedParams"] = sp;
1140 if (!imf.empty()) nj[
"immediateFeedback"] = imf;
1144 ld[
"type"] =
"loadDependent";
1145 ld[
"scaling"] = detail::vec_to_json(st.
lldscaling);
1146 nj[
"loadDependence"] = ld;
1151 for (
int kind = 0; kind < 2; ++kind) {
1153 if (!
static_cast<bool>(fun))
continue;
1155 blk[
"type"] = kind == 0 ?
"classDependent" :
"jointDependent";
1156 std::vector<double> bb(K, std::numeric_limits<double>::infinity());
1157 if (ist - 1 <
sn.classcap.size())
1158 for (std::size_t r = 0; r < K && r <
sn.classcap[ist - 1].size(); ++r)
1159 bb[r] =
sn.classcap[ist - 1][r];
1160 const double C = ist - 1 <
sn.cap.size() ?
sn.cap[ist - 1]
1161 : std::numeric_limits<double>::infinity();
1162 detail::cd_scaling_extent<T>(fun, K, bb, C, blk);
1164 nj[kind == 0 ?
"classDependence" :
"jointDependence"] = blk;
1167 typename std::map<std::size_t, typename SN::PasParam>::const_iterator pas =
1168 sn.pasparam.find(ist);
1169 if (pas !=
sn.pasparam.end() &&
static_cast<bool>(pas->second.svc_rate_fun)) {
1177 const auto put = [&](
const std::vector<int>& cnt,
const std::string& key) {
1178 std::vector<std::size_t> micro;
1179 for (std::size_t r = 0; r < K; ++r)
1180 for (
int c = 0; c < cnt[r]; ++c) micro.push_back(r + 1);
1182 tbl[key] = std::isfinite(v) ? v : 0.0;
1187 const double cmax = std::isfinite(st.
cap)
1188 ? std::max(std::floor(st.
cap), 1.0)
1189 : std::numeric_limits<double>::infinity();
1193 detail::for_each_lattice_point(sat.
tau, put);
1194 nj[
"oiServiceRate"] = tbl;
1195 nj[
"oiCutoffs"] = sat.
tau;
1196 }
else if (std::isfinite(st.
cap)) {
1197 detail::for_each_simplex_point(
1198 K, std::max(
static_cast<int>(std::floor(st.
cap)), 1), put);
1199 nj[
"oiServiceRate"] = tbl;
1201 for (std::size_t r = 0; r < K; ++r)
1203 !std::isfinite(
sn.classes[r].population))
1205 "' has an unbounded order-independent service rate (mu does not "
1206 "saturate) and no finite buffer; call setCap(N) on the station.");
1207 detail::for_each_lattice_point(cut, put);
1208 nj[
"oiServiceRate"] = tbl;
1209 nj[
"oiCutoffs"] = cut;
1211 bool any_swap =
false;
1212 for (
const std::vector<bool>& row : pas->second.swap_graph)
1213 for (
bool v : row) any_swap = any_swap || v;
1215 json sg = json::array();
1216 for (
const std::vector<bool>& row : pas->second.swap_graph) {
1217 json rj = json::array();
1218 for (
bool v : row) rj.push_back(v ? 1 : 0);
1221 nj[
"swapGraph"] = sg;
1226 nj[
"pollingType"] = detail::polling_to_json(st.
polling_type[0]);
1233 json so = json::array();
1234 for (std::size_t r = 0; r < K && r < st.
switchover.size(); ++r) {
1237 e[
"from"] =
sn.classes[r].name;
1238 e[
"distribution"] = detail::dist_to_json(st.
switchover[r]);
1242 for (std::size_t c = 0; c < K && c < st.
switchover_pair[r].size(); ++c) {
1245 e[
"from"] =
sn.classes[r].name;
1246 e[
"to"] =
sn.classes[c].name;
1250 if (!so.empty()) nj[
"switchoverTimes"] = so;
1253 typename std::map<std::size_t, qn::SetupDelayOffParam<T> >::const_iterator sd =
1254 sn.setupparam.find(ist);
1255 if (sd !=
sn.setupparam.end()) {
1257 for (std::size_t r = 0; r < K && r < sd->second.setup.size(); ++r) {
1258 if (sd->second.setup[r].disabled)
continue;
1259 su[
sn.classes[r].name] = detail::dist_to_json(sd->second.setup[r]);
1260 doff[
sn.classes[r].name] = detail::dist_to_json(sd->second.delayoff[r]);
1263 nj[
"setupTime"] = su;
1264 nj[
"delayOffTime"] = doff;
1273 typename std::map<std::size_t, qn::BreakdownParam<T> >::const_iterator bd =
1274 sn.breakdownparam.find(ist);
1275 if (bd !=
sn.breakdownparam.end()) {
1277 bj[
"failure"] = detail::dist_to_json(bd->second.failure);
1278 bj[
"repair"] = detail::dist_to_json(bd->second.repair);
1280 for (std::size_t r = 0; r < K && r < bd->second.down_service_rates.size(); ++r) {
1283 if (!(rate > 0.0))
continue;
1284 ds[
sn.classes[r].name] = detail::dist_to_json(
1287 if (!ds.empty()) bj[
"downService"] = ds;
1288 nj[
"breakdown"] = bj;
1292 typename std::map<std::size_t, qn::RetrialParam<T> >::const_iterator rt =
1293 sn.retrialparam.find(ist);
1294 if (rt !=
sn.retrialparam.end()) {
1296 for (std::size_t r = 0; r < K && r < rt->second.retrial_proc.size(); ++r) {
1297 if (rt->second.retrial_proc[r].disabled)
continue;
1299 e[
"delay"] = detail::dist_to_json(rt->second.retrial_proc[r]);
1300 e[
"maxAttempts"] = rt->second.max_attempts[r];
1301 if (r < rt->second.policy.size() && rt->second.policy[r] == 2)
1302 e[
"policy"] =
"constant";
1303 if (r < rt->second.max_orbit.size() && rt->second.max_orbit[r] >= 0)
1304 e[
"maxOrbit"] = rt->second.max_orbit[r];
1305 rj[
sn.classes[r].name] = e;
1307 if (!rj.empty()) nj[
"retrial"] = rj;
1311 json pat, orb, brp, blk;
1312 for (std::size_t r = 0; r < K; ++r) {
1315 e[
"distribution"] = detail::dist_to_json(st.
patience[r]);
1318 e[
"impatienceType"] = detail::impatience_to_json(st.
impatience[r]);
1319 pat[
sn.classes[r].name] = e;
1329 e[
"strategy"] = detail::balking_to_json(st.
balking[r].strategy);
1330 json ths = json::array();
1334 tj[
"minJobs"] = th.min_jobs;
1335 tj[
"maxJobs"] = th.max_jobs;
1339 e[
"thresholds"] = ths;
1340 blk[
sn.classes[r].name] = e;
1343 if (!pat.empty()) nj[
"patience"] = pat;
1344 if (!orb.empty()) nj[
"orbitImpatience"] = orb;
1345 if (!brp.empty()) nj[
"batchRejectProb"] = brp;
1346 if (!blk.
empty()) nj[
"balking"] = blk;
1349 json sts = json::array();
1352 tj[
"name"] = t.
name;
1353 tj[
"count"] = t.
count;
1355 json cn = json::array();
1356 for (std::size_t r = 0; r < K && r < t.
compatible.size(); ++r)
1358 tj[
"compatibleClasses"] = cn;
1361 for (std::size_t r = 0; r < K && r < t.
service.size(); ++r)
1363 sv[
sn.classes[r].name] = detail::dist_to_json(t.
service[r]);
1364 if (!sv.empty()) tj[
"service"] = sv;
1367 nj[
"serverTypes"] = sts;
1369 nj[
"heteroSchedPolicy"] = detail::hetero_to_json(st.
hetero_policy);
1376 if (!par.empty()) nj[
"serverParallelism"] = par;
1380 for (std::size_t r = 0; r < K && r < st.
arrival_batch.size(); ++r)
1383 if (!ab.empty()) nj[
"arrivalBatch"] = ab;
1385 json
mc = json::array();
1387 nj[
"markedClasses"] =
mc;
1392 dd[
sn.classes[r].name] =
"FIFO";
1393 if (!dd.empty()) nj[
"departureDiscipline"] = dd;
1398 typename std::map<std::size_t, qn::TransitionParam<T> >::const_iterator tp =
1399 sn.transparam.find(ind);
1400 if (tp !=
sn.transparam.end()) {
1401 json modes = json::array();
1402 for (std::size_t m = 0; m < tp->second.nmodes; ++m) {
1404 mj[
"name"] = tp->second.modenames[m];
1405 mj[
"timingStrategy"] =
1408 if (!tp->second.firingproc[m].disabled)
1409 mj[
"distribution"] = detail::dist_to_json(tp->second.firingproc[m]);
1410 mj[
"numServers"] = tp->second.nmodeservers[m];
1411 mj[
"firingPriority"] = tp->second.firingprio[m];
1413 const char* kArcKey[3] = {
"enablingConditions",
"inhibitingConditions",
1415 for (
int which = 0; which < 3; ++which) {
1416 const Matrix<T>& row = which == 0 ? tp->second.enabling[m]
1417 : which == 1 ? tp->second.inhibiting[m]
1418 : tp->second.firing[m];
1419 json arcs = json::array();
1420 for (std::size_t q = 0; q < row.rows(); ++q)
1421 for (std::size_t r = 0; r < row.cols(); ++r) {
1426 if (which == 1 ? !std::isfinite(v) : v == 0.0)
continue;
1428 a[
"node"] =
sn.nodes[q].name;
1435 a[
"class"] =
sn.classes[r].name;
1439 if (!arcs.empty()) mj[kArcKey[which]] = arcs;
1447 if (m < tp->second.firingdep.size() && tp->second.firingdep[m]) {
1448 const Matrix<T>& enab = tp->second.enabling[m];
1449 json slots = json::array();
1450 std::vector<std::size_t> slot_pos;
1451 std::vector<long> caps;
1454 const std::size_t KW =
sn.classes.size();
1455 for (std::size_t q = 0; q < enab.
rows(); ++q)
1456 for (std::size_t rq = 0; rq < enab.
cols(); ++rq) {
1459 sm[
"node"] =
sn.nodes[q].name;
1460 sm[
"class"] =
sn.classes[rq].name;
1461 slots.push_back(sm);
1462 slot_pos.push_back(q * KW + rq);
1466 const std::size_t sq =
sn.nodes[q].station;
1467 const double pc = sq == 0 ? std::numeric_limits<double>::infinity()
1468 :
sn.stations[sq - 1].cap;
1469 caps.push_back(std::isfinite(pc) ? std::lround(pc) : 10L);
1471 if (!slots.empty()) {
1473 frm[
"slots"] = slots;
1474 frm[
"cutoffs"] = caps;
1476 std::size_t total = 1;
1477 for (std::size_t s = 0; s < caps.size(); ++s)
1478 total *=
static_cast<std::size_t
>(caps[s]) + 1;
1480 for (std::size_t li = 0; li < total; ++li) {
1481 std::size_t rem = li;
1483 for (std::size_t s = 0; s < caps.size(); ++s) {
1484 const std::size_t shp =
static_cast<std::size_t
>(caps[s]) + 1;
1485 const std::size_t c = rem % shp;
1488 static_cast<long>(c));
1490 key += std::to_string(c);
1494 scaling[key] = std::isfinite(v) ? v : 0.0;
1496 for (std::size_t s = 0; s < slot_pos.size(); ++s)
1498 frm[
"scaling"] = scaling;
1499 mj[
"firingRateDependence"] = frm;
1505 json lst = json::array();
1508 e[
"node"] =
sn.nodes[a.node - 1].name;
1509 e[
"rate"] = a.value;
1512 mj[
"fluidRates"] = lst;
1515 json lst = json::array();
1518 e[
"node"] =
sn.nodes[g.node - 1].name;
1519 e[
"op"] = g.sgn > 0 ?
">=" :
"<=";
1520 e[
"threshold"] = g.thr;
1523 mj[
"fluidGuards"] = lst;
1526 json lst = json::array();
1529 e[
"node"] =
sn.nodes[a.node - 1].name;
1530 e[
"delta"] = a.value;
1533 mj[
"fluidImpulses"] = lst;
1538 json lst = json::array();
1540 const std::size_t sq =
sn.nodes[d.node - 1].station;
1543 if (!std::isfinite(L)) {
1545 for (
const auto& kv :
sn.transparam)
1546 for (std::size_t mm = 0; mm < kv.second.nmodes; ++mm)
1547 for (
const auto& g : kv.second.fluid_guards(mm))
1548 if (g.node == d.node) L = std::max(L, std::fabs(g.thr));
1549 L = std::max(10.0, 10.0 * L);
1551 const std::size_t npts = 201;
1552 json levels = json::array(), values = json::array();
1553 for (std::size_t i = 0; i < npts; ++i) {
1554 const double lv = L *
static_cast<double>(i) /
static_cast<double>(npts - 1);
1555 levels.push_back(lv);
1556 values.push_back(d.fun(lv));
1559 e[
"node"] =
sn.nodes[d.node - 1].name;
1560 e[
"levels"] = levels;
1561 e[
"values"] = values;
1564 mj[
"fluidRateDependence"] = lst;
1571 const std::vector<double> pop =
sn.njobs();
1572 const std::size_t KF =
sn.classes.size();
1573 json lst = json::array();
1575 json slots = json::array(), readn = json::array();
1576 std::vector<std::size_t> sflat;
1577 std::vector<long> caps;
1578 for (std::size_t q : fd.read) {
1579 readn.push_back(
sn.nodes[q - 1].name);
1580 const std::size_t sq =
sn.nodes[q - 1].station;
1581 const double pc = sq == 0 ? std::numeric_limits<double>::infinity()
1582 :
sn.stations[sq - 1].cap;
1583 for (std::size_t r = 0; r < KF; ++r) {
1585 sm[
"node"] =
sn.nodes[q - 1].name;
1586 sm[
"class"] =
sn.classes[r].name;
1587 slots.push_back(sm);
1588 sflat.push_back((q - 1) * KF + r);
1589 const double cc = r < pop.size() && std::isfinite(pop[r]) ? pop[r] : 10.0;
1590 caps.push_back(std::lround(std::min(pc, cc)));
1593 std::size_t total = 1;
1594 for (
long c : caps) total *=
static_cast<std::size_t
>(c) + 1;
1595 std::vector<double> mk(
sn.nodes.size() * KF, 0.0);
1596 json scaling = json::object();
1597 for (std::size_t li = 0; li < total; ++li) {
1598 std::size_t rem = li;
1600 for (std::size_t s = 0; s < caps.size(); ++s) {
1601 const std::size_t shp =
static_cast<std::size_t
>(caps[s]) + 1;
1602 const std::size_t c = rem % shp;
1604 mk[sflat[s]] =
static_cast<double>(c);
1606 key += std::to_string(c);
1608 const double v = fd.fun(mk);
1609 scaling[key] = std::isfinite(v) ? v : 0.0;
1612 e[
"node"] =
sn.nodes[fd.node - 1].name;
1615 e[
"cutoffs"] = caps;
1616 e[
"scaling"] = scaling;
1619 mj[
"fluidFlowDependence"] = lst;
1621 modes.push_back(mj);
1623 nj[
"modes"] = modes;
1626 nodes.push_back(nj);
1628 model[
"nodes"] = nodes;
1632 for (
const auto& kv :
sn.P) {
1633 const std::size_t r = kv.first.first, s = kv.first.second;
1635 for (std::size_t a = 0; a < kv.second.rows(); ++a) {
1637 for (std::size_t b = 0; b < kv.second.cols(); ++b) {
1639 if (p != 0.0) row[
sn.nodes[b].name] = p;
1641 if (!row.empty()) from_to[
sn.nodes[a].name] = row;
1643 if (!from_to.empty())
1644 matrix[
sn.classes[r - 1].name +
"," +
sn.classes[s - 1].name] = from_to;
1647 routing[
"type"] =
"matrix";
1648 routing[
"matrix"] = matrix;
1649 model[
"routing"] = routing;
1651 json strategies, weights, params;
1652 for (std::size_t i = 0; i <
sn.nodes.size(); ++i) {
1654 json per_class, per_class_w, per_class_p;
1655 for (std::size_t r = 0; r < K && r < nd.
routing.size(); ++r) {
1658 per_class[
sn.classes[r].name] = detail::routing_to_json(nd.
routing[r]);
1661 for (
const auto& kv : nd.
routing_weights[r]) dw[
sn.nodes[kv.first - 1].name] = kv.second;
1662 per_class_w[
sn.classes[r].name] = dw;
1667 per_class_p[
sn.classes[r].name] = pj;
1670 if (!per_class.empty()) strategies[nd.
name] = per_class;
1671 if (!per_class_w.empty()) weights[nd.
name] = per_class_w;
1672 if (!per_class_p.empty()) params[nd.
name] = per_class_p;
1674 if (!strategies.empty()) model[
"routingStrategies"] = strategies;
1675 if (!weights.empty()) model[
"routingWeights"] = weights;
1676 if (!params.empty()) model[
"routingParams"] = params;
1683 if (!
sn.sdr_nodes.empty()) {
1685 std::size_t cls = 0;
1686 for (std::size_t r = 0; r < K && cls == 0; ++r)
1687 if (
sn.nodes[sd.
entry].routing.size() > r &&
1691 throw InputError(
"network_writer: the model declares state-dependent routing at node '" +
1692 sn.nodes[sd.
entry].name +
"' but no class routes SDR there");
1694 sdr[
"entry"] =
sn.nodes[sd.
entry].name;
1696 sdr[
"class"] =
sn.classes[cls - 1].name;
1697 json branches = json::array();
1698 for (std::size_t b = 0; b < sd.
branch.size(); ++b) {
1699 json bn = json::array();
1700 for (std::size_t q = 0; q < sd.
branch[b].size(); ++q)
1701 bn.push_back(
sn.nodes[sd.
branch[b][q]].name);
1702 branches.push_back(bn);
1704 sdr[
"branches"] = branches;
1705 json level = json::array(), Cs = json::array(), dm = json::array();
1706 for (std::size_t b = 0; b < sd.
level.size(); ++b)
1707 level.push_back(
static_cast<double>(sd.
level[b]));
1708 for (std::size_t t = 0; t < sd.
C.size(); ++t) Cs.push_back(sd.
C[t]);
1709 for (std::size_t t = 0; t < sd.
d.
rows(); ++t) {
1710 json row = json::array();
1711 for (std::size_t b = 0; b < sd.
d.
cols(); ++b) row.push_back(sd.
d(t, b));
1714 sdr[
"level"] = level;
1717 model[
"stateDepRouting"] = sdr;
1721 if (!
sn.regions.empty()) {
1722 json fcr = json::array();
1723 for (std::size_t g = 0; g <
sn.regions.size(); ++g) {
1724 const typename SN::Region& rg =
sn.regions[g];
1726 rj[
"name"] = rg.name.empty() ?
"Region" + std::to_string(g + 1) : rg.name;
1727 json stations = json::array();
1728 double global = -1.0, globalmem = -1.0;
1729 json class_cap, class_size, class_weight;
1730 for (std::size_t m = 0; m < rg.members.size(); ++m) {
1731 if (!rg.members[m])
continue;
1733 sj[
"node"] =
sn.nodes[
sn.station_to_node[m] - 1].name;
1734 stations.push_back(sj);
1735 global = rg.cap[m][K];
1741 if (m < rg.maxmem.size()) globalmem = rg.maxmem[m];
1742 for (std::size_t r = 0; r < K; ++r)
1743 if (rg.cap[m][r] != -1.0) class_cap[
sn.classes[r].name] = rg.cap[m][r];
1745 rj[
"stations"] = stations;
1746 if (global != -1.0) rj[
"globalMaxJobs"] = global;
1747 if (globalmem != -1.0) rj[
"globalMaxMemory"] = globalmem;
1748 if (!class_cap.empty()) rj[
"classMaxJobs"] = class_cap;
1750 for (std::size_t r = 0; r < K && r < rg.rule.size(); ++r)
1751 rule[
sn.classes[r].name] = detail::drop_to_json(rg.rule[r]);
1752 rj[
"dropRule"] = rule;
1753 for (std::size_t r = 0; r < K; ++r) {
1762 if (!class_size.empty() || !class_weight.empty())
1763 for (json& sj : rj[
"stations"]) {
1764 if (!class_size.empty()) sj[
"classSize"] = class_size;
1765 if (!class_weight.empty()) sj[
"classWeight"] = class_weight;
1767 if (rg.lincon_A.rows() > 0) {
1768 rj[
"constraintA"] = detail::mat_to_json(rg.lincon_A);
1769 rj[
"constraintB"] = detail::vec_to_json(rg.lincon_b);
1773 model[
"finiteCapacityRegions"] = fcr;
1784 if (
static_cast<bool>(
sn.gdscaling)) {
1785 const std::size_t M =
sn.nstations, K =
sn.nclasses;
1786 const std::vector<double> njobs =
sn.njobs();
1787 const int wcut =
sn.gdscalingcutoff;
1788 std::vector<std::size_t> slot_st, slot_cl;
1789 std::vector<int> cuts;
1790 for (std::size_t i = 0; i < M; ++i) {
1792 for (std::size_t r = 0; r < K; ++r) {
1793 const double cap =
sn.classcap[i][r];
1794 if (!(cap > 0))
continue;
1795 int c = std::isfinite(njobs[r]) ?
static_cast<int>(std::lround(njobs[r])) : wcut;
1796 if (std::isfinite(cap)) c = std::min(c,
static_cast<int>(std::lround(cap)));
1797 slot_st.push_back(i);
1798 slot_cl.push_back(r);
1799 cuts.push_back(c > 0 ? c : 0);
1802 const std::size_t P = cuts.size();
1803 std::size_t total = 1;
1804 for (std::size_t d = 0; d < P; ++d) {
1805 total *=
static_cast<std::size_t
>(cuts[d] + 1);
1806 if (total > 200000u)
1808 "the global dependence lattice exceeds the wire limit of 200000 points; lower "
1809 "the wireCutoff argument of set_global_dependence, or solve the model "
1813 json blk, slots, tbl;
1814 blk[
"type"] =
"globalDependent";
1815 std::vector<std::string> station_names(M), class_names(K);
1816 for (std::size_t i = 0; i < M; ++i)
1817 station_names[i] =
sn.nodes[
sn.station_to_node[i] - 1].name;
1818 for (std::size_t r = 0; r < K; ++r) class_names[r] =
sn.classes[r].name;
1819 blk[
"stations"] = station_names;
1820 blk[
"classes"] = class_names;
1821 slots = json::array();
1822 for (std::size_t d = 0; d < P; ++d) {
1824 sm[
"station"] = station_names[slot_st[d]];
1825 sm[
"class"] = class_names[slot_cl[d]];
1826 slots.push_back(sm);
1828 blk[
"slots"] = slots;
1829 blk[
"cutoffs"] = cuts;
1830 blk[
"cutoff"] = wcut;
1832 for (std::size_t li = 0; li < total; ++li) {
1833 std::size_t rem = li;
1834 std::vector<int> cnt(P, 0);
1835 for (std::size_t d = 0; d < P; ++d) {
1836 cnt[d] =
static_cast<int>(rem %
static_cast<std::size_t
>(cuts[d] + 1));
1837 rem /=
static_cast<std::size_t
>(cuts[d] + 1);
1840 for (std::size_t d = 0; d < P; ++d)
1842 const std::vector<T> v =
sn.gdscaling(n);
1843 std::vector<double> flat(M * K, 1.0);
1844 for (std::size_t i = 0; i < M; ++i)
1845 for (std::size_t r = 0; r < K; ++r) {
1850 flat[i * K + r] = std::isfinite(x) ? x : 0.0;
1853 if (P == 0) key =
"0";
1855 for (std::size_t d = 0; d < P; ++d) {
1857 key += std::to_string(cnt[d]);
1861 blk[
"scaling"] = tbl;
1862 std::vector<double> pk(M * K, 1.0);
1863 for (std::size_t j = 0; j < pk.size() && j <
sn.gdscalingpeak.size(); ++j)
1866 model[
"globalDependence"] = blk;
1881 std::map<std::string, json> by_name;
1882 for (
const typename SN::Reward& rw :
sn.reward) {
1883 if (rw.kind.empty() || rw.node == 0)
continue;
1885 rj[
"name"] = rw.name;
1886 rj[
"type"] = rw.kind;
1887 rj[
"node"] =
sn.nodes[rw.node - 1].name;
1888 if (rw.cls != 0) rj[
"class"] =
sn.classes[rw.cls - 1].name;
1889 by_name[rw.name] = rj;
1891 json rewards = json::array();
1892 for (
const std::pair<const std::string, json>& kv : by_name) rewards.push_back(kv.second);
1893 if (!rewards.empty()) model[
"rewards"] = rewards;
1902 root[
"format"] =
"line-model";
1903 root[
"version"] =
"1.0";
1905 detail::wire_nonfinite(root);
1922 const std::size_t K =
sn.classes.size();
1924 it =
sn.pasparam.begin();
1925 it !=
sn.pasparam.end(); ++it) {
1926 if (!
static_cast<bool>(it->second.svc_rate_fun))
continue;
1927 const std::size_t ist = it->first;
1928 if (ist < 1 || ist >
sn.stations.size())
continue;
1929 const double cap =
sn.stations[ist - 1].cap;
1930 if (std::isfinite(cap))
continue;
1931 const std::string& name =
sn.stations[ist - 1].name;
1934 std::string tau =
"[";
1935 for (std::size_t r = 0; r < s.
tau.size(); ++r)
1936 tau += (r ?
", " :
"") + std::to_string(s.
tau[r]);
1939 "' has no finite buffer: LDES state-space generation requires one. "
1940 "Its order-independent service rate saturates at class counts " +
1942 ", so call setCap(N) with N comfortably above the mean occupancy.");
1945 "' has an unbounded order-independent service rate (mu does not "
1946 "saturate) and no finite buffer; call setCap(N) on the station.");
1953 std::ofstream out(path.c_str());
1954 if (!out)
throw InputError(
"network_writer: cannot open " + path +
" for writing");
UnsupportedError(const std::string &what)
A network plus its refreshed NetworkStruct.
What refreshProcessRepresentations and refreshLST compute FROM a distribution: the (D0,...
The exception types the port throws.
Enumerations and the minimal distribution descriptor shared by the model layer of the C++ port.
detail::json network_to_json(const qn::NetworkStruct< T > &sn)
qn::NetworkStruct -> the model.json model object.
void require_finite_pas_buffers(const qn::NetworkStruct< T > &sn)
Refuse an OI/PAS station with no finite buffer before it reaches LDES.
PasSaturation saturation_cutoffs(const std::function< std::vector< double >(const std::vector< int > &)> &f_at, std::size_t K, double cmax=std::numeric_limits< double >::infinity(), int max_cutoff=256, double tol=1e-9)
Per-class saturation cutoffs of a count-indexed function f(n), the search behind the OI/PAS and class...
detail::json network_json_envelope(const qn::NetworkStruct< T > &sn)
The complete model.json envelope: {format, version, model}.
void write_network_json(const qn::NetworkStruct< T > &sn, const std::string &path)
Write a model.json file, indented as the reference writers indent it.
PasSaturation pas_saturation_cutoffs(const std::function< T(const std::vector< std::size_t > &)> &mu, std::size_t K, double cmax=std::numeric_limits< double >::infinity())
The saturation cutoffs of an order-independent rate mu(c), searched over the states a buffer of cmax ...
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
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
@ CATASTROPHE
removes EVERY job at the station
RoutingStrategy
Routing strategies, with the values of MATLAB RoutingStrategy.
@ SDR
Krzesinski (1987) product-form state-dependent routing.
RemovalPolicy
Which job a negative signal removes, with the values of MATLAB RemovalPolicy.
@ FCFS
the oldest 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.
@ KLIMITED
serve at most K per visit (K in pollingPar)
@ EXHAUSTIVE
serve until the queue empties
@ GATED
serve exactly the jobs present at the polling instant
@ DECREMENTING
serve until the queue is one shorter than at arrival
ProcessType
Distribution kinds, with the values of MATLAB ProcessType.
const char * sched_to_text(SchedStrategy s)
const char * process_to_text(ProcessType p)
The MATLAB ProcessType name, as sn.procid prints it.
std::function< std::vector< T >(const std::vector< T > &)> CdScaling
A class-dependent scaling map, sn.cdscaling.
NodeType
Node kinds, with the values of MATLAB NodeType.
ReplacementStrategy
Cache replacement policies, with the values of MATLAB ReplacementStrategy.
@ HLRU
h-LRU / LRU(m): h lists, promote i -> i+1 on a hit
@ CLIMB
move up one position on a hit (transposition rule)
@ QLRU
q-LRU: LRU with probabilistic admission on a miss
@ FIFO
first in, first out
ImpatienceType
Impatience kinds, with the values of MATLAB ImpatienceType.
double tot(const std::vector< double > &pops)
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.
Reader for the LINE model.json interchange (a Network model) into a qn::Network<T> built through the ...
A queueing network and its refreshed NetworkStruct.
Number-type abstraction for the templated API port.
The per-class saturation cutoffs of a count-indexed rate table.
bool bounded
false when some tau(r) passed the search limit or reached cmax
std::vector< int > tau
tau(r): beyond it the function no longer changes with class-r jobs
static Distrib exp_rate(const T &r)
std::vector< T > params
Constructor arguments, in MATLAB getParam order.
std::vector< T > trace
Replayer / Trace samples; empty for every other type.
Topology and coefficients of a state-dependent routing subnetwork.
std::vector< std::size_t > level
level[b] is the unique t with B_b in V_t - V_{t+1}; level[0] is unused.
Matrix< double > d
Coefficients d_tb of eq.
std::vector< double > C
Coefficients C_t of eq.
std::size_t departure
Departure centre d of Q(V,V); may equal entry.
std::vector< std::vector< std::size_t > > branch
branch[b] holds the centres of branch b, b >= 1; branch[0] is unused.
std::size_t entry
Entry centre e of Q(V,V).
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< 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
Variable forking levels, the twin of MATLAB sn.nodeparam{f}.fanOutLink / .fanOutProb / ....
std::vector< std::vector< lang::Distrib< T > > > fan_out_dist
One job class of the network.
std::size_t refstat
1-based reference station
double deadline
sn.classdeadline(r): the soft deadline EDD and EDF order by, and the tardiness JMT reports.
double population
infinite for an open class
bool immfeed
Class-level immediate feedback, ORed with the station's own setting into sn.immfeed.
std::size_t spawn
sn.classspawn(r): the 1-based class injected at the SAME station on every completion of this class,...
bool self_looping
A SelfLoopingClass: a closed class that perpetually cycles at its reference station.
bool is_ref_class
marks the chain's reference class
The pass-and-swap / order-independent parameters of a PAS station, keyed by station index (Dorsman an...
A Logger node's trace configuration, MATLAB's Logger properties and sn.nodeparam{ind}...
std::string file_name
base name, no directory
std::string file_path
directory, MATLAB's model.getLogPath
std::vector< int > routing_param
The scalar parameter of a parameterized dispatcher, per class: the d of a power-of-d (SQ) choice.
std::vector< std::map< std::size_t, double > > routing_weights
The per-destination weights of a WRROBIN dispatcher, per class: a map from 1-based destination NODE i...
bool queue_object
Declared as a Queue although its INF discipline makes nodetype Delay (Network::add_queue).
std::vector< RoutingStrategy > routing
sn.routing, per class.
double tasks_per_link
Fork.output.tasksPerLink == MATLAB sn.nodeparam{f}.fanOut: how many tasks a fork emits per outgoing l...
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 station of the network.
std::vector< Distrib< T > > orbit_impatience
Queue.setOrbitImpatience(class, dist): abandonment from the RETRIAL ORBIT, which is a different popul...
double cap
Station capacity in Kendall's K, as setCapacity sets it.
std::vector< T > jdscalingpeak
sn.jdscalingpeak for this station: the declared peak joint-dependent scaling per class.
std::vector< BalkingParam > balking
std::vector< T > batch_reject
Queue.setBatchRejectProbability: per-class rejection of a whole batch.
std::vector< Distrib< T > > patience
Queue.setPatience(class, dist): the abandonment timer of a WAITING job, with impatience[r] naming whi...
std::vector< std::size_t > server_parallelism
Queue.setServerParallelism(class, n): the servers a job seizes for the whole of its service,...
std::vector< int > droprule
Per-class blocking rule as an INT, with 0 meaning "not set".
std::vector< std::vector< Distrib< T > > > switchover_pair
Queue.setSwitchover(fromClass, toClass, distrib): the walk the server takes when it turns from servin...
double nservers
may be infinite (a Delay, or an inf-scheduled task)
std::vector< lang::PollingType > polling_type
Polling parameters for a POLLING station, MATLAB's pollingType, switchoverTime and pollingPar on the ...
CdScaling< T > jdscaling
sn.jdscaling for this station: MATLAB's Station.ljdScaling, the JOINT dependence map eta_i(n),...
std::vector< T > cdscalingpeak
sn.cdscalingpeak for this station: the DECLARED peak rate scaling per class, empty when the station i...
std::vector< std::size_t > marked_classes
Source.markedClasses: the 1-based class of each mark of an MMAP arrival.
std::vector< T > lldscaling
sn.lldscaling for this station: the multiplier at population 1, 2, ... Empty when the station is not ...
std::vector< lang::ImpatienceType > impatience
std::vector< T > schedparam
sn.schedparam, per class: the DPS / GPS weight, or the SEPT / LEPT rank.
lang::HeteroSchedPolicy hetero_policy
CdScaling< T > cdscaling
sn.cdscaling for this station: the class-dependence map, empty when unset.
std::vector< double > classcap
Per-class buffer from setChainCapacity; infinite where unset.
std::vector< lang::DepartureDiscipline > departure_discipline
Place.departureDiscipline, per class.
std::vector< Distrib< T > > arrival_batch
Source.setArrivalBatch(class, dist): the batch-size law released at each arrival epoch.
std::vector< Distrib< T > > switchover
bool fluid
Place.setFluid (MATLAB sn.fluid): the place holds a continuous LEVEL rather than tokens,...
std::vector< bool > immfeed
Node-level immediate feedback, per class; empty when the station sets none.
std::vector< ServerType > server_types
The parameters of a Transition node: MATLAB sn.nodeparam{ind} for an SPN transition,...
const std::vector< FluidArc > & fluid_rates(std::size_t m) const
const std::vector< FluidArc > & fluid_impulses(std::size_t m) const
const std::vector< FluidDep > & fluid_deps(std::size_t m) const
const std::vector< FluidFlowDep > & fluid_flow_deps(std::size_t m) const
const std::vector< FluidGuard > & fluid_guards(std::size_t m) const