528 const std::size_t K =
sn.classes.size();
529 const std::vector<int> cut = detail::lattice_cutoffs(
sn);
532 model[
"type"] =
"Network";
533 model[
"name"] =
sn.name;
536 if (!
sn.log_path.empty()) model[
"logPath"] =
sn.log_path;
539 json classes = json::array();
540 for (std::size_t r = 0; r < K; ++r) {
547 const bool sig = r <
sn.issignal.size() &&
sn.issignal[r];
549 cj[
"type"] =
"Signal";
556 if (
sn.signaltarget[r] >= 1 &&
sn.signaltarget[r] <= K)
557 cj[
"targetClass"] =
sn.classes[
sn.signaltarget[r] - 1].name;
561 if (!
sn.signalremdist[r].empty()) {
562 json pv = json::array();
564 json xv = json::array();
570 for (std::size_t b = 0; b < pv.size(); ++b) xv.push_back(
static_cast<double>(b));
572 rd[
"type"] =
"DiscreteSampler";
573 rd[
"params"][
"p"] = pv;
574 rd[
"params"][
"x"] = xv;
575 cj[
"removalDistribution"] = rd;
586 "' names no reference station, which a closed class must have");
587 cj[
"refNode"] =
sn.nodes[
sn.station_to_node[c.
refstat - 1] - 1].name;
589 if (c.
prio != 0) cj[
"priority"] = c.
prio;
590 if (c.
immfeed) cj[
"immediateFeedback"] =
true;
593 if (c.
spawn >= 1 && c.
spawn <= K) cj[
"spawnClass"] =
sn.classes[c.
spawn - 1].name;
594 if (r <
sn.syncreply.size() &&
sn.syncreply[r] >= 1 &&
sn.syncreply[r] <= K)
595 cj[
"replySignalClass"] =
sn.classes[
sn.syncreply[r] - 1].name;
596 classes.push_back(cj);
598 model[
"classes"] = classes;
601 json nodes = json::array();
602 for (std::size_t i = 0; i <
sn.nodes.size(); ++i) {
604 const std::size_t ind = i + 1, ist = nd.
station;
606 nj[
"name"] = nd.
name;
607 nj[
"type"] = detail::node_type_to_json(nd.
nodetype);
622 json by_dest = json::array(), by_prob = json::array(), by_dist = json::array();
623 for (std::size_t k = 0; k < fp->
fan_out_link.rows(); ++k)
624 for (std::size_t r = 0; r < fp->
fan_out_link.cols() && r < K; ++r) {
626 if (p == 0.0)
continue;
628 rec[
"dest"] =
sn.nodes[k].name;
629 rec[
"class"] = r + 1;
631 by_dest.push_back(rec);
634 pr[
"dest"] =
sn.nodes[k].name;
637 by_prob.push_back(pr);
642 dr[
"dest"] =
sn.nodes[k].name;
644 json pv = json::array(), xv = json::array();
645 for (std::size_t e = 0; e < d.
params.size(); ++e) {
647 xv.push_back(d.
trace.empty()
648 ?
static_cast<double>(e + 1)
653 by_dist.push_back(dr);
656 if (!by_dest.empty()) nj[
"fanOutByDest"] = by_dest;
657 if (!by_prob.empty()) nj[
"fanOutProb"] = by_prob;
658 if (!by_dist.empty()) nj[
"fanOutDist"] = by_dist;
666 typename std::map<std::size_t, std::pair<std::size_t, std::size_t> >::const_iterator
fj;
667 for (std::size_t f = 0; f <
sn.fj.size(); ++f)
668 if (
sn.fj[f].second == ind)
669 nj[
"forkNode"] =
sn.nodes[
sn.fj[f].first - 1].name;
670 typename std::map<std::size_t, typename SN::JoinDecl>::const_iterator jd =
671 sn.joindecl.find(ind);
672 if (jd !=
sn.joindecl.end()) {
674 nj[
"joinStrategy"] =
"PARTIAL";
675 if (jd->second.quorum > 0) nj[
"joinQuorum"] = jd->second.quorum;
679 typename std::map<std::size_t, Matrix<T> >::const_iterator cs =
sn.csmatrix.find(ind);
680 if (cs !=
sn.csmatrix.end()) {
682 for (std::size_t r = 0; r < K; ++r) {
684 for (std::size_t s = 0; s < K; ++s) {
686 if (v != 0.0) row[
sn.classes[s].name] = v;
688 if (!row.empty()) csm[
sn.classes[r].name] = row;
690 nj[
"classSwitchMatrix"] = csm;
694 typename std::map<std::size_t, qn::CacheParam<T> >::const_iterator cp =
695 sn.nodeparam.find(ind);
696 if (cp !=
sn.nodeparam.end()) {
698 nj[
"numItems"] = c.
nitems;
699 nj[
"itemLevelCap"] = c.
itemcap;
700 nj[
"replacementStrategy"] = detail::replacement_to_json(c.
replacestrat);
706 else nj[
"costCaps"] = c.
costcap;
708 json pop, hit, miss, itemcls;
709 for (std::size_t r = 0; r < K && r < c.
pread.size(); ++r) {
710 if (c.
pread[r].empty())
continue;
712 pj[
"type"] =
"DiscreteSampler";
713 pj[
"params"][
"p"] = detail::vec_to_json(c.
pread[r]);
714 pj[
"params"][
"x"] = detail::discrete_support(c.
pread[r].size());
715 pop[
sn.classes[r].name] = pj;
717 for (std::size_t r = 0; r < K && r < c.
hitclass.size(); ++r)
719 hit[
sn.classes[r].name] =
sn.classes[c.
hitclass[r] - 1].name;
720 for (std::size_t r = 0; r < K && r < c.
missclass.size(); ++r)
722 miss[
sn.classes[r].name] =
sn.classes[c.
missclass[r] - 1].name;
723 for (std::size_t r = 0; r < K && r < c.
classitem.size(); ++r)
725 itemcls[
sn.classes[r].name] =
static_cast<double>(c.
classitem[r]);
726 if (!pop.empty()) nj[
"popularity"] = pop;
727 if (!hit.empty()) nj[
"hitClass"] = hit;
728 if (!miss.empty()) nj[
"missClass"] = miss;
729 if (!itemcls.empty()) nj[
"itemClass"] = itemcls;
731 json ap = json::array();
733 json row = json::array();
735 row.push_back(g.rows() == 0 ? json::array() : detail::mat_to_json(g));
738 nj[
"accessProb"] = ap;
747 json qs = json::array();
748 for (std::size_t q : kv.second) qs.push_back(
sn.nodes[q - 1].name);
749 entry[
"queues"] = qs;
754 items[std::to_string(it)] =
756 entry[
"items"] = items;
757 by[
sn.classes[kv.first].name] = entry;
760 nj[
"retrievalSystem"] = rs;
765 typename std::map<std::size_t, std::vector<T> >::const_iterator im =
766 sn.initmarking.find(ind);
767 if (im !=
sn.initmarking.end()) nj[
"initialState"] = detail::vec_to_json(im->second);
768 typename std::map<std::size_t, std::vector<T> >::const_iterator sp =
769 sn.stateprior.find(ind);
770 typename std::map<std::size_t, Matrix<T> >::const_iterator ss =
sn.statespace.find(ind);
771 if (sp !=
sn.stateprior.end() && ss !=
sn.statespace.end()) {
772 nj[
"stateSpace"] = detail::mat_to_json(ss->second);
773 nj[
"statePrior"] = detail::vec_to_json(sp->second);
781 nj[
"scheduling"] = detail::sched_to_json(st.
sched);
785 if (std::isfinite(st.
cap) && st.
cap > 0) nj[
"buffer"] = st.
cap;
787 json svc, cc, dr, sp, imf;
788 for (std::size_t r = 0; r < K; ++r) {
789 if (ist - 1 <
sn.service.size() && r <
sn.service[ist - 1].size() &&
790 !
sn.service[ist - 1][r].disabled)
791 svc[
sn.classes[r].name] = detail::dist_to_json(
sn.service[ist - 1][r]);
799 dr[
sn.classes[r].name] =
805 for (std::size_t r = 0; r < K && r < st.
schedparam.size(); ++r)
807 if (!svc.empty()) nj[
"service"] = svc;
808 if (!cc.empty()) nj[
"classCap"] = cc;
809 if (!dr.empty()) nj[
"dropRule"] = dr;
810 if (!sp.empty()) nj[
"schedParams"] = sp;
811 if (!imf.empty()) nj[
"immediateFeedback"] = imf;
815 ld[
"type"] =
"loadDependent";
816 ld[
"scaling"] = detail::vec_to_json(st.
lldscaling);
817 nj[
"loadDependence"] = ld;
821 for (
int kind = 0; kind < 2; ++kind) {
823 if (!
static_cast<bool>(fun))
continue;
825 blk[
"type"] = kind == 0 ?
"classDependent" :
"jointDependent";
826 blk[
"cutoffs"] = cut;
827 detail::for_each_lattice_point(cut, [&](
const std::vector<int>& cnt,
828 const std::string& key) {
830 for (std::size_t r = 0; r < K; ++r)
832 tbl[key] = detail::vec_to_json(fun(n));
834 blk[
"scaling"] = tbl;
836 nj[kind == 0 ?
"classDependence" :
"jointDependence"] = blk;
839 typename std::map<std::size_t, typename SN::PasParam>::const_iterator pas =
840 sn.pasparam.find(ist);
841 if (pas !=
sn.pasparam.end() &&
static_cast<bool>(pas->second.svc_rate_fun)) {
843 detail::for_each_lattice_point(
844 cut, [&](
const std::vector<int>& cnt,
const std::string& key) {
845 std::vector<std::size_t> micro;
846 for (std::size_t r = 0; r < K; ++r)
847 for (
int c = 0; c < cnt[r]; ++c) micro.push_back(r + 1);
850 nj[
"oiServiceRate"] = tbl;
851 nj[
"oiCutoffs"] = cut;
852 bool any_swap =
false;
853 for (
const std::vector<bool>& row : pas->second.swap_graph)
854 for (
bool v : row) any_swap = any_swap || v;
856 json sg = json::array();
857 for (
const std::vector<bool>& row : pas->second.swap_graph) {
858 json rj = json::array();
859 for (
bool v : row) rj.push_back(v ? 1 : 0);
862 nj[
"swapGraph"] = sg;
867 nj[
"pollingType"] = detail::polling_to_json(st.
polling_type[0]);
871 json so = json::array();
872 for (std::size_t r = 0; r < K && r < st.
switchover.size(); ++r) {
875 e[
"from"] =
sn.classes[r].name;
876 e[
"distribution"] = detail::dist_to_json(st.
switchover[r]);
879 if (!so.empty()) nj[
"switchoverTimes"] = so;
882 typename std::map<std::size_t, qn::SetupDelayOffParam<T> >::const_iterator sd =
883 sn.setupparam.find(ist);
884 if (sd !=
sn.setupparam.end()) {
886 for (std::size_t r = 0; r < K && r < sd->second.setup.size(); ++r) {
887 if (sd->second.setup[r].disabled)
continue;
888 su[
sn.classes[r].name] = detail::dist_to_json(sd->second.setup[r]);
889 doff[
sn.classes[r].name] = detail::dist_to_json(sd->second.delayoff[r]);
892 nj[
"setupTime"] = su;
893 nj[
"delayOffTime"] = doff;
902 typename std::map<std::size_t, qn::BreakdownParam<T> >::const_iterator bd =
903 sn.breakdownparam.find(ist);
904 if (bd !=
sn.breakdownparam.end()) {
906 bj[
"failure"] = detail::dist_to_json(bd->second.failure);
907 bj[
"repair"] = detail::dist_to_json(bd->second.repair);
909 for (std::size_t r = 0; r < K && r < bd->second.down_service_rates.size(); ++r) {
912 if (!(rate > 0.0))
continue;
913 ds[
sn.classes[r].name] = detail::dist_to_json(
916 if (!ds.empty()) bj[
"downService"] = ds;
917 nj[
"breakdown"] = bj;
921 typename std::map<std::size_t, qn::RetrialParam<T> >::const_iterator rt =
922 sn.retrialparam.find(ist);
923 if (rt !=
sn.retrialparam.end()) {
925 for (std::size_t r = 0; r < K && r < rt->second.retrial_proc.size(); ++r) {
926 if (rt->second.retrial_proc[r].disabled)
continue;
928 e[
"delay"] = detail::dist_to_json(rt->second.retrial_proc[r]);
929 e[
"maxAttempts"] = rt->second.max_attempts[r];
930 rj[
sn.classes[r].name] = e;
932 if (!rj.empty()) nj[
"retrial"] = rj;
936 json pat, orb, brp, blk;
937 for (std::size_t r = 0; r < K; ++r) {
940 e[
"distribution"] = detail::dist_to_json(st.
patience[r]);
943 e[
"impatienceType"] = detail::impatience_to_json(st.
impatience[r]);
944 pat[
sn.classes[r].name] = e;
954 e[
"strategy"] = detail::balking_to_json(st.
balking[r].strategy);
955 json ths = json::array();
959 tj[
"minJobs"] = th.min_jobs;
960 tj[
"maxJobs"] = th.max_jobs;
964 e[
"thresholds"] = ths;
965 blk[
sn.classes[r].name] = e;
968 if (!pat.empty()) nj[
"patience"] = pat;
969 if (!orb.empty()) nj[
"orbitImpatience"] = orb;
970 if (!brp.empty()) nj[
"batchRejectProb"] = brp;
971 if (!blk.
empty()) nj[
"balking"] = blk;
974 json sts = json::array();
978 tj[
"count"] = t.
count;
980 json cn = json::array();
981 for (std::size_t r = 0; r < K && r < t.
compatible.size(); ++r)
983 tj[
"compatibleClasses"] = cn;
986 for (std::size_t r = 0; r < K && r < t.
service.size(); ++r)
988 sv[
sn.classes[r].name] = detail::dist_to_json(t.
service[r]);
989 if (!sv.empty()) tj[
"service"] = sv;
992 nj[
"serverTypes"] = sts;
994 nj[
"heteroSchedPolicy"] = detail::hetero_to_json(st.
hetero_policy);
1001 if (!par.empty()) nj[
"serverParallelism"] = par;
1005 for (std::size_t r = 0; r < K && r < st.
arrival_batch.size(); ++r)
1008 if (!ab.empty()) nj[
"arrivalBatch"] = ab;
1010 json
mc = json::array();
1012 nj[
"markedClasses"] =
mc;
1017 dd[
sn.classes[r].name] =
"FIFO";
1018 if (!dd.empty()) nj[
"departureDiscipline"] = dd;
1023 typename std::map<std::size_t, qn::TransitionParam<T> >::const_iterator tp =
1024 sn.transparam.find(ind);
1025 if (tp !=
sn.transparam.end()) {
1026 json modes = json::array();
1027 for (std::size_t m = 0; m < tp->second.nmodes; ++m) {
1029 mj[
"name"] = tp->second.modenames[m];
1030 mj[
"timingStrategy"] =
1033 if (!tp->second.firingproc[m].disabled)
1034 mj[
"distribution"] = detail::dist_to_json(tp->second.firingproc[m]);
1035 mj[
"numServers"] = tp->second.nmodeservers[m];
1036 mj[
"firingPriority"] = tp->second.firingprio[m];
1038 const char* kArcKey[3] = {
"enablingConditions",
"inhibitingConditions",
1040 for (
int which = 0; which < 3; ++which) {
1041 const Matrix<T>& row = which == 0 ? tp->second.enabling[m]
1042 : which == 1 ? tp->second.inhibiting[m]
1043 : tp->second.firing[m];
1044 json arcs = json::array();
1045 for (std::size_t q = 0; q < row.rows(); ++q)
1046 for (std::size_t r = 0; r < row.cols(); ++r) {
1051 if (which == 1 ? !std::isfinite(v) : v == 0.0)
continue;
1053 a[
"node"] =
sn.nodes[q].name;
1060 a[
"class"] =
sn.classes[r].name;
1064 if (!arcs.empty()) mj[kArcKey[which]] = arcs;
1072 if (m < tp->second.firingdep.size() && tp->second.firingdep[m]) {
1073 const Matrix<T>& enab = tp->second.enabling[m];
1074 json slots = json::array();
1075 std::vector<std::size_t> slot_node;
1076 std::vector<long> caps;
1084 for (std::size_t q = 0; q < enab.
rows(); ++q) {
1085 std::size_t rq = enab.
cols();
1086 for (std::size_t r = 0; r < enab.
cols(); ++r)
1088 if (rq == enab.
cols())
continue;
1090 sm[
"node"] =
sn.nodes[q].name;
1091 sm[
"class"] =
sn.classes[rq].name;
1092 slots.push_back(sm);
1093 slot_node.push_back(q);
1097 const std::size_t sq =
sn.nodes[q].station;
1098 const double pc = sq == 0 ? std::numeric_limits<double>::infinity()
1099 :
sn.stations[sq - 1].cap;
1100 caps.push_back(std::isfinite(pc) ? std::lround(pc) : 10L);
1102 if (!slots.empty()) {
1104 frm[
"slots"] = slots;
1105 frm[
"cutoffs"] = caps;
1107 std::size_t total = 1;
1108 for (std::size_t s = 0; s < caps.size(); ++s)
1109 total *=
static_cast<std::size_t
>(caps[s]) + 1;
1111 for (std::size_t li = 0; li < total; ++li) {
1112 std::size_t rem = li;
1114 for (std::size_t s = 0; s < caps.size(); ++s) {
1115 const std::size_t shp =
static_cast<std::size_t
>(caps[s]) + 1;
1116 const std::size_t c = rem % shp;
1119 static_cast<long>(c));
1121 key += std::to_string(c);
1125 scaling[key] = std::isfinite(v) ? v : 0.0;
1127 for (std::size_t s = 0; s < slot_node.size(); ++s)
1129 frm[
"scaling"] = scaling;
1130 mj[
"firingRateDependence"] = frm;
1133 modes.push_back(mj);
1135 nj[
"modes"] = modes;
1138 nodes.push_back(nj);
1140 model[
"nodes"] = nodes;
1144 for (
const auto& kv :
sn.P) {
1145 const std::size_t r = kv.first.first, s = kv.first.second;
1147 for (std::size_t a = 0; a < kv.second.rows(); ++a) {
1149 for (std::size_t b = 0; b < kv.second.cols(); ++b) {
1151 if (p != 0.0) row[
sn.nodes[b].name] = p;
1153 if (!row.empty()) from_to[
sn.nodes[a].name] = row;
1155 if (!from_to.empty())
1156 matrix[
sn.classes[r - 1].name +
"," +
sn.classes[s - 1].name] = from_to;
1159 routing[
"type"] =
"matrix";
1160 routing[
"matrix"] = matrix;
1161 model[
"routing"] = routing;
1163 json strategies, weights, params;
1164 for (std::size_t i = 0; i <
sn.nodes.size(); ++i) {
1166 json per_class, per_class_w, per_class_p;
1167 for (std::size_t r = 0; r < K && r < nd.
routing.size(); ++r) {
1170 per_class[
sn.classes[r].name] = detail::routing_to_json(nd.
routing[r]);
1173 for (
const auto& kv : nd.
routing_weights[r]) dw[
sn.nodes[kv.first - 1].name] = kv.second;
1174 per_class_w[
sn.classes[r].name] = dw;
1179 per_class_p[
sn.classes[r].name] = pj;
1182 if (!per_class.empty()) strategies[nd.
name] = per_class;
1183 if (!per_class_w.empty()) weights[nd.
name] = per_class_w;
1184 if (!per_class_p.empty()) params[nd.
name] = per_class_p;
1186 if (!strategies.empty()) model[
"routingStrategies"] = strategies;
1187 if (!weights.empty()) model[
"routingWeights"] = weights;
1188 if (!params.empty()) model[
"routingParams"] = params;
1195 if (!
sn.sdr_nodes.empty()) {
1197 std::size_t cls = 0;
1198 for (std::size_t r = 0; r < K && cls == 0; ++r)
1199 if (
sn.nodes[sd.
entry].routing.size() > r &&
1203 throw InputError(
"network_writer: the model declares state-dependent routing at node '" +
1204 sn.nodes[sd.
entry].name +
"' but no class routes SDR there");
1206 sdr[
"entry"] =
sn.nodes[sd.
entry].name;
1208 sdr[
"class"] =
sn.classes[cls - 1].name;
1209 json branches = json::array();
1210 for (std::size_t b = 0; b < sd.
branch.size(); ++b) {
1211 json bn = json::array();
1212 for (std::size_t q = 0; q < sd.
branch[b].size(); ++q)
1213 bn.push_back(
sn.nodes[sd.
branch[b][q]].name);
1214 branches.push_back(bn);
1216 sdr[
"branches"] = branches;
1217 json level = json::array(), Cs = json::array(), dm = json::array();
1218 for (std::size_t b = 0; b < sd.
level.size(); ++b)
1219 level.push_back(
static_cast<double>(sd.
level[b]));
1220 for (std::size_t t = 0; t < sd.
C.size(); ++t) Cs.push_back(sd.
C[t]);
1221 for (std::size_t t = 0; t < sd.
d.
rows(); ++t) {
1222 json row = json::array();
1223 for (std::size_t b = 0; b < sd.
d.
cols(); ++b) row.push_back(sd.
d(t, b));
1226 sdr[
"level"] = level;
1229 model[
"stateDepRouting"] = sdr;
1233 if (!
sn.regions.empty()) {
1234 json fcr = json::array();
1235 for (std::size_t g = 0; g <
sn.regions.size(); ++g) {
1236 const typename SN::Region& rg =
sn.regions[g];
1238 rj[
"name"] = rg.name.empty() ?
"Region" + std::to_string(g + 1) : rg.name;
1239 json stations = json::array();
1240 double global = -1.0, globalmem = -1.0;
1241 json class_cap, class_size, class_weight;
1242 for (std::size_t m = 0; m < rg.members.size(); ++m) {
1243 if (!rg.members[m])
continue;
1245 sj[
"node"] =
sn.nodes[
sn.station_to_node[m] - 1].name;
1246 stations.push_back(sj);
1247 global = rg.cap[m][K];
1253 if (m < rg.maxmem.size()) globalmem = rg.maxmem[m];
1254 for (std::size_t r = 0; r < K; ++r)
1255 if (rg.cap[m][r] != -1.0) class_cap[
sn.classes[r].name] = rg.cap[m][r];
1257 rj[
"stations"] = stations;
1258 if (global != -1.0) rj[
"globalMaxJobs"] = global;
1259 if (globalmem != -1.0) rj[
"globalMaxMemory"] = globalmem;
1260 if (!class_cap.empty()) rj[
"classMaxJobs"] = class_cap;
1262 for (std::size_t r = 0; r < K && r < rg.rule.size(); ++r)
1263 rule[
sn.classes[r].name] = detail::drop_to_json(rg.rule[r]);
1264 rj[
"dropRule"] = rule;
1265 for (std::size_t r = 0; r < K; ++r) {
1274 if (!class_size.empty() || !class_weight.empty())
1275 for (json& sj : rj[
"stations"]) {
1276 if (!class_size.empty()) sj[
"classSize"] = class_size;
1277 if (!class_weight.empty()) sj[
"classWeight"] = class_weight;
1279 if (rg.lincon_A.rows() > 0) {
1280 rj[
"constraintA"] = detail::mat_to_json(rg.lincon_A);
1281 rj[
"constraintB"] = detail::vec_to_json(rg.lincon_b);
1285 model[
"finiteCapacityRegions"] = fcr;
1296 if (
static_cast<bool>(
sn.gdscaling)) {
1297 const std::size_t M =
sn.nstations, K =
sn.nclasses;
1298 const std::vector<double> njobs =
sn.njobs();
1299 const int wcut =
sn.gdscalingcutoff;
1300 std::vector<std::size_t> slot_st, slot_cl;
1301 std::vector<int> cuts;
1302 for (std::size_t i = 0; i < M; ++i) {
1304 for (std::size_t r = 0; r < K; ++r) {
1305 const double cap =
sn.classcap[i][r];
1306 if (!(cap > 0))
continue;
1307 int c = std::isfinite(njobs[r]) ?
static_cast<int>(std::lround(njobs[r])) : wcut;
1308 if (std::isfinite(cap)) c = std::min(c,
static_cast<int>(std::lround(cap)));
1309 slot_st.push_back(i);
1310 slot_cl.push_back(r);
1311 cuts.push_back(c > 0 ? c : 0);
1314 const std::size_t P = cuts.size();
1315 std::size_t total = 1;
1316 for (std::size_t d = 0; d < P; ++d) {
1317 total *=
static_cast<std::size_t
>(cuts[d] + 1);
1318 if (total > 200000u)
1320 "the global dependence lattice exceeds the wire limit of 200000 points; lower "
1321 "the wireCutoff argument of set_global_dependence, or solve the model "
1325 json blk, slots, tbl;
1326 blk[
"type"] =
"globalDependent";
1327 std::vector<std::string> station_names(M), class_names(K);
1328 for (std::size_t i = 0; i < M; ++i)
1329 station_names[i] =
sn.nodes[
sn.station_to_node[i] - 1].name;
1330 for (std::size_t r = 0; r < K; ++r) class_names[r] =
sn.classes[r].name;
1331 blk[
"stations"] = station_names;
1332 blk[
"classes"] = class_names;
1333 slots = json::array();
1334 for (std::size_t d = 0; d < P; ++d) {
1336 sm[
"station"] = station_names[slot_st[d]];
1337 sm[
"class"] = class_names[slot_cl[d]];
1338 slots.push_back(sm);
1340 blk[
"slots"] = slots;
1341 blk[
"cutoffs"] = cuts;
1342 blk[
"cutoff"] = wcut;
1344 for (std::size_t li = 0; li < total; ++li) {
1345 std::size_t rem = li;
1346 std::vector<int> cnt(P, 0);
1347 for (std::size_t d = 0; d < P; ++d) {
1348 cnt[d] =
static_cast<int>(rem %
static_cast<std::size_t
>(cuts[d] + 1));
1349 rem /=
static_cast<std::size_t
>(cuts[d] + 1);
1352 for (std::size_t d = 0; d < P; ++d)
1354 const std::vector<T> v =
sn.gdscaling(n);
1355 std::vector<double> flat(M * K, 1.0);
1356 for (std::size_t i = 0; i < M; ++i)
1357 for (std::size_t r = 0; r < K; ++r) {
1362 flat[i * K + r] = std::isfinite(x) ? x : 0.0;
1365 if (P == 0) key =
"0";
1367 for (std::size_t d = 0; d < P; ++d) {
1369 key += std::to_string(cnt[d]);
1373 blk[
"scaling"] = tbl;
1374 std::vector<double> pk(M * K, 1.0);
1375 for (std::size_t j = 0; j < pk.size() && j <
sn.gdscalingpeak.size(); ++j)
1378 model[
"globalDependence"] = blk;
1393 std::map<std::string, json> by_name;
1394 for (
const typename SN::Reward& rw :
sn.reward) {
1395 if (rw.kind.empty() || rw.node == 0)
continue;
1397 rj[
"name"] = rw.name;
1398 rj[
"type"] = rw.kind;
1399 rj[
"node"] =
sn.nodes[rw.node - 1].name;
1400 if (rw.cls != 0) rj[
"class"] =
sn.classes[rw.cls - 1].name;
1401 by_name[rw.name] = rj;
1403 json rewards = json::array();
1404 for (
const std::pair<const std::string, json>& kv : by_name) rewards.push_back(kv.second);
1405 if (!rewards.empty()) model[
"rewards"] = rewards;