5#ifndef LINE_LANG_QN_NETWORK_BUILDER_H
6#define LINE_LANG_QN_NETWORK_BUILDER_H
62 void set(std::size_t r, std::size_t s, std::size_t i, std::size_t j,
const T& p) {
65 void set(std::size_t i, std::size_t j,
const T& p) {
set(1, 1, i, j, p); }
75 for (
const auto& kv : block.entries)
76 set(cls, cls, kv.first.i, kv.first.j, kv.second);
79 T
get(std::size_t r, std::size_t s, std::size_t i, std::size_t j)
const {
86 Key(std::size_t r_, std::size_t s_, std::size_t i_, std::size_t j_)
87 :
r(r_),
s(s_),
i(i_),
j(j_) {}
89 if (
r != o.
r)
return r < o.
r;
90 if (
s != o.
s)
return s < o.
s;
91 if (
i != o.
i)
return i < o.
i;
110 explicit Network(
const std::string& nm) { sn_.name = nm; }
132 st.
nodetype = (sched == SchedStrategy::INF) ? NodeType::Delay : NodeType::Queue;
134 st.
nservers = (sched == SchedStrategy::INF)
135 ? std::numeric_limits<double>::infinity()
137 const std::size_t ist = sn_.add_station(st);
138 sn_.nodes[sn_.station_to_node[ist - 1] - 1].queue_object =
true;
139 init_node(sn_.station_to_node[ist - 1]);
140 return sn_.station_to_node[ist - 1];
148 st.
sched = SchedStrategy::INF;
149 st.
nservers = std::numeric_limits<double>::infinity();
150 const std::size_t ist = sn_.add_station(st);
151 init_node(sn_.station_to_node[ist - 1]);
152 return sn_.station_to_node[ist - 1];
162 if (sn_.sourceIdx != 0)
throw InputError(
"Network: the model already has a Source");
166 st.
sched = SchedStrategy::EXT;
168 const std::size_t ist = sn_.add_station(st);
170 init_node(sn_.station_to_node[ist - 1]);
171 return sn_.station_to_node[ist - 1];
176 if (sn_.sinkNode != 0)
throw InputError(
"Network: the model already has a Sink");
177 const std::size_t nd = sn_.add_node(nm, NodeType::Sink,
false);
185 const std::size_t nd = sn_.add_node(nm, NodeType::Router,
false);
199 std::size_t
add_logger(
const std::string& nm,
const std::string& log_file = std::string()) {
200 const std::size_t nd = sn_.add_node(nm, NodeType::Logger,
false);
204 std::string base = log_file;
205 const std::size_t slash = base.find_last_of(
'/');
206 if (slash != std::string::npos) base = base.substr(slash + 1);
207 sn_.nodes[nd - 1].logger.file_name = base;
221 if (C.
rows() != sn_.classes.size() || C.
cols() != sn_.classes.size())
223 "': the matrix must be (nclasses x nclasses); declare the classes "
225 const std::size_t nd = sn_.add_node(nm, NodeType::ClassSwitch,
false);
226 sn_.csmatrix[nd] = C;
247 const std::size_t nd = sn_.add_node(nm, NodeType::ClassSwitch,
false);
255 if (node == 0 || node > sn_.nodes.size())
256 throw InputError(
"set_class_switch_matrix: node index is out of range");
257 if (sn_.nodes[node - 1].nodetype != NodeType::ClassSwitch)
258 throw InputError(
"set_class_switch_matrix: node '" + sn_.nodes[node - 1].name +
259 "' is not a ClassSwitch");
260 if (C.
rows() != sn_.classes.size() || C.
cols() != sn_.classes.size())
261 throw InputError(
"set_class_switch_matrix: the matrix of '" +
262 sn_.nodes[node - 1].name +
263 "' must be (nclasses x nclasses)");
264 sn_.csmatrix[node] = C;
269 std::size_t
add_fork(
const std::string& nm,
double tasks_per_link = 1.0) {
270 const std::size_t nd = sn_.add_node(nm, NodeType::Fork,
false);
271 sn_.nodes[nd - 1].tasks_per_link = tasks_per_link;
298 double tasks, std::size_t dest_node = 0) {
300 ov.kind = ForkOverride::TASKS;
305 record_fork_override(ov);
311 std::size_t dest_node = 0) {
313 throw InputError(
"set_fork_tasks_per_link_dist: the jobs-per-link "
314 "distribution must be a DiscreteSampler");
316 ov.kind = ForkOverride::DIST;
321 record_fork_override(ov);
326 std::size_t dest_node,
double prob) {
327 if (prob < 0.0 || prob > 1.0)
328 throw InputError(
"set_fork_branch_probability: a branch activation "
329 "probability must lie in [0,1]");
331 ov.kind = ForkOverride::PROB;
336 record_fork_override(ov);
343 std::size_t
add_join(
const std::string& nm, std::size_t fork_node) {
361 st.
sched = SchedStrategy::INF;
362 st.
nservers = std::numeric_limits<double>::infinity();
363 const std::size_t ist = sn_.add_station(st);
364 const std::size_t nd = sn_.station_to_node[ist - 1];
370 void bind_join(std::size_t join_node, std::size_t fork_node) {
371 if (join_node == 0 || join_node > sn_.nodes.size() ||
372 sn_.nodes[join_node - 1].nodetype != NodeType::Join)
373 throw InputError(
"bind_join: the node being bound is not a Join");
374 if (fork_node == 0 || fork_node > sn_.nodes.size() ||
375 sn_.nodes[fork_node - 1].nodetype != NodeType::Fork)
376 throw InputError(
"Join '" + sn_.nodes[join_node - 1].name +
377 "': the node it closes is not a Fork");
378 sn_.fj.emplace_back(fork_node, join_node);
415 st.
nservers = (sched == SchedStrategy::INF)
416 ? std::numeric_limits<double>::infinity()
418 const std::size_t ist = sn_.add_station(st);
419 init_node(sn_.station_to_node[ist - 1]);
420 return sn_.station_to_node[ist - 1];
434 throw InputError(
"add_transition: a transition needs at least one mode");
437 "add_transition: enabling and firing must have one entry per mode");
438 const std::size_t nd = sn_.add_node(nm, NodeType::Transition,
true);
439 sn_.transparam[nd] = par;
458 const std::size_t nd = sn_.add_node(nm, NodeType::Transition,
true);
471 std::size_t
add_mode(std::size_t node,
const std::string& nm) {
480 tp.
firingdep.push_back(std::function<T(
const std::vector<T>&)>());
492 mode_param(node, mode,
"set_mode_distribution").firingproc[mode - 1] = d;
497 mode_param(node, mode,
"set_mode_timing").timing[mode - 1] = ts;
502 mode_param(node, mode,
"set_mode_servers").nmodeservers[mode - 1] = n;
507 mode_param(node, mode,
"set_firing_priority").firingprio[mode - 1] = prio;
512 mode_param(node, mode,
"set_firing_weight").fireweight[mode - 1] = w;
517 const std::function<T(
const std::vector<T>&)>& g) {
518 mode_param(node, mode,
"set_mode_firing_dependence").firingdep[mode - 1] = g;
530 std::size_t place,
const T& tokens) {
531 arc_target(node, mode, cls, place,
true,
"set_enabling_conditions");
532 pending_arcs_[node].enab.push_back(TransitionArc(mode, place, cls, tokens));
541 std::size_t place,
const T& tokens) {
542 arc_target(node, mode, cls, place,
true,
"set_inhibiting_conditions");
543 pending_arcs_[node].inhib.push_back(TransitionArc(mode, place, cls, tokens));
552 std::size_t dest,
const T& tokens) {
553 arc_target(node, mode, cls, dest,
false,
"set_firing_outcome");
554 pending_arcs_[node].fire.push_back(TransitionArc(mode, dest, cls, tokens));
564 int max_attempts = 0) {
565 if (node == 0 || node > sn_.nodes.size())
566 throw InputError(
"set_retrial: node index is out of range");
567 const std::size_t ist = sn_.nodes[node - 1].station;
568 if (ist == 0)
throw InputError(
"set_retrial: node is not a station");
573 const std::size_t K = sn_.classes.size();
574 if (cls == 0 || cls > K)
575 throw InputError(
"set_retrial: class index is out of range");
593 Station<T>& st = station_ref(node, cls,
"set_patience");
602 Station<T>& st = station_ref(node, cls,
"set_orbit_impatience");
609 Station<T>& st = station_ref(node, cls,
"set_batch_reject");
621 Station<T>& st = station_ref(node, cls,
"set_balking");
623 st.
balking[cls - 1].strategy = strategy;
624 st.
balking[cls - 1].thresholds = thresholds;
629 const std::size_t ist = station_of(node,
"add_server_type");
630 sn_.stations[ist - 1].server_types.push_back(stype);
633 for (
const auto& pool : sn_.stations[ist - 1].server_types) total += pool.count;
634 sn_.stations[ist - 1].nservers = total;
643 Station<T>& st = station_ref(node, cls,
"set_server_parallelism");
645 throw InputError(
"set_server_parallelism: parallelism must be a positive integer");
649 "set_server_parallelism: parallelism " + std::to_string(n) +
" exceeds the " +
650 std::to_string(
static_cast<long long>(st.
nservers)) +
" servers of station '" +
651 sn_.nodes[node - 1].name +
"', so a job of this class could never enter service");
659 sn_.stations[station_of(node,
"set_hetero_sched_policy") - 1].hetero_policy = policy;
667 Station<T>& st = station_ref(node, cls,
"set_arrival_batch");
674 sn_.stations[station_of(node,
"set_marked_classes") - 1].marked_classes = classes;
680 Station<T>& st = station_ref(node, cls,
"set_departure_discipline");
687 if (node == 0 || node > sn_.nodes.size())
688 throw InputError(
"set_initial_marking: node index is out of range");
689 if (sn_.nodes[node - 1].nodetype != NodeType::Place)
690 throw InputError(
"set_initial_marking: only a Place carries an initial marking");
691 sn_.initmarking[node] = tokens;
700 if (node == 0 || node > sn_.nodes.size())
701 throw InputError(
"set_state_prior: node index is out of range");
702 if (space.
rows() != prior.size())
704 "set_state_prior: the prior has one entry per ROW of the declared state space");
705 sn_.statespace[node] = space;
706 sn_.stateprior[node] = prior;
711 if (node == 0 || node > sn_.nodes.size())
712 throw InputError(
"set_join_strategy: node index is out of range");
713 if (sn_.nodes[node - 1].nodetype != NodeType::Join)
714 throw InputError(
"set_join_strategy: the node is not a Join");
718 sn_.joindecl[node] = jd;
723 const std::map<std::size_t, double>& weights) {
724 if (node == 0 || node > sn_.nodes.size())
725 throw InputError(
"set_routing_weights: node index is out of range");
726 std::vector<std::map<std::size_t, double>>& rw = sn_.nodes[node - 1].routing_weights;
727 if (rw.size() < sn_.classes.size()) rw.resize(sn_.classes.size());
728 if (cls == 0 || cls > rw.size())
729 throw InputError(
"set_routing_weights: class index is out of range");
730 rw[cls - 1] = weights;
735 if (node == 0 || node > sn_.nodes.size())
736 throw InputError(
"set_routing_param: node index is out of range");
737 std::vector<int>& rp = sn_.nodes[node - 1].routing_param;
738 if (rp.size() < sn_.classes.size()) rp.resize(sn_.classes.size(), 0);
739 if (cls == 0 || cls > rp.size())
740 throw InputError(
"set_routing_param: class index is out of range");
752 const std::size_t ist = station_of(node,
"set_setup_delayoff");
753 const std::size_t K = sn_.classes.size();
754 if (cls == 0 || cls > K)
755 throw InputError(
"set_setup_delayoff: class index is out of range");
757 if (sp.
setup.size() < K) {
761 sp.
setup[cls - 1] = setup;
781 const std::size_t ist = station_of(node,
"set_breakdown");
784 "set_breakdown: both a failure and a repair time are required; a server that "
785 "never recovers is an absorbing model, not a breakdown");
788 if (!(fm > 0.0) || !(rm > 0.0))
789 throw InputError(
"set_breakdown: the failure and repair times must have positive means");
790 const std::size_t K = sn_.classes.size();
797 for (std::size_t r = 0; r < K && r < down_service.size(); ++r) {
802 "set_breakdown: station '" + sn_.stations[ist - 1].name +
803 "': the down-server service distribution must be exponential; a phase-type "
804 "degraded service would need its own phase block in the joint chain");
812 const std::size_t nd = sn_.add_node(nm, NodeType::Cache,
true);
813 sn_.nodeparam[nd] = par;
829 const std::vector<std::size_t>& read_classes,
830 const std::vector<std::size_t>& hit_classes) {
831 auto it = sn_.nodeparam.find(cache_node);
832 if (it == sn_.nodeparam.end())
833 throw InputError(
"setItemReadClasses: node is not a Cache");
835 const std::size_t nitems = cp.
nitems;
836 if (read_classes.size() != nitems)
837 throw InputError(
"setItemReadClasses: pass exactly one read class per item");
838 const std::vector<std::size_t> hit =
839 per_item_classes(hit_classes, nitems,
"setItemReadClasses hit");
840 const std::size_t K = sn_.classes.size();
846 for (std::size_t i = 0; i < nitems; ++i) {
849 cp.
pread[read_classes[i] - 1] = onehot;
850 cp.
hitclass[read_classes[i] - 1] = hit[i];
851 cp.
classitem[read_classes[i] - 1] = i + 1;
853 cache_item_classes_[cache_node] = read_classes;
863 std::vector<std::size_t>
set_miss_cache(std::size_t cache_node, std::size_t next_cache,
864 const std::vector<std::size_t>& hit_classes_at_next) {
865 auto it = sn_.nodeparam.find(cache_node);
866 auto itn = sn_.nodeparam.find(next_cache);
867 if (it == sn_.nodeparam.end() || itn == sn_.nodeparam.end())
868 throw InputError(
"setMissCache: both nodes must be Caches");
869 if (it->second.nitems != itn->second.nitems)
870 throw InputError(
"setMissCache: a cache network requires one common item set");
871 auto self_it = cache_item_classes_.find(cache_node);
872 if (self_it == cache_item_classes_.end())
873 throw InputError(
"setMissCache: call setItemReadClasses on the source cache first");
874 const std::vector<std::size_t> self_cls = self_it->second;
875 const std::size_t nitems = it->second.nitems;
876 const std::vector<std::size_t> hit =
877 per_item_classes(hit_classes_at_next, nitems,
"setMissCache hit");
879 std::vector<std::size_t> minted;
880 minted.reserve(nitems);
881 const bool closed = sn_.classes[self_cls[0] - 1].type == JobClassType::CLOSED;
882 const std::size_t refstat = sn_.classes[self_cls[0] - 1].refstat;
883 for (std::size_t i = 0; i < nitems; ++i) {
886 cls =
add_closed_class(sn_.nodes[next_cache - 1].name +
"_item" + std::to_string(i + 1),
887 0.0, sn_.station_to_node[refstat - 1]);
889 cls =
add_open_class(sn_.nodes[next_cache - 1].name +
"_item" + std::to_string(i + 1));
890 minted.push_back(cls);
893 const std::size_t K = sn_.classes.size();
894 for (std::size_t nd : {cache_node, next_cache}) {
904 for (std::size_t i = 0; i < nitems; ++i) {
907 dst.
pread[minted[i] - 1] = onehot;
908 dst.
hitclass[minted[i] - 1] = hit[i];
910 src.
missclass[self_cls[i] - 1] = minted[i];
914 cache_miss_arcs_.push_back(CacheMissArc(minted[i], cache_node, next_cache));
916 cache_item_classes_[next_cache] = minted;
926 auto it = sn_.nodeparam.find(cache_node);
927 if (it == sn_.nodeparam.end())
928 throw InputError(
"setItemMissClass: node is not a Cache");
929 auto self_it = cache_item_classes_.find(cache_node);
930 if (self_it == cache_item_classes_.end())
931 throw InputError(
"setItemMissClass: the cache has no per-item classes");
932 const std::vector<std::size_t>& self_cls = self_it->second;
933 const std::vector<std::size_t> miss =
934 per_item_classes(miss_classes, self_cls.size(),
"setItemMissClass miss");
936 cp.
missclass.resize(sn_.classes.size(), 0);
937 for (std::size_t i = 0; i < self_cls.size(); ++i)
953 std::size_t miss_class,
const std::vector<std::size_t>& queue_nodes) {
954 auto it = sn_.nodeparam.find(cache_node);
955 if (it == sn_.nodeparam.end())
956 throw InputError(
"setRetrievalSystem: node is not a Cache");
958 if (queue_nodes.empty())
959 throw InputError(
"setRetrievalSystem: the retrieval system has no stations");
960 const std::size_t nitems = cp.
nitems;
962 for (
int c : cp.
itemcap) totalcap += c;
967 std::vector<Distrib<T> > svc;
968 svc.reserve(queue_nodes.size());
969 for (std::size_t q : queue_nodes) {
970 const std::size_t st = station_of(q,
"setRetrievalSystem");
971 svc.push_back(sn_.service[st - 1][read_class - 1]);
974 cp.
retrieval_classes.assign(nitems, std::vector<std::size_t>(sn_.classes.size(), 0));
975 const bool closed = sn_.classes[read_class - 1].type == JobClassType::CLOSED;
976 const std::size_t refstat = sn_.classes[read_class - 1].refstat;
977 for (std::size_t i = 0; i < nitems; ++i) {
980 rc =
add_closed_class(sn_.classes[read_class - 1].name +
"_retrievalClass_" +
981 std::to_string(i + 1),
982 0.0, sn_.station_to_node[refstat - 1]);
984 rc =
add_open_class(sn_.classes[read_class - 1].name +
"_retrievalClass_" +
985 std::to_string(i + 1));
986 for (std::size_t s = 0; s < queue_nodes.size(); ++s) {
992 for (std::size_t k = 0; k < nitems; ++k)
999 const std::size_t K = sn_.classes.size();
1006 cp.
pread[rc - 1] = onehot;
1018 if (!(njobs >= 0.0) || std::isinf(njobs))
1019 throw InputError(
"ClosedClass '" + nm +
"': the population must be finite");
1022 cl.
type = JobClassType::CLOSED;
1024 cl.
refstat = station_of(refstat_node,
"ClosedClass '" + nm +
"'");
1026 const std::size_t r = sn_.add_class(cl);
1027 grow_class_vectors();
1036 if (sn_.sourceIdx == 0)
1038 "': the model has no Source to reference; add one first");
1041 cl.
type = JobClassType::OPEN;
1042 cl.
population = std::numeric_limits<double>::infinity();
1045 const std::size_t r = sn_.add_class(cl);
1046 grow_class_vectors();
1059 std::size_t refstat_node,
int prio = 0) {
1061 sn_.classes[r - 1].self_looping =
true;
1067 class_ref(cls,
"set_reference_class").is_ref_class =
true;
1072 class_ref(cls,
"set_class_deadline").deadline = due;
1080 const std::size_t K = sn_.classes.size();
1081 if (spawn_cls == 0 || spawn_cls > K)
1082 throw InputError(
"set_class_spawn: the spawned class index is out of range");
1083 class_ref(cls,
"set_class_spawn").spawn = spawn_cls;
1097 const std::size_t K = sn_.classes.size();
1098 if (cls == 0 || cls > K)
throw InputError(
"set_class_patience: class index out of range");
1099 for (std::size_t ist = 0; ist < sn_.stations.size(); ++ist) {
1100 const std::size_t ind = sn_.station_to_node[ist];
1106 if (!st.
patience[cls - 1].disabled)
continue;
1124 const std::size_t K = sn_.classes.size();
1125 if (call_cls == 0 || call_cls > K || reply_cls == 0 || reply_cls > K)
1126 throw InputError(
"set_reply_signal_class: class index is out of range");
1128 if (sn_.syncreply.size() < K) sn_.syncreply.resize(K, 0);
1129 sn_.syncreply[call_cls - 1] = reply_cls;
1151 sn_.set_service(station_of(node,
"setService"), cls, dd);
1156 const std::size_t ist = station_of(node,
"setArrival");
1157 if (sn_.stations[ist - 1].nodetype != NodeType::Source)
1158 throw InputError(
"setArrival: node '" + sn_.nodes[node - 1].name +
"' is not a Source");
1164 sn_.set_service(ist, cls, dd);
1178 const std::size_t ist = station_of(node,
"setNumberOfServers");
1179 if (sn_.stations[ist - 1].sched == SchedStrategy::INF)
return;
1180 if (!(n >= 1.0))
throw InputError(
"setNumberOfServers: the server count must be >= 1");
1181 sn_.stations[ist - 1].nservers = n;
1184 if (std::isinf(n) && sn_.stations[ist - 1].nodetype == NodeType::Queue)
1185 sn_.stations[ist - 1].nodetype = NodeType::Delay;
1190 sn_.stations[station_of(node,
"setCapacity") - 1].cap = k;
1195 Station<T>& st = sn_.stations[station_of(node,
"setChainCapacity") - 1];
1196 st.
classcap.resize(sn_.classes.size(), std::numeric_limits<double>::infinity());
1209 Station<T>& st = sn_.stations[station_of(node,
"setImmediateFeedback") - 1];
1210 st.
immfeed.resize(sn_.classes.size(),
false);
1216 sn_.classes[cls - 1].immfeed =
true;
1221 Station<T>& st = sn_.stations[station_of(node,
"setDropRule") - 1];
1222 st.
droprule.resize(sn_.classes.size(), 0);
1223 st.
droprule[cls - 1] =
static_cast<int>(rule);
1242 std::size_t node,
const std::function<T(
const std::vector<std::size_t>&)>& muFun,
1244 const std::size_t ist = station_of(node,
"setServiceRateFunction");
1246 if (st.
sched != SchedStrategy::PAS && st.
sched != SchedStrategy::OI)
1248 "setServiceRateFunction is only applicable to PAS (pass-and-swap) and OI "
1249 "(order-independent) queues");
1250 if (!muFun)
throw InputError(
"setServiceRateFunction: the rate function is empty");
1261 for (std::size_t r = 1; r <= sn_.classes.size(); ++r) {
1262 const T rate_r = muFun(std::vector<std::size_t>{r});
1275 Station<T>& st = sn_.stations[station_of(node,
"setPollingType") - 1];
1276 if (st.
sched != SchedStrategy::POLLING)
1277 throw InputError(
"setPollingType is only applicable to a POLLING station");
1279 throw InputError(
"K-limited polling requires a parameter K >= 1");
1286 Station<T>& st = sn_.stations[station_of(node,
"setSwitchover") - 1];
1287 if (st.
sched != SchedStrategy::POLLING)
1288 throw InputError(
"setSwitchover is only applicable to a POLLING station");
1310 Station<T>& st = sn_.stations[station_of(node,
"setSwitchover") - 1];
1311 if (st.
sched == SchedStrategy::POLLING)
1313 "setSwitchover(fromClass, toClass, distrib) is not applicable to a POLLING "
1314 "station, whose switchover is the walk out of one buffer and is declared per "
1316 const std::size_t K = sn_.classes.size();
1317 if (from_cls == 0 || from_cls > K || to_cls == 0 || to_cls > K)
1318 throw InputError(
"setSwitchover: class index is out of range");
1326 Station<T>& st = sn_.stations[station_of(node,
"setSchedParam") - 1];
1337 if (alpha.empty())
throw InputError(
"setLoadDependence: the scaling vector is empty");
1338 sn_.stations[station_of(node,
"setLoadDependence") - 1].lldscaling = alpha;
1359 const std::vector<T>& peak = std::vector<T>()) {
1360 Station<T>& st = sn_.stations[station_of(node,
"setClassDependence") - 1];
1361 const std::size_t K = sn_.classes.size();
1365 else if (peak.size() == 1)
1367 else if (peak.size() == K)
1371 "setClassDependence: peakRatePerClass must be a scalar or a vector of length "
1385 const std::vector<T>& peak) {
1386 Station<T>& st = sn_.stations[station_of(node,
"setJointDependence") - 1];
1387 const std::size_t K = sn_.classes.size();
1390 "setJointDependence: joint dependence requires an explicit peak rate; pass a "
1391 "scalar (identical peak for every class) or a per-class vector");
1393 if (peak.size() == 1)
1395 else if (peak.size() == K)
1399 "setJointDependence: peakRatePerClass must be a scalar or a vector of length "
1433 throw InputError(
"setGlobalDependence: the scaling must be a callable");
1434 const std::size_t M = sn_.stations.size(), K = sn_.classes.size();
1437 "setGlobalDependence: a global dependence requires an explicit peak rate; pass a "
1438 "scalar, one entry per station, or one entry per (station, class)");
1439 for (std::size_t i = 0; i < peak.size(); ++i)
1441 throw InputError(
"setGlobalDependence: peak must be positive");
1444 for (
int probe = 0; probe < 2; ++probe) {
1446 const std::vector<T> v = fun(n);
1447 if (v.size() != 1 && v.size() != M && v.size() != M * K)
1449 "setGlobalDependence: the handle must return a scalar, one entry per station, "
1450 "or one entry per (station, class)");
1451 for (std::size_t j = 0; j < v.size(); ++j)
1454 "setGlobalDependence: the handle must return finite nonnegative scalings");
1456 if (wire_cutoff < 1)
1457 throw InputError(
"setGlobalDependence: wireCutoff must be a positive integer");
1458 sn_.gdscaling = fun;
1459 sn_.gdscalingcutoff = wire_cutoff;
1460 if (peak.size() == 1)
1461 sn_.gdscalingpeak.assign(M * K, peak[0]);
1462 else if (peak.size() == M) {
1464 for (std::size_t i = 0; i < M; ++i)
1465 for (std::size_t r = 0; r < K; ++r) sn_.gdscalingpeak[i * K + r] = peak[i];
1466 }
else if (peak.size() == M * K)
1467 sn_.gdscalingpeak = peak;
1470 "setGlobalDependence: peak must be a scalar, one entry per station, or one entry "
1471 "per (station, class)");
1476 NodeDef& nd = sn_.nodes[node - 1];
1477 nd.
routing.resize(sn_.classes.size(), RoutingStrategy::PROB);
1505 const std::vector<std::vector<std::size_t>>& branches,
1506 const std::vector<std::size_t>& level,
1508 std::size_t cls = 0) {
1509 if (branches.size() < 2 || !branches[0].empty())
1510 throw InputError(
"set_state_dep_routing: branches[0] must be empty, branch index 1 "
1511 "denotes the complement M-V");
1512 const std::size_t B = branches.size();
1513 if (level.size() != B)
1514 throw InputError(
"set_state_dep_routing: level must have one entry per branch index, "
1515 "including the unused index 0");
1517 nodesdr.
entry = entry - 1;
1519 nodesdr.
branch.assign(B, std::vector<std::size_t>());
1522 for (std::size_t b = 1; b < B; ++b) {
1523 if (branches[b].empty())
1524 throw InputError(
"set_state_dep_routing: branch " + std::to_string(b + 1) +
1526 for (std::size_t k = 0; k < branches[b].size(); ++k)
1527 nodesdr.
branch[b].push_back(branches[b][k] - 1);
1528 nodesdr.
entryOf[b] = branches[b].front() - 1;
1531 nodesdr.
level = level;
1538 std::vector<std::size_t> node_to_station(sn_.nodes.size(), 0);
1539 std::vector<bool> is_station(sn_.nodes.size(),
false);
1540 for (std::size_t k = 0; k < sn_.station_to_node.size(); ++k) {
1541 node_to_station[sn_.station_to_node[k] - 1] = k;
1542 is_station[sn_.station_to_node[k] - 1] =
true;
1545 const std::vector<std::size_t>& n2s;
1546 const std::vector<bool>& isst;
1548 std::size_t operator()(std::size_t nd)
const {
1549 if (nd >= isst.size() || !isst[nd])
1550 throw InputError(
"set_state_dep_routing: node '" +
sn.nodes[nd].name +
1551 "' takes part in state-dependent routing but is not a "
1552 "station: the product form is over queue lengths, and a "
1553 "stateless node holds none");
1556 } to_station{node_to_station, is_station, sn_};
1561 for (std::size_t b = 1; b < B; ++b) {
1562 for (std::size_t k = 0; k < nodesdr.
branch[b].size(); ++k)
1569 sn_.sdr_nodes = nodesdr;
1571 const std::size_t K = sn_.classes.size();
1573 for (std::size_t r = 1; r <= K; ++r)
set_routing(entry, r, RoutingStrategy::SDR);
1594 for (std::size_t k = 0; k < nodes.size(); ++k) {
1595 if (nodes[k] == 0 || nodes[k] > sn_.nodes.size())
1596 throw InputError(
"serialRouting: the path names a node that does not exist");
1599 if (nodes.size() > 1 && sn_.nodes[nodes.back() - 1].nodetype != NodeType::Sink)
1612 for (
const auto& kv : Pm.
entries) {
1613 const auto& k = kv.first;
1614 if (k.r == 0 || k.r > sn_.classes.size() || k.s == 0 || k.s > sn_.classes.size())
1615 throw InputError(
"link: the routing matrix names a class that does not exist");
1616 if (k.i == 0 || k.i > sn_.nodes.size() || k.j == 0 || k.j > sn_.nodes.size())
1617 throw InputError(
"link: the routing matrix names a node that does not exist");
1631 const std::size_t K = sn_.classes.size();
1632 const std::size_t I = sn_.nodes.size();
1638 for (std::size_t a = 0; a < cache_miss_arcs_.size(); ++a) {
1639 const CacheMissArc&
mc = cache_miss_arcs_[a];
1640 P.
set(
mc.cls,
mc.cls,
mc.from_node,
mc.to_node, one);
1642 std::map<std::pair<std::size_t, std::size_t>, std::size_t> csid;
1643 for (std::size_t i = 1; i <= I; ++i)
1644 for (std::size_t j = 1; j <= I; ++j) {
1647 for (std::size_t r = 1; r <= K; ++r)
1648 for (std::size_t s = 1; s <= K; ++s) {
1649 const T p = P.
get(r, s, i, j);
1651 C(r - 1, s - 1) = p;
1656 bool offdiag =
false;
1657 for (std::size_t r = 1; r <= K; ++r) {
1659 for (std::size_t s = 1; s <= K; ++s) S += C(r - 1, s - 1);
1661 for (std::size_t s = 1; s <= K; ++s) C(r - 1, s - 1) = T(C(r - 1, s - 1) / S);
1663 C(r - 1, r - 1) = one;
1665 for (std::size_t s = 1; s <= K; ++s)
1668 if (!offdiag)
continue;
1684 if (sn_.nodes[i - 1].nodetype == NodeType::Sink &&
1685 sn_.nodes[j - 1].nodetype == NodeType::Source)
1687 csid[std::make_pair(i, j)] =
1688 add_class_switch(
"CS_" + sn_.nodes[i - 1].name +
"_to_" + sn_.nodes[j - 1].name, C);
1693 for (
const auto& kv : csid) {
1694 const std::size_t i = kv.first.first, j = kv.first.second, c = kv.second;
1695 for (std::size_t r = 1; r <= K; ++r)
1696 for (std::size_t s = 1; s <= K; ++s) {
1697 const T p = P.
get(r, s, i, j);
1699 P.
set(r, r, i, c, T(P.
get(r, r, i, c) + p));
1700 P.
set(r, s, i, j, zero);
1701 P.
set(s, s, c, j, one);
1704 for (
const auto& kv : P.
entries) {
1705 const auto& k = kv.first;
1706 sn_.set_route(k.r, k.s, k.i, k.j, kv.second);
1712 for (
const auto& np : sn_.nodeparam) {
1713 const std::size_t ci = np.first;
1717 const std::size_t rd = rq.first + 1;
1718 std::vector<std::size_t> nodeset = rq.second;
1719 nodeset.push_back(ci);
1720 bool minted =
false;
1721 const std::size_t Inodes = sn_.nodes.size();
1722 for (std::size_t i = 0; i < cp.
nitems; ++i) {
1724 if (rc == 0)
continue;
1730 std::vector<bool> declared(nodeset.size(),
false);
1731 for (std::size_t si = 0; si < nodeset.size(); ++si)
1732 for (std::size_t b = 1; b <= Inodes && !declared[si]; ++b)
1734 declared[si] =
true;
1735 for (std::size_t si = 0; si < nodeset.size(); ++si) {
1736 if (declared[si])
continue;
1737 for (std::size_t b : nodeset) {
1738 const T p = Pm.
get(rd, rd, nodeset[si], b);
1740 sn_.set_route(rc, rc, nodeset[si], b, p);
1756 for (std::size_t a : nodeset)
1757 for (std::size_t b : nodeset)
1786 const std::size_t Kc = sn_.classes.size(), Ic = sn_.nodes.size();
1787 for (std::size_t i = 1; i <= Ic; ++i) {
1788 NodeDef& nd = sn_.nodes[i - 1];
1789 nd.
routing.resize(Kc, RoutingStrategy::PROB);
1790 bool linked =
false;
1791 for (std::size_t j = 1; j <= Ic && !linked; ++j)
1792 for (std::size_t a = 1; a <= Kc && !linked; ++a)
1793 for (std::size_t b = 1; b <= Kc && !linked; ++b)
1795 if (!linked)
continue;
1796 for (std::size_t r = 1; r <= Kc; ++r) {
1797 if (nd.
routing[r - 1] != RoutingStrategy::PROB)
continue;
1798 if (sn_.classes[r - 1].self_looping)
continue;
1799 if (r <= sn_.issignal.size() && sn_.issignal[r - 1])
continue;
1800 bool routed =
false;
1801 for (std::size_t j = 1; j <= Ic && !routed; ++j)
1802 for (std::size_t s = 1; s <= Kc && !routed; ++s)
1804 if (!routed) nd.
routing[r - 1] = RoutingStrategy::RAND;
1813 routing_linked_ =
true;
1814 apply_fork_overrides();
1823 materialize_transitions();
1824 if (routing_installed()) apply_fork_overrides();
1826 sn_.refresh_struct();
1846 const std::function<T(
const std::vector<std::size_t>&)>& mu,
1847 const std::vector<std::vector<bool>>& swap_graph =
1848 std::vector<std::vector<bool>>()) {
1849 const std::size_t ist = station_of(node,
"set_pas");
1854 pp.
swap_graph.assign(sn_.classes.size(), std::vector<bool>(sn_.classes.size(),
false));
1855 sn_.pasparam[ist] = pp;
1861 for (std::size_t a = 0; a < R; ++a)
1862 for (std::size_t b = 0; b < pp.
swap_graph[a].size(); ++b)
1873 const std::function<T(
const std::vector<std::size_t>&)>& mu,
1877 const std::size_t R = sn_.classes.size();
1878 pp.
swap_graph.assign(R, std::vector<bool>(R,
false));
1879 for (std::size_t a = 0; a < R && a < swap_graph.
rows(); ++a)
1880 for (std::size_t b = 0; b < R && b < swap_graph.
cols(); ++b)
1882 sn_.pasparam[ist] = pp;
1894 std::size_t pk = 1) {
1895 const std::size_t ist = station_of(node,
"set_polling");
1896 if (sn_.stations[ist - 1].sched != SchedStrategy::POLLING)
1897 throw InputError(
"set_polling: the station is not POLLING-scheduled");
1902 if (pp.
switchover.size() < sn_.classes.size())
1904 sn_.pollingparam[ist] = pp;
1919 const std::size_t K = sn_.classes.size();
1920 if (node == 0 || node > sn_.nodes.size())
1921 throw InputError(
"set_sync_reply: node index is out of range");
1922 if (call_cls == 0 || call_cls > K || reply_cls == 0 || reply_cls > K)
1923 throw InputError(
"set_sync_reply: class index is out of range");
1924 const std::size_t ist = sn_.nodes[node - 1].station;
1925 if (ist == 0)
throw InputError(
"set_sync_reply: node is not a station");
1929 if (sn_.replyblock.size() < sn_.nodes.size()) sn_.replyblock.resize(sn_.nodes.size());
1930 for (std::vector<bool>& row : sn_.replyblock)
1931 if (row.size() < K) row.resize(K,
false);
1932 if (sn_.syncreply.size() < K) sn_.syncreply.resize(K, 0);
1933 sn_.replyblock[node - 1][call_cls - 1] =
true;
1934 sn_.syncreply[call_cls - 1] = reply_cls;
1957 const std::vector<double>& class_max_jobs,
1958 double global_max_jobs = -1.0,
1959 const std::vector<DropStrategy>& rule = std::vector<DropStrategy>(),
1960 const std::vector<double>& class_max_memory = std::vector<double>(),
1961 const std::vector<T>& class_size = std::vector<T>(),
1962 double global_max_memory = -1.0,
1963 const std::string& name = std::string()) {
1964 const std::size_t M = sn_.stations.size(), K = sn_.classes.size();
1967 rg.
cap.assign(M, std::vector<double>(K + 1, -1.0));
1968 rg.
maxmem.assign(M, -1.0);
1973 rg.
rule.assign(K, DropStrategy::WAITQ);
1976 for (std::size_t r = 0; r < K && r < rule.size(); ++r) rg.
rule[r] = rule[r];
1977 for (std::size_t r = 0; r < K && r < class_size.size(); ++r) rg.
size[r] = class_size[r];
1979 for (std::size_t j = 0; j < nodes.size(); ++j) {
1980 const std::size_t ist = station_of(nodes[j],
"addRegion");
1982 for (std::size_t r = 0; r < K; ++r) {
1988 if (r >= class_max_jobs.size()) {
1989 rg.
cap[ist - 1][r] = -1.0;
1992 double c = class_max_jobs[r];
1993 if (r < class_max_memory.size() && class_max_memory[r] != -1.0) {
1996 const double memjobs = std::floor(class_max_memory[r] / sz);
1997 c = c == -1.0 ? memjobs : std::min(c, memjobs);
2000 rg.
cap[ist - 1][r] = c;
2002 rg.
cap[ist - 1][K] = global_max_jobs;
2003 rg.
maxmem[ist - 1] = global_max_memory;
2005 sn_.regions.push_back(rg);
2006 return sn_.regions.size();
2015 if (region == 0 || region > sn_.regions.size())
2016 throw InputError(
"set_region_weights: region index is out of range");
2018 for (std::size_t r = 0; r < rg.
weight.size() && r < weight.size(); ++r)
2019 rg.
weight[r] = weight[r];
2024 if (region == 0 || region > sn_.regions.size())
2025 throw InputError(
"set_region_constraint: region index is out of range");
2026 if (A.
rows() != b.size())
2027 throw InputError(
"set_region_constraint: A and b disagree on the number of rows");
2028 sn_.regions[region - 1].lincon_A = A;
2029 sn_.regions[region - 1].lincon_b = b;
2041 const std::function<T(
const std::vector<T>&)>& fn,
2042 const std::string& kind = std::string(), std::size_t node = 0,
2043 std::size_t cls = 0) {
2044 for (std::size_t i = 0; i < sn_.reward.size(); ++i)
2045 if (sn_.reward[i].name == nm) {
2046 sn_.reward[i].fn = fn;
2047 sn_.reward[i].kind = kind;
2048 sn_.reward[i].node = node;
2049 sn_.reward[i].cls = cls;
2058 sn_.reward.push_back(rw);
2071 std::size_t target = 0,
2072 const std::vector<T>& remdist = std::vector<T>()) {
2073 const std::size_t K = sn_.classes.size();
2074 if (cls == 0 || cls > K)
throw InputError(
"set_signal: class index is out of range");
2075 if (target > K)
throw InputError(
"set_signal: target class index is out of range");
2077 if (sn_.issignal.size() < K) {
2078 sn_.issignal.resize(K,
false);
2080 sn_.signaltarget.resize(K, 0);
2082 sn_.signalremdist.resize(K, std::vector<T>());
2084 sn_.issignal[cls - 1] =
true;
2085 sn_.signaltype[cls - 1] = type;
2086 sn_.signaltarget[cls - 1] = target;
2087 sn_.signalrempolicy[cls - 1] = policy;
2088 sn_.signalremdist[cls - 1] = remdist;
2091 std::size_t
station_index(std::size_t node)
const {
return station_of(node,
"station_index"); }
2097 struct TransitionArc {
2098 std::size_t mode, node, cls;
2100 TransitionArc(std::size_t m, std::size_t n, std::size_t c,
const T& v)
2101 : mode(m), node(n), cls(c), count(v) {}
2105 struct TransitionArcs {
2106 std::vector<TransitionArc> enab, inhib, fire;
2117 std::map<std::size_t, TransitionArcs> pending_arcs_;
2120 TransitionParam<T>& declarative_transition(std::size_t node,
const char* what) {
2121 if (node == 0 || node > sn_.nodes.size())
2122 throw InputError(std::string(what) +
": node index is out of range");
2123 if (sn_.nodes[node - 1].nodetype != NodeType::Transition)
2124 throw InputError(std::string(what) +
": node '" + sn_.nodes[node - 1].name +
2125 "' is not a Transition");
2126 if (pending_arcs_.find(node) == pending_arcs_.end())
2127 throw InputError(std::string(what) +
": the Transition '" + sn_.nodes[node - 1].name +
2128 "' was created from a finished TransitionParam, so its modes are "
2129 "already fixed; create it as add_transition(name) to declare them "
2131 return sn_.transparam[node];
2135 TransitionParam<T>& mode_param(std::size_t node, std::size_t mode,
const char* what) {
2136 TransitionParam<T>& tp = declarative_transition(node, what);
2137 if (mode == 0 || mode > tp.nmodes)
2138 throw InputError(std::string(what) +
": the Transition '" + sn_.nodes[node - 1].name +
2139 "' has no mode " + std::to_string(mode));
2144 void arc_target(std::size_t node, std::size_t mode, std::size_t cls, std::size_t target,
2145 bool must_be_place,
const char* what) {
2146 mode_param(node, mode, what);
2147 if (cls == 0 || cls > sn_.classes.size())
2149 ": class index is out of range; declare the job class before the "
2150 "arcs that move its tokens");
2151 if (target == 0 || target > sn_.nodes.size())
2152 throw InputError(std::string(what) +
": the node the arc names is out of range");
2153 if (must_be_place && sn_.nodes[target - 1].nodetype != NodeType::Place)
2154 throw InputError(std::string(what) +
": '" + sn_.nodes[target - 1].name +
2155 "' is not a Place, and only a Place holds the tokens a mode tests");
2159 static void fill_arcs(std::vector<Matrix<T> >& dst,
const std::vector<TransitionArc>& arcs) {
2160 for (std::size_t a = 0; a < arcs.size(); ++a)
2161 dst[arcs[a].mode - 1](arcs[a].node - 1, arcs[a].cls - 1) = arcs[a].count;
2172 static void normalize_arcs(std::vector<Matrix<T> >& a, std::size_t N, std::size_t K,
2173 bool inhibitor,
const std::string& nm,
const char* what) {
2174 for (std::size_t m = 0; m < a.size(); ++m) {
2175 if (a[m].rows() == N && a[m].cols() == K)
continue;
2176 if (a[m].rows() > N || a[m].cols() > K)
2178 "the Transition '" + nm +
"' has an " + what +
" matrix of " +
2179 std::to_string(a[m].rows()) +
"x" + std::to_string(a[m].cols()) +
2180 " against a model of " + std::to_string(N) +
" nodes and " +
2181 std::to_string(K) +
" classes; an arc matrix is read row by row against "
2182 "the marking, so a wider one is read past its end");
2183 Matrix<T> grown(N, K, inhibitor ? no_inhibitor() : num_traits<T>::from_int(0));
2184 for (std::size_t q = 0; q < a[m].rows(); ++q)
2185 for (std::size_t r = 0; r < a[m].cols(); ++r) grown(q, r) = a[m](q, r);
2198 static T no_inhibitor() {
2199 return num_traits<T>::from_double(std::numeric_limits<double>::infinity());
2211 void materialize_transitions() {
2212 if (sn_.transparam.empty())
return;
2213 const std::size_t N = sn_.nodes.size(), K = sn_.classes.size();
2214 const T zero = num_traits<T>::from_int(0);
2215 for (
typename std::map<std::size_t, TransitionParam<T> >::iterator it =
2216 sn_.transparam.begin();
2217 it != sn_.transparam.end(); ++it) {
2218 TransitionParam<T>& tp = it->second;
2219 const std::string& nm = sn_.nodes[it->first - 1].name;
2220 if (pending_arcs_.find(it->first) == pending_arcs_.end()) {
2221 normalize_arcs(tp.enabling, N, K,
false, nm,
"enabling");
2222 normalize_arcs(tp.inhibiting, N, K,
true, nm,
"inhibiting");
2223 normalize_arcs(tp.firing, N, K,
false, nm,
"firing");
2227 throw InputError(
"Network '" + sn_.name +
"': the Transition '" + nm +
2228 "' has no mode, and every transition needs at least one");
2229 const TransitionArcs& pa = pending_arcs_[it->first];
2230 tp.enabling.assign(tp.nmodes, Matrix<T>(N, K, zero));
2231 tp.inhibiting.assign(tp.nmodes, Matrix<T>(N, K, no_inhibitor()));
2232 tp.firing.assign(tp.nmodes, Matrix<T>(N, K, zero));
2233 fill_arcs(tp.enabling, pa.enab);
2234 fill_arcs(tp.inhibiting, pa.inhib);
2235 fill_arcs(tp.firing, pa.fire);
2239 for (std::size_t m = 0; m < tp.nmodes; ++m)
2240 tp.firingphases[m] =
2248 std::map<std::size_t, std::vector<std::size_t> > cache_item_classes_;
2251 struct CacheMissArc {
2252 std::size_t cls, from_node, to_node;
2253 CacheMissArc(std::size_t c, std::size_t f, std::size_t t)
2254 : cls(c), from_node(f), to_node(t) {}
2256 std::vector<CacheMissArc> cache_miss_arcs_;
2259 static std::vector<std::size_t> per_item_classes(
const std::vector<std::size_t>& spec,
2260 std::size_t nitems,
const char* what) {
2261 if (spec.size() == nitems)
return spec;
2262 if (spec.size() == 1)
return std::vector<std::size_t>(nitems, spec[0]);
2264 ": pass one class per item or a single class shared by all");
2268 JobClass& class_ref(std::size_t cls,
const char* what) {
2269 if (cls == 0 || cls > sn_.classes.size())
2270 throw InputError(std::string(what) +
": class index is out of range");
2271 return sn_.classes[cls - 1];
2274 void init_node(std::size_t nd) {
2275 sn_.nodes[nd - 1].routing.assign(sn_.classes.size(), RoutingStrategy::PROB);
2279 void grow_class_vectors() {
2280 const std::size_t K = sn_.classes.size();
2281 for (NodeDef& nd : sn_.nodes) nd.routing.resize(K, RoutingStrategy::PROB);
2282 for (Station<T>& st : sn_.stations) {
2283 if (!st.classcap.empty())
2284 st.classcap.resize(K, std::numeric_limits<double>::infinity());
2285 if (!st.droprule.empty()) st.droprule.resize(K, 0);
2286 if (!st.schedparam.empty()) st.schedparam.resize(K, num_traits<T>::from_int(1));
2291 Station<T>& station_ref(std::size_t node, std::size_t cls,
const char* what) {
2292 const std::size_t ist = station_of(node, what);
2293 if (cls == 0 || cls > sn_.classes.size())
2294 throw InputError(std::string(what) +
": class index is out of range");
2295 return sn_.stations[ist - 1];
2306 void grow_class_slot(std::vector<V>& v, std::size_t cls,
const V& fill) {
2307 const std::size_t K = sn_.classes.size();
2308 if (v.size() < K) v.resize(K < cls ? cls : K, fill);
2311 std::size_t station_of(std::size_t node,
const std::string& what)
const {
2312 if (node == 0 || node > sn_.nodes.size())
2313 throw InputError(what +
": node index " + std::to_string(node) +
" does not exist");
2314 const std::size_t ist = sn_.nodes[node - 1].station;
2316 throw InputError(what +
": node '" + sn_.nodes[node - 1].name +
2317 "' is not a station (it serves no jobs)");
2331 struct ForkOverride {
2332 enum Kind { TASKS, DIST, PROB };
2334 std::size_t fork = 0, cls = 0, dest = 0;
2336 lang::Distrib<T> dist;
2338 std::vector<ForkOverride> fork_overrides_;
2340 bool routing_linked_ =
false;
2349 void record_fork_override(
const ForkOverride& ov) {
2350 if (ov.fork == 0 || ov.fork > sn_.nodes.size() ||
2351 sn_.nodes[ov.fork - 1].nodetype != NodeType::Fork)
2352 throw InputError(
"the node given is not a Fork of this model");
2353 if (ov.cls == 0 || ov.cls > sn_.classes.size())
2354 throw InputError(
"a fork override names a class that does not exist");
2355 fork_overrides_.push_back(ov);
2356 if (routing_installed()) apply_fork_override(ov);
2367 bool routing_installed()
const {
return routing_linked_; }
2370 void apply_fork_overrides() {
2371 for (std::size_t i = 0; i < fork_overrides_.size(); ++i)
2372 apply_fork_override(fork_overrides_[i]);
2375 void apply_fork_override(
const ForkOverride& ov) {
2376 qn::ForkParam<T>& f = fork_param(ov.fork);
2377 const std::vector<std::size_t> dests = fork_dests(ov.fork, ov.dest);
2378 for (std::size_t x = 0; x < dests.size(); ++x) {
2379 const std::size_t k = dests[x];
2381 case ForkOverride::TASKS:
2382 f.fan_out_link(k - 1, ov.cls - 1) = num_traits<T>::from_double(ov.value);
2384 case ForkOverride::DIST:
2385 f.fan_out_dist[k - 1][ov.cls - 1] = ov.dist;
2388 f.fan_out_link(k - 1, ov.cls - 1) = ov.dist.mean;
2390 case ForkOverride::PROB:
2391 f.fan_out_prob(k - 1, ov.cls - 1) = num_traits<T>::from_double(ov.value);
2395 refresh_fork_scalar(sn_.nodes[ov.fork - 1], f);
2398 qn::ForkParam<T>& fork_param(std::size_t fork_node) {
2399 if (fork_node == 0 || fork_node > sn_.nodes.size() ||
2400 sn_.nodes[fork_node - 1].nodetype != NodeType::Fork)
2401 throw InputError(
"the node given is not a Fork of this model");
2402 qn::ForkParam<T>& f = sn_.forkparam[fork_node];
2403 const std::size_t I = sn_.nodes.size(), K = sn_.classes.size();
2404 if (f.fan_out_link.rows() == I && f.fan_out_link.cols() == K)
return f;
2405 const T zero = num_traits<T>::from_int(0);
2408 f.fan_out_dist.assign(I, std::vector<lang::Distrib<T> >(K));
2409 const T tpl = num_traits<T>::from_double(sn_.nodes[fork_node - 1].tasks_per_link);
2410 const T one = num_traits<T>::from_int(1);
2411 for (std::size_t k = 1; k <= I; ++k)
2412 for (std::size_t r = 1; r <= K; ++r)
2413 if (fork_links_to(fork_node, k, r)) {
2414 f.fan_out_link(k - 1, r - 1) = tpl;
2415 f.fan_out_prob(k - 1, r - 1) = one;
2429 void refresh_fork_scalar(qn::NodeDef& nd,
const qn::ForkParam<T>& f) {
2431 std::size_t cnt = 0;
2432 for (std::size_t k = 0; k < f.fan_out_link.rows(); ++k)
2433 for (std::size_t r = 0; r < f.fan_out_link.cols(); ++r) {
2434 if (num_traits<T>::to_double(f.fan_out_prob(k, r)) == 0.0)
continue;
2435 acc += num_traits<T>::to_double(f.fan_out_link(k, r)) *
2436 num_traits<T>::to_double(f.fan_out_prob(k, r));
2439 if (cnt > 0) nd.tasks_per_link = acc /
static_cast<double>(cnt);
2456 bool fork_links_to(std::size_t fork_node, std::size_t k, std::size_t r)
const {
2457 const std::size_t K = sn_.classes.size();
2458 for (std::size_t s = 1; s <= K; ++s)
2459 if (num_traits<T>::to_double(sn_.route_eff(r, s, fork_node, k)) != 0.0)
return true;
2469 std::vector<std::size_t> fork_dests(std::size_t fork_node, std::size_t dest_node)
const {
2470 std::vector<std::size_t> out;
2471 const std::size_t I = sn_.nodes.size(), K = sn_.classes.size();
2472 if (dest_node != 0) {
2474 throw InputError(
"a fork override names a node that does not exist");
2475 out.push_back(dest_node);
2478 for (std::size_t k = 1; k <= I; ++k)
2479 for (std::size_t r = 1; r <= K; ++r)
2480 if (fork_links_to(fork_node, k, r)) { out.push_back(k);
break; }
2482 throw InputError(
"a fork override was set on '" + sn_.nodes[fork_node - 1].name +
2483 "', which links nowhere yet: call link() before the override");
2494 void validate()
const {
2495 if (sn_.classes.empty())
throw InputError(
"Network '" + sn_.name +
"': it has no classes");
2496 if (sn_.stations.empty())
2497 throw InputError(
"Network '" + sn_.name +
"': it has no stations");
2513 const std::size_t K = sn_.classes.size();
2514 const T zero = num_traits<T>::from_int(0);
2515 std::vector<std::vector<bool>> switches(K, std::vector<bool>(K,
false));
2516 for (std::size_t r = 0; r < K; ++r)
2517 for (std::size_t sc = 0; sc < K; ++sc) {
2518 if (r == sc)
continue;
2519 for (std::size_t i = 1; i <= sn_.nodes.size() && !switches[r][sc]; ++i)
2520 for (std::size_t j = 1; j <= sn_.nodes.size(); ++j)
2521 if (sn_.get_route(r + 1, sc + 1, i, j) > zero) {
2522 switches[r][sc] =
true;
2526 for (
const auto& kv : sn_.csmatrix) {
2527 const Matrix<T>& C = kv.second;
2528 for (std::size_t r = 0; r < K && r < C.rows(); ++r)
2529 for (std::size_t sc = 0; sc < K && sc < C.cols(); ++sc)
2530 if (r != sc && C(r, sc) > zero) switches[r][sc] =
true;
2532 for (
const auto& kv : sn_.nodeparam) {
2533 const CacheParam<T>& cp = kv.second;
2534 for (std::size_t r = 0; r < K; ++r) {
2535 if (r < cp.hitclass.size() && cp.hitclass[r] >= 1 && cp.hitclass[r] <= K)
2536 switches[r][cp.hitclass[r] - 1] =
true;
2537 if (r < cp.missclass.size() && cp.missclass[r] >= 1 && cp.missclass[r] <= K)
2538 switches[r][cp.missclass[r] - 1] =
true;
2540 for (
const auto& row : cp.retrieval_classes)
2541 for (std::size_t r = 0; r < K && r < row.size(); ++r)
2542 if (row[r] >= 1 && row[r] <= K) switches[r][row[r] - 1] =
true;
2548 for (std::size_t r = 0; r < K; ++r)
2549 if (sn_.classes[r].spawn >= 1 && sn_.classes[r].spawn <= K)
2550 switches[r][sn_.classes[r].spawn - 1] =
true;
2551 for (std::size_t r = 0; r < sn_.classes.size(); ++r) {
2555 bool switched_into =
false;
2556 for (std::size_t q = 0; q < K; ++q)
2557 if (switches[q][r]) switched_into =
true;
2558 bool served =
false;
2559 for (std::size_t i = 0; i < sn_.stations.size(); ++i)
2560 if (!sn_.service[i][r].disabled) served =
true;
2566 for (std::size_t nd = 0; nd < sn_.nodes.size(); ++nd)
2567 if (sn_.nodes[nd].nodetype == NodeType::Transition) petri =
true;
2568 if (!served && !switched_into && !petri)
2569 throw InputError(
"Network '" + sn_.name +
"': class '" + sn_.classes[r].name +
2570 "' has no service process at any station");
2599 for (std::size_t i = 0; i < sn_.nodes.size(); ++i) {
2600 if (sn_.nodes[i].nodetype != NodeType::Fork)
continue;
2601 bool closed =
false;
2602 for (
const auto& fjp : sn_.fj)
2603 if (fjp.first == i + 1) closed =
true;
2604 if (closed)
continue;
2608 std::vector<bool> seen(sn_.nodes.size(),
false);
2609 std::vector<std::size_t> stack(1, i + 1);
2611 bool departs =
false;
2612 while (!stack.empty() && !departs) {
2613 const std::size_t u = stack.back();
2615 for (std::size_t v = 1; v <= sn_.nodes.size() && !departs; ++v) {
2616 if (seen[v - 1])
continue;
2618 for (std::size_t r = 1; r <= K && !edge; ++r)
2619 for (std::size_t s = 1; s <= K; ++s)
2620 if (sn_.get_route(r, s, u, v) > zero) {
2624 if (!edge)
continue;
2625 if (sn_.nodes[v - 1].nodetype == NodeType::Sink) {
2634 throw InputError(
"Network '" + sn_.name +
"': the Fork '" + sn_.nodes[i].name +
2635 "' is not closed by a Join and no Sink is reachable from it, "
2636 "so its siblings can neither merge nor depart");
UnsupportedError(const std::string &what)
A network plus its refreshed NetworkStruct.
void set_service_rate_function(std::size_t node, const std::function< T(const std::vector< std::size_t > &)> &muFun, const Matrix< T > &swap_graph=Matrix< T >())
Queue.setServiceRateFunction(muFun): the TOTAL service rate of a PAS or OI station as a function of t...
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_global_dependence(const GdScaling< T > &fun, const std::vector< T > &peak, int wire_cutoff)
As above, with an explicit per-slot OPEN-class truncation used when phi is materialized onto the JSON...
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)
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)
void set_enabling_conditions(std::size_t node, std::size_t mode, std::size_t cls, std::size_t place, const T &tokens)
Transition.setEnablingConditions(mode, class, place, tokens): how many class-r tokens the mode needs ...
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)
void set_mode_timing(std::size_t node, std::size_t mode, lang::TimingStrategy ts)
Transition.setTimingStrategy(mode, strategy): TIMED or IMMEDIATE.
std::size_t add_place(const std::string &nm, SchedStrategy sched)
A Place whose EMBEDDED QUEUE is served under sched: the QUEUEING PLACE of a queueing Petri net,...
Network(const std::string &nm)
void set_firing_weight(std::size_t node, std::size_t mode, const T &w)
Transition.setFiringWeights(mode, weight): the share among tied modes.
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.
RoutingMatrix< T > init_routing_matrix() const
An empty routing matrix, MATLAB's model.initRoutingMatrix.
std::vector< std::size_t > set_miss_cache(std::size_t cache_node, std::size_t next_cache, const std::vector< std::size_t > &hit_classes_at_next)
Cache.setMissCache(readClass, nextCache, hitClassAtNext): send this cache's misses to next_cache pres...
void set_class_immediate_feedback(std::size_t cls)
jobclass.setImmediateFeedback(): the same property, class-wide.
void set_mode_distribution(std::size_t node, std::size_t mode, const Distrib< T > &d)
Transition.setDistribution(mode, dist): the mode's firing law.
void set_polling(std::size_t node, lang::PollingType ptype, const std::vector< Distrib< T > > &switchover=std::vector< Distrib< T > >(), std::size_t pk=1)
Queue.setPollingType(...): the polling discipline of a POLLING station and the switchover walks betwe...
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_inhibiting_conditions(std::size_t node, std::size_t mode, std::size_t cls, std::size_t place, const T &tokens)
Transition.setInhibitingConditions(mode, class, place, tokens): the class-r count at place that BLOCK...
void set_number_of_servers(std::size_t node, double n)
queue.setNumberOfServers(n).
RoutingMatrix< T > serial_routing(const std::vector< std::size_t > &nodes) const
model.serialRouting(nodes): a unit-probability path through nodes.
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_mode_firing_dependence(std::size_t node, std::size_t mode, const std::function< T(const std::vector< T > &)> &g)
Transition.setFiringRateDependence(mode, g): g(marking) scales the rate.
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_class_switch_matrix(std::size_t node, const Matrix< T > &C)
Install the switching matrix of a ClassSwitch created without one.
void set_routing(std::size_t node, std::size_t cls, RoutingStrategy rs)
node.setRouting(class, strategy).
std::size_t add_transition(const std::string &nm)
A Transition with NO modes yet: Transition(model, name) as MATLAB, the JAR and Python spell it,...
void set_join_strategy(std::size_t node, lang::JoinStrategy strategy, double quorum=0.0)
Join.setStrategy(...): STD waits for every sibling, PARTIAL for a quorum.
void set_fork_branch_probability(std::size_t fork_node, std::size_t jobclass, std::size_t dest_node, double prob)
A branch that fires only with probability prob.
std::size_t add_cache(const std::string &nm, const CacheParam< T > &par)
A Cache node with its item population, list capacities and popularity.
void set_reward(const std::string &nm, const std::function< T(const std::vector< T > &)> &fn, const std::string &kind=std::string(), std::size_t node=0, std::size_t cls=0)
model.setReward(name, fn): a named reward evaluated on the AGGREGATE state row, the per-(station,...
std::size_t add_mode(std::size_t node, const std::string &nm)
Transition.addMode(name): a new firing mode, returning its 1-based index.
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)
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.
void set_firing_priority(std::size_t node, std::size_t mode, double prio)
Transition.setFiringPriorities(mode, priority).
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().
std::size_t add_class_switch(const std::string &nm)
A ClassSwitch node whose matrix is installed LATER, by set_class_switch_matrix.
void set_batch_reject(std::size_t node, std::size_t cls, const T &p)
Queue.setBatchRejectProbability(class, p).
void set_class_dependence(std::size_t node, const CdScaling< T > &fun, const std::vector< T > &peak=std::vector< T >())
station.setClassDependence(beta, peakRatePerClass).
void set_state_dep_routing(std::size_t entry, std::size_t departure, const std::vector< std::vector< std::size_t > > &branches, const std::vector< std::size_t > &level, const std::vector< double > &C, const Matrix< double > &d, std::size_t cls=0)
node.setStateDepRouting(class, departure, branches, level, C, d).
std::size_t add_place(const std::string &nm)
A Place: an SPN token container.
void set_routing_weights(std::size_t node, std::size_t cls, const std::map< std::size_t, double > &weights)
The per-destination weights of a WRROBIN dispatcher, per (node, class).
void set_item_miss_class(std::size_t cache_node, const std::vector< std::size_t > &miss_classes)
Cache.setItemMissClass(readClass, missClasses): terminate a cache network, every per-item class of th...
void set_log_path(const std::string &path)
Network.setLogPath: the directory every Logger of this model writes into.
void set_capacity(std::size_t node, double k)
station.setCapacity(k), the K of Kendall's notation.
NetworkStruct< T > & raw_struct()
The struct WITHOUT refreshing it, for a caller that is still building.
void set_switchover(std::size_t node, std::size_t cls, const Distrib< T > &so)
Queue.setSwitchover(jobclass, distrib): the switchover time of a class.
void set_immediate_feedback(std::size_t node, std::size_t cls)
queue.setImmediateFeedback(class): a completing job of that class is fed straight back into service,...
void set_balking(std::size_t node, std::size_t cls, lang::BalkingStrategy strategy, const std::vector< typename Station< T >::BalkingThreshold > &thresholds)
Queue.setBalking(class, strategy, thresholds): an arrival that refuses to JOIN, on the state it finds...
void add_server_type(std::size_t node, const typename Station< T >::ServerType &stype)
Queue.addServerType(...): one heterogeneous server pool of the station.
void set_setup_delayoff(std::size_t node, std::size_t cls, const Distrib< T > &setup, const Distrib< T > &delayoff)
Queue.setDelayOff(class, setupTime, delayoffTime): the station powers down after sitting idle for the...
void set_retrieval_system(std::size_t cache_node, std::size_t read_class, std::size_t miss_class, const std::vector< std::size_t > &queue_nodes)
Cache.setRetrievalSystem(readClass, missClass, queues): a delayed-hit cache whose misses are fetched ...
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< double > &d)
void set_reference_class(std::size_t cls)
JobClass.setReferenceClass(true): sn.refclass(c) picks this class.
void pas_mirror(std::size_t ist, const std::function< double(const std::vector< std::size_t > &)> &mu, const Matrix< double > &swap_graph)
std::size_t add_join_unbound(const std::string &nm)
void set_hetero_sched_policy(std::size_t node, lang::HeteroSchedPolicy policy)
Queue.setHeteroSchedPolicy(...): how the server pools are picked among.
std::size_t add_join(const std::string &nm, std::size_t fork_node)
A Join node, which IS a station: it serves at an infinite rate, and the synchronisation delay is supp...
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...
void set_item_read_classes(std::size_t cache_node, const std::vector< std::size_t > &read_classes, const std::vector< std::size_t > &hit_classes)
Cache.setItemReadClasses(readClasses, hitClasses): declare that read_classes[i] is the request stream...
void set_mode_servers(std::size_t node, std::size_t mode, double n)
Transition.setNumberOfServers(mode, n); GlobalConstants::MaxInt is infinite.
void set_switchover(std::size_t node, std::size_t from_cls, std::size_t to_cls, const Distrib< T > &so)
Queue.setSwitchover(fromClass, toClass, distrib): the walk between two CLASSES at an ordinary station...
void set_firing_outcome(std::size_t node, std::size_t mode, std::size_t cls, std::size_t dest, const T &tokens)
Transition.setFiringOutcome(mode, class, node, tokens): the class-r tokens the firing deposits.
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_sync_reply(std::size_t node, std::size_t call_cls, std::size_t reply_cls)
Declare a SYNCHRONOUS call: a job of call_cls leaving node keeps its server until a job of the REPLY ...
void set_signal(std::size_t cls, lang::SignalType type, lang::RemovalPolicy policy=lang::RemovalPolicy::RANDOM, std::size_t target=0, const std::vector< double > &remdist=std::vector< double >())
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.
std::size_t station_index(std::size_t node) const
void set_region_weights(std::size_t region, const std::vector< T > &weight)
FiniteCapacityRegion.setClassWeight: the per-class weight the region's global cap counts a job agains...
void set_arrival(std::size_t node, std::size_t cls, const Distrib< T > &d)
source.setArrival(class, dist): the same table, at the Source.
std::size_t add_region(const std::vector< std::size_t > &nodes, const std::vector< double > &class_max_jobs, double global_max_jobs=-1.0, const std::vector< DropStrategy > &rule=std::vector< DropStrategy >(), const std::vector< double > &class_max_memory=std::vector< double >(), const std::vector< T > &class_size=std::vector< T >(), double global_max_memory=-1.0, const std::string &name=std::string())
FiniteCapacityRegion(model, nodes): a cap on the jobs held ACROSS a set of stations.
void set_polling_type(std::size_t node, lang::PollingType rule, int par=0)
Queue.setPollingType(rule, par): the polling discipline of a POLLING station, identical across all cl...
The routing matrix a model script fills in, MATLAB's P cell array.
void set(std::size_t cls, const RoutingMatrix< T > &block)
P.set(class, Network.serialRouting(...)): install a one-class block.
T get(std::size_t r, std::size_t s, std::size_t i, std::size_t j) const
void set(std::size_t r, std::size_t s, std::size_t i, std::size_t j, const T &p)
void set(std::size_t i, std::size_t j, const T &p)
std::map< Key, double > entries
What refreshProcessRepresentations and refreshLST compute FROM a distribution: the (D0,...
The exception types the port throws.
Dense matrix and non-owning view.
void sn_fj_nodevisits_mmt(qn::NetworkStruct< T > &sn)
Rewrite sn.nodevisits with the MMT correction.
mam::Map< T > dist_to_map(const Distrib< T > &d)
SchedStrategy
Scheduling disciplines, with the values of MATLAB SchedStrategy.
DropStrategy
Blocking and loss rules, with the values of MATLAB DropStrategy.
TimingStrategy
SPN transition timing, with the values of MATLAB TimingStrategy.
@ 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)
void prior_refresh_moments(Distrib< T > &d)
Write the mixture moments onto a Prior, the counterpart of dist_refresh_moments for the Markovian fam...
JoinStrategy
Join rules, with the values of MATLAB JoinStrategy.
RoutingStrategy
Routing strategies, with the values of MATLAB RoutingStrategy.
DepartureDiscipline
When a Place releases a served token, MATLAB DepartureDiscipline.
RemovalPolicy
Which job a negative signal removes, with the values of MATLAB RemovalPolicy.
@ 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)
std::function< std::vector< T >(const std::vector< T > &)> GdScaling
A globally state-dependent scaling, sn.gdscaling.
std::function< std::vector< T >(const std::vector< T > &)> CdScaling
A class-dependent scaling map, sn.cdscaling.
void dist_refresh_moments(Distrib< T > &d)
Fill in the first two moments of a distribution given by its matrices.
NodeType
Node kinds, with the values of MATLAB NodeType.
ImpatienceType
Impatience kinds, with the values of MATLAB ImpatienceType.
SdrCoeff pfqn_sdrcoeff(const SdrStruct &sdr)
Validates an SDR structure and returns its derived coefficients.
Conservation laws of a layered queueing network, enumerated from its structure.
A queueing network and its refreshed NetworkStruct.
Number-type abstraction for the templated API port.
Prior: parameter uncertainty as a weighted set of alternative models.
Post-MMT node visits of a fork-join model.
Topology and coefficients of a state-dependent routing subnetwork.
std::vector< std::size_t > departureOf
departureOf[b] is the departure centre d(b) of branch b.
std::vector< std::size_t > entryOf
entryOf[b] is the entry centre e(b) of branch b.
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).
Server breakdown and repair of a station whose server fails and is repaired.
T failure_rate
sn.breakdownMu: 1 / mean failure time
lang::Distrib< T > repair
time to repair of a down server
lang::Distrib< T > failure
time to failure of an up server
T repair_rate
sn.repairMu: 1 / mean repair time
std::vector< T > down_service_rates
sn.downServiceRates(ist, :): per class, 0 = no service while down.
std::vector< Popularity > preadkind
per class, parallel to pread
std::map< std::size_t, std::vector< std::size_t > > retrieval_queues
read class(0-based)->nodes
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
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
static Distrib exp_rate(const T &r)
static Distrib disabled_dist()
static Distrib immediate()
The Immediate singleton.
One job class of the network.
std::size_t refstat
1-based reference station
double population
infinite for an open class
The DECLARED join rule of a Join node, by 1-based node index.
lang::JoinStrategy strategy
double quorum
0 = every sibling
The G-network signal declaration, per CLASS.
std::function< T(const std::vector< std::size_t > &)> svc_rate_fun
std::vector< std::vector< bool > > swap_graph
The polling controller of a POLLING station, keyed by station index.
std::size_t pk
the K of K-LIMITED
std::vector< lang::Distrib< T > > switchover
FINITE CAPACITY REGIONS, MATLAB's refreshRegions output.
std::vector< std::vector< double > > cap
(nstations x nclasses+1), -1 = unbounded
std::string name
The region's declared name, as the wire carries it; a generated one otherwise.
std::vector< double > maxmem
per member station, -1 = unbounded
std::vector< DropStrategy > rule
per class
std::vector< T > size
per class; size is the memory footprint
std::vector< bool > members
membership, independent of the caps
sn.reward: the user-declared reward functions, MATLAB's model.setReward(name, fn).
std::function< T(const std::vector< T > &)> fn
std::string kind
The DECLARATIVE form the reward was built from, when it was: the template name (QLen,...
std::vector< RoutingStrategy > routing
sn.routing, per class.
The parameters of a retrial station: MATLAB sn.retrialProc and friends.
std::vector< int > max_attempts
0 = unbounded
std::vector< T > retrial_rate
mu_r, the per-class orbit retry rate
std::vector< lang::Distrib< T > > retrial_proc
retrial_proc[r] is the class-r retrial process; empty = not a retrial class.
Key(std::size_t r_, std::size_t s_, std::size_t i_, std::size_t j_)
bool operator<(const Key &o) const
Setup and delay-off of a station that powers down when it falls idle.
std::vector< lang::Distrib< T > > setup
per class, disabled = not declared
std::vector< lang::Distrib< T > > delayoff
per class
Per class; strategy == NONE is a class that declares no balking.
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...
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...
std::vector< T > jdscalingpeak
sn.jdscalingpeak for this station: the declared peak joint-dependent scaling per class.
std::vector< BalkingParam > balking
std::function< T(const std::vector< std::size_t > &)> svc_rate_fun
sn.nodeparam{ind}.svcRateFun for a PAS / OI station: the TOTAL service rate as a function of the orde...
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 ...
Matrix< T > swap_graph
sn.nodeparam{ind}.swapGraph: which class a departing job promotes the jobs behind it into.
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< lang::ImpatienceType > impatience
std::vector< T > schedparam
sn.schedparam, per class: the DPS / GPS weight, or the SEPT / LEPT rank.
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
std::vector< bool > immfeed
Node-level immediate feedback, per class; empty when the station sets none.
The parameters of a Cache node, MATLAB's sn.nodeparam{ind} for a Cache.
std::vector< double > firingprio
firing priority per mode
std::vector< lang::TimingStrategy > timing
immediate or timed
std::vector< std::string > modenames
std::vector< lang::Distrib< T > > firingproc
firing distribution per mode
std::vector< double > nmodeservers
servers per mode, may be infinite
std::vector< T > fireweight
weight among simultaneously enabled modes
std::vector< Matrix< T > > firing
firing[m](p,r): class-r tokens mode m moves to/from place p when it fires.
std::vector< Matrix< T > > enabling
enabling[m](p,r): class-r tokens of place p (0-based node) mode m needs.
std::vector< std::function< T(const std::vector< T > &)> > firingdep
Marking-dependent firing-rate multiplier g_m(marking); an empty entry is the unit multiplier.
std::vector< Matrix< T > > inhibiting
inhibiting[m](p,r): class-r tokens of p that BLOCK mode m (Inf = never).
std::vector< std::size_t > firingphases
phase count per mode, 0 when non-Markovian