5#ifndef LINE_LANG_QN_NETWORK_BUILDER_H
6#define LINE_LANG_QN_NETWORK_BUILDER_H
65 void set(std::size_t r, std::size_t s, std::size_t i, std::size_t j,
const T& p) {
68 void set(std::size_t i, std::size_t j,
const T& p) {
set(1, 1, i, j, p); }
78 for (
const auto& kv : block.entries)
79 set(cls, cls, kv.first.i, kv.first.j, kv.second);
82 T
get(std::size_t r, std::size_t s, std::size_t i, std::size_t j)
const {
89 Key(std::size_t r_, std::size_t s_, std::size_t i_, std::size_t j_)
90 :
r(r_),
s(s_),
i(i_),
j(j_) {}
92 if (
r != o.
r)
return r < o.
r;
93 if (
s != o.
s)
return s < o.
s;
94 if (
i != o.
i)
return i < o.
i;
113 explicit Network(
const std::string& nm) { sn_.name = nm; }
135 st.
nodetype = (sched == SchedStrategy::INF) ? NodeType::Delay : NodeType::Queue;
137 st.
nservers = (sched == SchedStrategy::INF)
138 ? std::numeric_limits<double>::infinity()
140 const std::size_t ist = sn_.add_station(st);
141 sn_.nodes[sn_.station_to_node[ist - 1] - 1].queue_object =
true;
142 init_node(sn_.station_to_node[ist - 1]);
143 return sn_.station_to_node[ist - 1];
151 st.
sched = SchedStrategy::INF;
152 st.
nservers = std::numeric_limits<double>::infinity();
153 const std::size_t ist = sn_.add_station(st);
154 init_node(sn_.station_to_node[ist - 1]);
155 return sn_.station_to_node[ist - 1];
165 if (sn_.sourceIdx != 0)
166 throw InputError(
ErrorKind{
"Model"},
"Network '" + sn_.
name +
"' already has a Source; a network holds at most one. "
167 "Give every open class its arrival process on that Source.");
170 st.nodetype = NodeType::Source;
171 st.sched = SchedStrategy::EXT;
173 const std::size_t ist = sn_.add_station(st);
175 init_node(sn_.station_to_node[ist - 1]);
176 return sn_.station_to_node[ist - 1];
181 if (sn_.sinkNode != 0)
throw InputError(
ErrorKind{
"Model"},
"Network '" + sn_.
name +
"' already has a Sink; a network holds at most one. Route every open class to that Sink.");
182 const std::size_t nd = sn_.add_node(nm, NodeType::Sink,
false);
190 const std::size_t nd = sn_.add_node(nm, NodeType::Router,
false);
204 std::size_t
add_logger(
const std::string& nm,
const std::string& log_file = std::string()) {
205 const std::size_t nd = sn_.add_node(nm, NodeType::Logger,
false);
209 std::string base = log_file;
210 const std::size_t slash = base.find_last_of(
'/');
211 if (slash != std::string::npos) base = base.substr(slash + 1);
212 sn_.nodes[nd - 1].logger.file_name = base;
226 if (C.
rows() != sn_.classes.size() || C.
cols() != sn_.classes.size())
228 "': the matrix must be (nclasses x nclasses); declare the classes "
230 const std::size_t nd = sn_.add_node(nm, NodeType::ClassSwitch,
false);
231 sn_.csmatrix[nd] = C;
252 const std::size_t nd = sn_.add_node(nm, NodeType::ClassSwitch,
false);
260 if (node == 0 || node > sn_.nodes.size())
261 throw InputError(
"set_class_switch_matrix: node index is out of range");
262 if (sn_.nodes[node - 1].nodetype != NodeType::ClassSwitch)
263 throw InputError(
"set_class_switch_matrix: node '" + sn_.nodes[node - 1].name +
264 "' is not a ClassSwitch");
265 if (C.
rows() != sn_.classes.size() || C.
cols() != sn_.classes.size())
266 throw InputError(
"set_class_switch_matrix: the matrix of '" +
267 sn_.nodes[node - 1].name +
268 "' must be (nclasses x nclasses)");
269 sn_.csmatrix[node] = C;
274 std::size_t
add_fork(
const std::string& nm,
double tasks_per_link = 1.0) {
275 const std::size_t nd = sn_.add_node(nm, NodeType::Fork,
false);
276 sn_.nodes[nd - 1].tasks_per_link = tasks_per_link;
303 double tasks, std::size_t dest_node = 0) {
305 ov.kind = ForkOverride::TASKS;
310 record_fork_override(ov);
316 std::size_t dest_node = 0) {
318 throw InputError(
"set_fork_tasks_per_link_dist: the jobs-per-link "
319 "distribution must be a DiscreteSampler");
321 ov.kind = ForkOverride::DIST;
326 record_fork_override(ov);
331 std::size_t dest_node,
double prob) {
332 if (prob < 0.0 || prob > 1.0)
333 throw InputError(
"set_fork_branch_probability: a branch activation "
334 "probability must lie in [0,1]");
336 ov.kind = ForkOverride::PROB;
341 record_fork_override(ov);
348 std::size_t
add_join(
const std::string& nm, std::size_t fork_node) {
366 st.
sched = SchedStrategy::INF;
367 st.
nservers = std::numeric_limits<double>::infinity();
368 const std::size_t ist = sn_.add_station(st);
369 const std::size_t nd = sn_.station_to_node[ist - 1];
375 void bind_join(std::size_t join_node, std::size_t fork_node) {
376 if (join_node == 0 || join_node > sn_.nodes.size() ||
377 sn_.nodes[join_node - 1].nodetype != NodeType::Join)
378 throw InputError(
"bind_join: the node being bound is not a Join");
379 if (fork_node == 0 || fork_node > sn_.nodes.size() ||
380 sn_.nodes[fork_node - 1].nodetype != NodeType::Fork)
381 throw InputError(
"Join '" + sn_.nodes[join_node - 1].name +
382 "': the node it closes is not a Fork");
383 sn_.fj.emplace_back(fork_node, join_node);
420 st.
nservers = (sched == SchedStrategy::INF)
421 ? std::numeric_limits<double>::infinity()
423 const std::size_t ist = sn_.add_station(st);
424 init_node(sn_.station_to_node[ist - 1]);
425 return sn_.station_to_node[ist - 1];
439 throw InputError(
"add_transition: a transition needs at least one mode");
442 "add_transition: enabling and firing must have one entry per mode");
443 const std::size_t nd = sn_.add_node(nm, NodeType::Transition,
true);
444 sn_.transparam[nd] = par;
463 const std::size_t nd = sn_.add_node(nm, NodeType::Transition,
true);
476 std::size_t
add_mode(std::size_t node,
const std::string& nm) {
485 tp.
firingdep.push_back(std::function<T(
const std::vector<T>&)>());
502 mode_param(node, mode,
"set_mode_distribution").firingproc[mode - 1] = d;
507 mode_param(node, mode,
"set_mode_timing").timing[mode - 1] = ts;
512 mode_param(node, mode,
"set_mode_servers").nmodeservers[mode - 1] = n;
517 mode_param(node, mode,
"set_firing_priority").firingprio[mode - 1] = prio;
522 mode_param(node, mode,
"set_firing_weight").fireweight[mode - 1] = w;
527 const std::function<T(
const std::vector<T>&)>& g) {
528 mode_param(node, mode,
"set_mode_firing_dependence").firingdep[mode - 1] = g;
537 fluid_place_arg(place,
"set_mode_fluid_rate");
538 if (!std::isfinite(rate))
539 throw InputError(
"set_mode_fluid_rate: the fluid rate must be a finite scalar");
540 set_fluid_row(tp.
fluidrate[mode - 1], place, rate);
548 const std::string& op,
double threshold) {
550 fluid_place_arg(place,
"set_mode_fluid_guard");
551 std::vector<typename TransitionParam<T>::FluidGuard>& g = tp.
fluidguard[mode - 1];
552 for (std::size_t k = g.size(); k-- > 0;)
553 if (g[k].node == place) g.erase(g.begin() +
static_cast<std::ptrdiff_t
>(k));
554 if (op.empty())
return;
556 if (op ==
">=") sgn = 1;
557 else if (op ==
"<=") sgn = -1;
558 else throw InputError(
"set_mode_fluid_guard: the fluid guard operator must be '>=' or '<='");
559 if (!std::isfinite(threshold) || threshold < 0.0)
561 "set_mode_fluid_guard: the fluid guard threshold must be a finite non-negative scalar");
575 fluid_place_arg(place,
"set_mode_fluid_impulse");
576 if (!std::isfinite(delta))
577 throw InputError(
"set_mode_fluid_impulse: the fluid impulse must be a finite scalar");
587 const std::function<
double(
double)>& h) {
589 fluid_place_arg(place,
"set_mode_fluid_dependence");
590 std::vector<typename TransitionParam<T>::FluidDep>& d = tp.
fluiddep[mode - 1];
591 for (std::size_t k = d.size(); k-- > 0;)
592 if (d[k].node == place) d.erase(d.begin() +
static_cast<std::ptrdiff_t
>(k));
596 "set_mode_fluid_dependence: fluid rate dependence is not supported for IMMEDIATE modes");
599 "set_mode_fluid_dependence: fluid rate dependence is supported only for exponentially "
600 "distributed (memoryless) firing; set an Exp distribution on the mode first");
614 const std::function<
double(
const std::vector<double>&)>& phi,
615 const std::vector<std::size_t>& read = std::vector<std::size_t>()) {
617 fluid_place_arg(place,
"set_mode_fluid_flow_dependence");
618 std::vector<typename TransitionParam<T>::FluidFlowDep>& d = tp.
fluidflowdep[mode - 1];
619 for (std::size_t k = d.size(); k-- > 0;)
620 if (d[k].node == place) d.erase(d.begin() +
static_cast<std::ptrdiff_t
>(k));
625 for (std::size_t q : read) {
626 if (q == 0 || q > sn_.nodes.size() || sn_.nodes[q - 1].nodetype != NodeType::Place)
627 throw InputError(
"set_mode_fluid_flow_dependence: the places a fluid flow dependence reads must be "
629 fd.
read.push_back(q);
631 std::sort(fd.
read.begin(), fd.
read.end());
646 std::size_t place,
const T& tokens) {
647 arc_target(node, mode, cls, place,
true,
"set_enabling_conditions");
648 pending_arcs_[node].enab.push_back(TransitionArc(mode, place, cls, tokens));
657 std::size_t place,
const T& tokens) {
658 arc_target(node, mode, cls, place,
true,
"set_inhibiting_conditions");
659 pending_arcs_[node].inhib.push_back(TransitionArc(mode, place, cls, tokens));
668 std::size_t dest,
const T& tokens) {
669 arc_target(node, mode, cls, dest,
false,
"set_firing_outcome");
670 pending_arcs_[node].fire.push_back(TransitionArc(mode, dest, cls, tokens));
680 int max_attempts = 0) {
681 if (node == 0 || node > sn_.nodes.size())
682 throw InputError(
"set_retrial: node index is out of range");
683 const std::size_t ist = sn_.nodes[node - 1].station;
684 if (ist == 0)
throw InputError(
"set_retrial: node is not a station");
689 const std::size_t K = sn_.classes.size();
690 if (cls == 0 || cls > K)
691 throw InputError(
"set_retrial: class index is out of range");
705 set_drop_rule(node, cls, max_attempts > 0 ? DropStrategy::RETRIAL_WITH_LIMIT
706 : DropStrategy::RETRIAL);
718 if (node == 0 || node > sn_.nodes.size())
719 throw InputError(
"set_orbit_policy: node index is out of range");
720 const std::size_t ist = sn_.nodes[node - 1].station;
721 typename std::map<std::size_t, RetrialParam<T> >::iterator it = sn_.retrialparam.find(ist);
722 if (ist == 0 || it == sn_.retrialparam.end() || cls == 0 ||
723 cls > it->second.retrial_proc.size() || it->second.retrial_proc[cls - 1].disabled)
724 throw InputError(
"set_orbit_policy: the class has no retrial declared at this station");
725 if (policy != 1 && policy != 2)
726 throw InputError(
"set_orbit_policy: the policy must be 1 (LINEAR) or 2 (CONSTANT)");
728 throw InputError(
"set_orbit_policy: max_orbit must be -1 (unbounded) or non-negative");
729 it->second.policy[cls - 1] = policy;
730 it->second.max_orbit[cls - 1] = max_orbit;
739 Station<T>& st = station_ref(node, cls,
"set_patience");
750 Station<T>& st = station_ref(node, cls,
"set_orbit_impatience");
757 Station<T>& st = station_ref(node, cls,
"set_batch_reject");
769 Station<T>& st = station_ref(node, cls,
"set_balking");
771 st.
balking[cls - 1].strategy = strategy;
772 st.
balking[cls - 1].thresholds = thresholds;
777 const std::size_t ist = station_of(node,
"add_server_type");
778 sn_.stations[ist - 1].server_types.push_back(stype);
781 for (
const auto& pool : sn_.stations[ist - 1].server_types) total += pool.count;
782 sn_.stations[ist - 1].nservers = total;
791 Station<T>& st = station_ref(node, cls,
"set_server_parallelism");
793 throw InputError(
"set_server_parallelism: parallelism must be a positive integer");
797 "set_server_parallelism: parallelism " + std::to_string(n) +
" exceeds the " +
798 std::to_string(
static_cast<long long>(st.
nservers)) +
" servers of station '" +
799 sn_.nodes[node - 1].name +
"', so a job of this class could never enter service");
807 sn_.stations[station_of(node,
"set_hetero_sched_policy") - 1].hetero_policy = policy;
815 Station<T>& st = station_ref(node, cls,
"set_arrival_batch");
819 bool zero_in_support =
false;
824 zero_in_support =
true;
827 zero_in_support = !dist.
params.empty() && dist.
params[0] < one;
830 for (
const T& x : dist.
trace) zero_in_support = zero_in_support || x < one;
836 throw InputError(
"set_arrival_batch: the batch-size law can return a value below one; a batch must carry "
837 "at least one job, so its support must be {1,2,...}");
839 throw InputError(
"set_arrival_batch: the batch-size law has mean below one; a batch must carry at least "
840 "one job, so its support must be {1,2,...}");
848 sn_.stations[station_of(node,
"set_marked_classes") - 1].marked_classes = classes;
854 Station<T>& st = station_ref(node, cls,
"set_departure_discipline");
861 if (node == 0 || node > sn_.nodes.size())
862 throw InputError(
"set_initial_marking: node index is out of range");
863 if (sn_.nodes[node - 1].nodetype != NodeType::Place)
864 throw InputError(
"set_initial_marking: only a Place carries an initial marking");
865 sn_.initmarking[node] = tokens;
874 place_station(node,
"set_place_fluid").fluid =
fluid;
879 if (std::isnan(cap) || cap <= 0.0)
880 throw InputError(
"set_fluid_capacity: the fluid capacity must be a positive scalar, or Inf "
881 "for an unbounded place");
882 place_station(node,
"set_fluid_capacity").fluidcap = cap;
887 if (!std::isfinite(level) || level < 0.0)
888 throw InputError(
"set_fluid_level: the initial fluid level must be a finite non-negative scalar");
889 place_station(node,
"set_fluid_level").fluidinit = level;
893 bool is_place_fluid(std::size_t node) {
return place_station(node,
"is_place_fluid").fluid; }
895 return place_station(node,
"get_fluid_capacity").fluidcap;
897 double get_fluid_level(std::size_t node) {
return place_station(node,
"get_fluid_level").fluidinit; }
901 const auto it = sn_.transparam.find(node);
902 return it != sn_.transparam.end() && it->second.has_fluid_arcs();
911 if (node == 0 || node > sn_.nodes.size())
912 throw InputError(
"set_state_prior: node index is out of range");
913 if (space.
rows() != prior.size())
915 "set_state_prior: the prior has one entry per ROW of the declared state space");
916 sn_.statespace[node] = space;
917 sn_.stateprior[node] = prior;
922 if (node == 0 || node > sn_.nodes.size())
923 throw InputError(
"set_join_strategy: node index is out of range");
924 if (sn_.nodes[node - 1].nodetype != NodeType::Join)
925 throw InputError(
"set_join_strategy: the node is not a Join");
929 sn_.joindecl[node] = jd;
934 const std::map<std::size_t, double>& weights) {
935 if (node == 0 || node > sn_.nodes.size())
936 throw InputError(
"set_routing_weights: node index is out of range");
937 std::vector<std::map<std::size_t, double>>& rw = sn_.nodes[node - 1].routing_weights;
938 if (rw.size() < sn_.classes.size()) rw.resize(sn_.classes.size());
939 if (cls == 0 || cls > rw.size())
940 throw InputError(
"set_routing_weights: class index is out of range");
941 rw[cls - 1] = weights;
946 if (node == 0 || node > sn_.nodes.size())
947 throw InputError(
"set_routing_param: node index is out of range");
948 std::vector<int>& rp = sn_.nodes[node - 1].routing_param;
949 if (rp.size() < sn_.classes.size()) rp.resize(sn_.classes.size(), 0);
950 if (cls == 0 || cls > rp.size())
951 throw InputError(
"set_routing_param: class index is out of range");
963 const std::size_t ist = station_of(node,
"set_setup_delayoff");
964 const std::size_t K = sn_.classes.size();
965 if (cls == 0 || cls > K)
966 throw InputError(
"set_setup_delayoff: class index is out of range");
968 if (sp.
setup.size() < K) {
972 sp.
setup[cls - 1] = setup;
992 const std::size_t ist = station_of(node,
"set_breakdown");
995 "set_breakdown: both a failure and a repair time are required; a server that "
996 "never recovers is an absorbing model, not a breakdown");
999 if (!(fm > 0.0) || !(rm > 0.0))
1000 throw InputError(
"set_breakdown: the failure and repair times must have positive means");
1001 const std::size_t K = sn_.classes.size();
1008 for (std::size_t r = 0; r < K && r < down_service.size(); ++r) {
1013 "set_breakdown: station '" + sn_.stations[ist - 1].
name +
1014 "': the down-server service distribution must be exponential; a phase-type "
1015 "degraded service would need its own phase block in the joint chain");
1023 const std::size_t nd = sn_.add_node(nm, NodeType::Cache,
true);
1024 sn_.nodeparam[nd] = par;
1040 const std::vector<std::size_t>& read_classes,
1041 const std::vector<std::size_t>& hit_classes) {
1042 auto it = sn_.nodeparam.find(cache_node);
1043 if (it == sn_.nodeparam.end())
1044 throw InputError(
"setItemReadClasses: node is not a Cache");
1046 const std::size_t nitems = cp.
nitems;
1047 if (read_classes.size() != nitems)
1048 throw InputError(
"setItemReadClasses: pass exactly one read class per item");
1049 const std::vector<std::size_t> hit =
1050 per_item_classes(hit_classes, nitems,
"setItemReadClasses hit");
1051 const std::size_t K = sn_.classes.size();
1057 for (std::size_t i = 0; i < nitems; ++i) {
1060 cp.
pread[read_classes[i] - 1] = onehot;
1061 cp.
hitclass[read_classes[i] - 1] = hit[i];
1062 cp.
classitem[read_classes[i] - 1] = i + 1;
1064 cache_item_classes_[cache_node] = read_classes;
1072 void set_cache_read(std::size_t cache_node, std::size_t r,
const std::vector<T>& pmf) {
1073 CacheParam<T>& cp = cache_param_for_class(cache_node, r,
"set_read");
1074 if (pmf.size() != cp.
nitems)
1075 throw InputError(
"set_read: the popularity has " + std::to_string(pmf.size()) +
1076 " entries and the cache '" + sn_.nodes[cache_node - 1].name +
"' " +
1077 std::to_string(cp.
nitems) +
" items");
1078 cp.
pread[r - 1] = pmf;
1093 for (std::size_t k = 1; k <= n; ++k) h += std::pow(
double(k), -sv);
1094 for (std::size_t k = 1; k <= n; ++k)
1102 kind.
n = pmf.size();
1104 throw InputError(
"set_read: an item popularity is a Zipf or a DiscreteSampler over the item ranks");
1107 cache_param_for_class(cache_node, r,
"set_read").preadkind[r - 1] = kind;
1112 CacheParam<T>& cp = cache_param_for_class(cache_node, r,
"set_hit_class");
1113 check_class(h,
"set_hit_class");
1119 CacheParam<T>& cp = cache_param_for_class(cache_node, r,
"set_miss_class");
1120 check_class(m,
"set_miss_class");
1131 std::vector<std::size_t>
set_miss_cache(std::size_t cache_node, std::size_t next_cache,
1132 const std::vector<std::size_t>& hit_classes_at_next) {
1133 auto it = sn_.nodeparam.find(cache_node);
1134 auto itn = sn_.nodeparam.find(next_cache);
1135 if (it == sn_.nodeparam.end() || itn == sn_.nodeparam.end())
1136 throw InputError(
"setMissCache: both nodes must be Caches");
1137 if (it->second.nitems != itn->second.nitems)
1138 throw InputError(
"setMissCache: a cache network requires one common item set");
1139 auto self_it = cache_item_classes_.find(cache_node);
1140 if (self_it == cache_item_classes_.end())
1141 throw InputError(
"setMissCache: call setItemReadClasses on the source cache first");
1142 const std::vector<std::size_t> self_cls = self_it->second;
1143 const std::size_t nitems = it->second.nitems;
1144 const std::vector<std::size_t> hit =
1145 per_item_classes(hit_classes_at_next, nitems,
"setMissCache hit");
1147 std::vector<std::size_t> minted;
1148 minted.reserve(nitems);
1149 const bool closed = sn_.classes[self_cls[0] - 1].type == JobClassType::CLOSED;
1150 const std::size_t refstat = sn_.classes[self_cls[0] - 1].refstat;
1151 for (std::size_t i = 0; i < nitems; ++i) {
1154 cls =
add_closed_class(sn_.nodes[next_cache - 1].name +
"_item" + std::to_string(i + 1),
1155 0.0, sn_.station_to_node[refstat - 1]);
1157 cls =
add_open_class(sn_.nodes[next_cache - 1].name +
"_item" + std::to_string(i + 1));
1158 minted.push_back(cls);
1161 const std::size_t K = sn_.classes.size();
1162 for (std::size_t nd : {cache_node, next_cache}) {
1172 for (std::size_t i = 0; i < nitems; ++i) {
1175 dst.
pread[minted[i] - 1] = onehot;
1176 dst.
hitclass[minted[i] - 1] = hit[i];
1178 src.
missclass[self_cls[i] - 1] = minted[i];
1182 cache_miss_arcs_.push_back(CacheMissArc(minted[i], cache_node, next_cache));
1184 cache_item_classes_[next_cache] = minted;
1194 auto it = sn_.nodeparam.find(cache_node);
1195 if (it == sn_.nodeparam.end())
1196 throw InputError(
"setItemMissClass: node is not a Cache");
1197 auto self_it = cache_item_classes_.find(cache_node);
1198 if (self_it == cache_item_classes_.end())
1199 throw InputError(
"setItemMissClass: the cache has no per-item classes");
1200 const std::vector<std::size_t>& self_cls = self_it->second;
1201 const std::vector<std::size_t> miss =
1202 per_item_classes(miss_classes, self_cls.size(),
"setItemMissClass miss");
1204 cp.
missclass.resize(sn_.classes.size(), 0);
1205 for (std::size_t i = 0; i < self_cls.size(); ++i)
1206 cp.
missclass[self_cls[i] - 1] = miss[i];
1221 std::size_t miss_class,
const std::vector<std::size_t>& queue_nodes) {
1222 auto it = sn_.nodeparam.find(cache_node);
1223 if (it == sn_.nodeparam.end())
1224 throw InputError(
"setRetrievalSystem: node is not a Cache");
1226 if (queue_nodes.empty())
1227 throw InputError(
"setRetrievalSystem: the retrieval system has no stations");
1228 const std::size_t nitems = cp.
nitems;
1230 for (
int c : cp.
itemcap) totalcap += c;
1236 std::vector<Distrib<T> > svc;
1237 svc.reserve(queue_nodes.size());
1238 for (std::size_t q : queue_nodes) {
1239 const std::size_t st = station_of(q,
"setRetrievalSystem");
1240 const Distrib<T>& d = sn_.service[st - 1][read_class - 1];
1242 throw InputError(
"No service distribution for the read class at queue \"" +
1243 sn_.nodes[q - 1].name +
1244 "\"; call queue.setService(readClass, ...) before setRetrievalSystem.");
1248 cp.
retrieval_classes.assign(nitems, std::vector<std::size_t>(sn_.classes.size(), 0));
1249 const bool closed = sn_.classes[read_class - 1].type == JobClassType::CLOSED;
1250 const std::size_t refstat = sn_.classes[read_class - 1].refstat;
1251 for (std::size_t i = 0; i < nitems; ++i) {
1254 rc =
add_closed_class(sn_.classes[read_class - 1].name +
"_retrievalClass_" +
1255 std::to_string(i + 1),
1256 0.0, sn_.station_to_node[refstat - 1]);
1258 rc =
add_open_class(sn_.classes[read_class - 1].name +
"_retrievalClass_" +
1259 std::to_string(i + 1));
1260 for (std::size_t s = 0; s < queue_nodes.size(); ++s) {
1266 for (std::size_t k = 0; k < nitems; ++k)
1273 const std::size_t K = sn_.classes.size();
1280 cp.
pread[rc - 1] = onehot;
1287 std::size_t queue_node) {
1288 set_retrieval_system(cache_node, read_class, miss_class, std::vector<std::size_t>(1, queue_node));
1297 std::size_t item,
const T& rate) {
1307 std::size_t source_node, std::size_t dest_node,
const T& p) {
1308 const std::size_t rc = retrieval_class_of(cache_node, read_class, item);
1310 retrieval_routing_entries_.push_back(RetrievalRoutingEntry(rc, source_node, dest_node, p));
1315 std::size_t source_node, std::size_t dest_node,
const T& p) {
1326 if (!(njobs >= 0.0) || std::isinf(njobs))
1327 throw InputError(
"ClosedClass '" + nm +
"': the population must be finite");
1330 cl.
type = JobClassType::CLOSED;
1332 cl.
refstat = station_of(refstat_node,
"ClosedClass '" + nm +
"'");
1334 const std::size_t r = sn_.add_class(cl);
1335 grow_class_vectors();
1344 if (sn_.sourceIdx == 0)
1346 "': the model has no Source to reference; add one first");
1349 cl.
type = JobClassType::OPEN;
1350 cl.
population = std::numeric_limits<double>::infinity();
1353 const std::size_t r = sn_.add_class(cl);
1354 grow_class_vectors();
1367 std::size_t refstat_node,
int prio = 0) {
1369 sn_.classes[r - 1].self_looping =
true;
1375 class_ref(cls,
"set_reference_class").is_ref_class =
true;
1380 class_ref(cls,
"set_class_deadline").deadline = due;
1388 const std::size_t K = sn_.classes.size();
1389 if (spawn_cls == 0 || spawn_cls > K)
1390 throw InputError(
"set_class_spawn: the spawned class index is out of range");
1391 class_ref(cls,
"set_class_spawn").spawn = spawn_cls;
1405 const std::size_t K = sn_.classes.size();
1406 if (cls == 0 || cls > K)
throw InputError(
"set_class_patience: class index out of range");
1409 for (std::size_t ist = 0; ist < sn_.stations.size(); ++ist) {
1410 const std::size_t ind = sn_.station_to_node[ist];
1416 if (!st.
patience[cls - 1].disabled)
continue;
1434 const std::size_t K = sn_.classes.size();
1435 if (call_cls == 0 || call_cls > K || reply_cls == 0 || reply_cls > K)
1436 throw InputError(
"set_reply_signal_class: class index is out of range");
1438 if (sn_.syncreply.size() < K) sn_.syncreply.resize(K, 0);
1439 sn_.syncreply[call_cls - 1] = reply_cls;
1461 sn_.set_service(station_of(node,
"setService"), cls, dd);
1466 const std::size_t ist = station_of(node,
"setArrival");
1467 if (sn_.stations[ist - 1].nodetype != NodeType::Source)
1468 throw InputError(
"setArrival: node '" + sn_.nodes[node - 1].name +
"' is not a Source");
1474 sn_.set_service(ist, cls, dd);
1488 const std::size_t ist = station_of(node,
"setNumberOfServers");
1489 if (sn_.stations[ist - 1].sched == SchedStrategy::INF)
return;
1492 if (!(n >= 0.0))
throw InputError(
"setNumberOfServers: the server count must be >= 0");
1493 sn_.stations[ist - 1].nservers = n;
1496 if (std::isinf(n) && sn_.stations[ist - 1].nodetype == NodeType::Queue)
1497 sn_.stations[ist - 1].nodetype = NodeType::Delay;
1502 sn_.stations[station_of(node,
"setCapacity") - 1].cap = k;
1507 Station<T>& st = sn_.stations[station_of(node,
"setChainCapacity") - 1];
1508 st.
classcap.resize(sn_.classes.size(), std::numeric_limits<double>::infinity());
1521 Station<T>& st = sn_.stations[station_of(node,
"setImmediateFeedback") - 1];
1522 st.
immfeed.resize(sn_.classes.size(),
false);
1528 sn_.classes[cls - 1].immfeed =
true;
1533 Station<T>& st = sn_.stations[station_of(node,
"setDropRule") - 1];
1534 st.
droprule.resize(sn_.classes.size(), 0);
1535 st.
droprule[cls - 1] =
static_cast<int>(rule);
1554 std::size_t node,
const std::function<T(
const std::vector<std::size_t>&)>& muFun,
1556 const std::size_t ist = station_of(node,
"setServiceRateFunction");
1558 if (st.
sched != SchedStrategy::PAS && st.
sched != SchedStrategy::OI)
1560 "setServiceRateFunction is only applicable to PAS (pass-and-swap) and OI "
1561 "(order-independent) queues");
1562 if (!muFun)
throw InputError(
"setServiceRateFunction: the rate function is empty");
1573 for (std::size_t r = 1; r <= sn_.classes.size(); ++r) {
1574 const T rate_r = muFun(std::vector<std::size_t>{r});
1587 Station<T>& st = sn_.stations[station_of(node,
"setPollingType") - 1];
1588 if (st.
sched != SchedStrategy::POLLING)
1589 throw InputError(
"setPollingType is only applicable to a POLLING station");
1591 throw InputError(
"K-limited polling requires a parameter K >= 1");
1598 Station<T>& st = sn_.stations[station_of(node,
"setSwitchover") - 1];
1599 if (st.
sched != SchedStrategy::POLLING)
1600 throw InputError(
"setSwitchover is only applicable to a POLLING station");
1622 Station<T>& st = sn_.stations[station_of(node,
"setSwitchover") - 1];
1623 if (st.
sched == SchedStrategy::POLLING)
1625 "setSwitchover(fromClass, toClass, distrib) is not applicable to a POLLING "
1626 "station, whose switchover is the walk out of one buffer and is declared per "
1628 const std::size_t K = sn_.classes.size();
1629 if (from_cls == 0 || from_cls > K || to_cls == 0 || to_cls > K)
1630 throw InputError(
"setSwitchover: class index is out of range");
1638 Station<T>& st = sn_.stations[station_of(node,
"setSchedParam") - 1];
1649 if (alpha.empty())
throw InputError(
"setLoadDependence: the scaling vector is empty");
1650 sn_.stations[station_of(node,
"setLoadDependence") - 1].lldscaling = alpha;
1671 const std::vector<T>& peak = std::vector<T>()) {
1672 Station<T>& st = sn_.stations[station_of(node,
"setClassDependence") - 1];
1673 const std::size_t K = sn_.classes.size();
1677 else if (peak.size() == 1)
1679 else if (peak.size() == K)
1683 "setClassDependence: peakRatePerClass must be a scalar or a vector of length "
1697 const std::vector<T>& peak) {
1698 Station<T>& st = sn_.stations[station_of(node,
"setJointDependence") - 1];
1699 const std::size_t K = sn_.classes.size();
1702 "setJointDependence: joint dependence requires an explicit peak rate; pass a "
1703 "scalar (identical peak for every class) or a per-class vector");
1705 if (peak.size() == 1)
1707 else if (peak.size() == K)
1711 "setJointDependence: peakRatePerClass must be a scalar or a vector of length "
1745 throw InputError(
"setGlobalDependence: the scaling must be a callable");
1746 const std::size_t M = sn_.stations.size(), K = sn_.classes.size();
1749 "setGlobalDependence: a global dependence requires an explicit peak rate; pass a "
1750 "scalar, one entry per station, or one entry per (station, class)");
1751 for (std::size_t i = 0; i < peak.size(); ++i)
1753 throw InputError(
"setGlobalDependence: peak must be positive");
1756 for (
int probe = 0; probe < 2; ++probe) {
1758 const std::vector<T> v = fun(n);
1759 if (v.size() != 1 && v.size() != M && v.size() != M * K)
1761 "setGlobalDependence: the handle must return a scalar, one entry per station, "
1762 "or one entry per (station, class)");
1763 for (std::size_t j = 0; j < v.size(); ++j)
1766 "setGlobalDependence: the handle must return finite nonnegative scalings");
1768 if (wire_cutoff < 1)
1769 throw InputError(
"setGlobalDependence: wireCutoff must be a positive integer");
1770 sn_.gdscaling = fun;
1771 sn_.gdscalingcutoff = wire_cutoff;
1772 if (peak.size() == 1)
1773 sn_.gdscalingpeak.assign(M * K, peak[0]);
1774 else if (peak.size() == M) {
1776 for (std::size_t i = 0; i < M; ++i)
1777 for (std::size_t r = 0; r < K; ++r) sn_.gdscalingpeak[i * K + r] = peak[i];
1778 }
else if (peak.size() == M * K)
1779 sn_.gdscalingpeak = peak;
1782 "setGlobalDependence: peak must be a scalar, one entry per station, or one entry "
1783 "per (station, class)");
1788 NodeDef& nd = sn_.nodes[node - 1];
1789 nd.
routing.resize(sn_.classes.size(), RoutingStrategy::PROB);
1820 const std::vector<std::vector<std::size_t>>& branches,
1821 const std::vector<std::size_t>& level,
1823 std::size_t cls = 0) {
1824 if (branches.size() < 2 || !branches[0].empty())
1825 throw InputError(
"set_state_dep_routing: branches[0] must be empty, branch index 1 "
1826 "denotes the complement M-V");
1827 const std::size_t B = branches.size();
1828 if (level.size() != B)
1829 throw InputError(
"set_state_dep_routing: level must have one entry per branch index, "
1830 "including the unused index 0");
1832 nodesdr.
entry = entry - 1;
1834 nodesdr.
branch.assign(B, std::vector<std::size_t>());
1837 for (std::size_t b = 1; b < B; ++b) {
1838 if (branches[b].empty())
1839 throw InputError(
"set_state_dep_routing: branch " + std::to_string(b + 1) +
1841 for (std::size_t k = 0; k < branches[b].size(); ++k)
1842 nodesdr.
branch[b].push_back(branches[b][k] - 1);
1843 nodesdr.
entryOf[b] = branches[b].front() - 1;
1846 nodesdr.
level = level;
1853 std::vector<std::size_t> node_to_station(sn_.nodes.size(), 0);
1854 std::vector<bool> is_station(sn_.nodes.size(),
false);
1855 for (std::size_t k = 0; k < sn_.station_to_node.size(); ++k) {
1856 node_to_station[sn_.station_to_node[k] - 1] = k;
1857 is_station[sn_.station_to_node[k] - 1] =
true;
1860 const std::vector<std::size_t>& n2s;
1861 const std::vector<bool>& isst;
1863 std::size_t operator()(std::size_t nd)
const {
1864 if (nd >= isst.size() || !isst[nd])
1865 throw InputError(
"set_state_dep_routing: node '" +
sn.nodes[nd].name +
1866 "' takes part in state-dependent routing but is not a "
1867 "station: the product form is over queue lengths, and a "
1868 "stateless node holds none");
1871 } to_station{node_to_station, is_station, sn_};
1876 for (std::size_t b = 1; b < B; ++b) {
1877 for (std::size_t k = 0; k < nodesdr.
branch[b].size(); ++k)
1884 sn_.sdr_nodes = nodesdr;
1886 const std::size_t K = sn_.classes.size();
1888 for (std::size_t r = 1; r <= K; ++r)
set_routing(entry, r, RoutingStrategy::SDR);
1909 for (std::size_t k = 0; k < nodes.size(); ++k) {
1910 if (nodes[k] == 0 || nodes[k] > sn_.nodes.size())
1911 throw InputError(
"serialRouting: the path names a node that does not exist");
1914 if (nodes.size() > 1 && sn_.nodes[nodes.back() - 1].nodetype != NodeType::Sink)
1927 for (
const auto& kv : Pm.
entries) {
1928 const auto& k = kv.first;
1929 if (k.r == 0 || k.r > sn_.classes.size() || k.s == 0 || k.s > sn_.classes.size())
1930 throw InputError(
"link: the routing matrix names a class that does not exist");
1931 if (k.i == 0 || k.i > sn_.nodes.size() || k.j == 0 || k.j > sn_.nodes.size())
1932 throw InputError(
"link: the routing matrix names a node that does not exist");
1946 const std::size_t K = sn_.classes.size();
1947 const std::size_t I = sn_.nodes.size();
1953 for (std::size_t a = 0; a < cache_miss_arcs_.size(); ++a) {
1954 const CacheMissArc&
mc = cache_miss_arcs_[a];
1955 P.set(
mc.cls,
mc.cls,
mc.from_node,
mc.to_node, one);
1960 for (std::size_t r = 1; r <= K; ++r) {
1961 if (!sn_.classes[r - 1].self_looping)
continue;
1962 for (
auto it = P.entries.begin(); it != P.entries.end();)
1963 it = it->first.r == r ? P.entries.erase(it) : std::next(it);
1964 const std::size_t ref = sn_.node_of_station(sn_.classes[r - 1].refstat);
1965 P.set(r, r, ref, ref, one);
1967 std::map<std::pair<std::size_t, std::size_t>, std::size_t> csid;
1968 for (std::size_t i = 1; i <= I; ++i)
1969 for (std::size_t j = 1; j <= I; ++j) {
1972 for (std::size_t r = 1; r <= K; ++r)
1973 for (std::size_t s = 1; s <= K; ++s) {
1974 const T p = P.get(r, s, i, j);
1976 C(r - 1, s - 1) = p;
1981 bool offdiag =
false;
1982 for (std::size_t r = 1; r <= K; ++r) {
1984 for (std::size_t s = 1; s <= K; ++s) S += C(r - 1, s - 1);
1986 for (std::size_t s = 1; s <= K; ++s) C(r - 1, s - 1) = T(C(r - 1, s - 1) / S);
1988 C(r - 1, r - 1) = one;
1990 for (std::size_t s = 1; s <= K; ++s)
1993 if (!offdiag)
continue;
2009 if (sn_.nodes[i - 1].nodetype == NodeType::Sink &&
2010 sn_.nodes[j - 1].nodetype == NodeType::Source)
2012 csid[std::make_pair(i, j)] =
2013 add_class_switch(
"CS_" + sn_.nodes[i - 1].name +
"_to_" + sn_.nodes[j - 1].name, C);
2018 for (
const auto& kv : csid) {
2019 const std::size_t i = kv.first.first, j = kv.first.second, c = kv.second;
2020 for (std::size_t r = 1; r <= K; ++r)
2021 for (std::size_t s = 1; s <= K; ++s) {
2022 const T p = P.get(r, s, i, j);
2024 P.set(r, r, i, c, T(P.get(r, r, i, c) + p));
2025 P.set(r, s, i, j, zero);
2026 P.set(s, s, c, j, one);
2029 for (
const auto& kv : P.entries) {
2030 const auto& k = kv.first;
2031 sn_.set_route(k.r, k.s, k.i, k.j, kv.second);
2037 for (
const auto& np : sn_.nodeparam) {
2038 const std::size_t ci = np.first;
2042 const std::size_t rd = rq.first + 1;
2043 std::vector<std::size_t> nodeset = rq.second;
2044 nodeset.push_back(ci);
2045 bool minted =
false;
2046 const std::size_t Inodes = sn_.nodes.size();
2047 for (std::size_t i = 0; i < cp.
nitems; ++i) {
2049 if (rc == 0)
continue;
2055 std::vector<bool> declared(nodeset.size(),
false);
2056 for (std::size_t si = 0; si < nodeset.size(); ++si)
2057 for (std::size_t b = 1; b <= Inodes && !declared[si]; ++b)
2059 declared[si] =
true;
2060 for (std::size_t si = 0; si < nodeset.size(); ++si) {
2061 if (declared[si])
continue;
2062 for (std::size_t b : nodeset) {
2063 const T p = Pm.
get(rd, rd, nodeset[si], b);
2065 sn_.set_route(rc, rc, nodeset[si], b, p);
2081 for (std::size_t a : nodeset)
2082 for (std::size_t b : nodeset)
2088 for (
const RetrievalRoutingEntry& e : retrieval_routing_entries_)
2089 sn_.set_route(e.cls, e.cls, e.from_node, e.to_node, e.p);
2114 const std::size_t Kc = sn_.classes.size(), Ic = sn_.nodes.size();
2115 for (std::size_t i = 1; i <= Ic; ++i) {
2116 NodeDef& nd = sn_.nodes[i - 1];
2117 nd.
routing.resize(Kc, RoutingStrategy::PROB);
2118 bool linked =
false;
2119 for (std::size_t j = 1; j <= Ic && !linked; ++j)
2120 for (std::size_t a = 1; a <= Kc && !linked; ++a)
2121 for (std::size_t b = 1; b <= Kc && !linked; ++b)
2123 if (!linked)
continue;
2124 for (std::size_t r = 1; r <= Kc; ++r) {
2125 if (nd.
routing[r - 1] != RoutingStrategy::PROB)
continue;
2126 if (sn_.classes[r - 1].self_looping)
continue;
2127 if (r <= sn_.issignal.size() && sn_.issignal[r - 1])
continue;
2128 bool routed =
false;
2129 for (std::size_t j = 1; j <= Ic && !routed; ++j)
2130 for (std::size_t s = 1; s <= Kc && !routed; ++s)
2132 if (!routed) nd.
routing[r - 1] = RoutingStrategy::RAND;
2141 routing_linked_ =
true;
2142 apply_fork_overrides();
2151 materialize_transitions();
2152 if (routing_installed()) apply_fork_overrides();
2154 sn_.refresh_struct();
2165 if (!violations.empty()) {
2167 for (
const auto& x : violations) msg +=
"\n " + x;
2168 std::cerr <<
"[LINE] Warning: The NetworkStruct of model " << sn_.name
2169 <<
" violates sn_contract:" << msg << std::endl;
2188 const std::function<T(
const std::vector<std::size_t>&)>& mu,
2189 const std::vector<std::vector<bool>>& swap_graph =
2190 std::vector<std::vector<bool>>()) {
2191 const std::size_t ist = station_of(node,
"set_pas");
2196 pp.
swap_graph.assign(sn_.classes.size(), std::vector<bool>(sn_.classes.size(),
false));
2197 sn_.pasparam[ist] = pp;
2203 for (std::size_t a = 0; a < R; ++a)
2204 for (std::size_t b = 0; b < pp.
swap_graph[a].size(); ++b)
2215 const std::function<T(
const std::vector<std::size_t>&)>& mu,
2219 const std::size_t R = sn_.classes.size();
2220 pp.
swap_graph.assign(R, std::vector<bool>(R,
false));
2221 for (std::size_t a = 0; a < R && a < swap_graph.
rows(); ++a)
2222 for (std::size_t b = 0; b < R && b < swap_graph.
cols(); ++b)
2224 sn_.pasparam[ist] = pp;
2236 std::size_t pk = 1) {
2237 const std::size_t ist = station_of(node,
"set_polling");
2238 if (sn_.stations[ist - 1].sched != SchedStrategy::POLLING)
2239 throw InputError(
"set_polling: the station is not POLLING-scheduled");
2244 if (pp.
switchover.size() < sn_.classes.size())
2246 sn_.pollingparam[ist] = pp;
2261 const std::size_t K = sn_.classes.size();
2262 if (node == 0 || node > sn_.nodes.size())
2263 throw InputError(
"set_sync_reply: node index is out of range");
2264 if (call_cls == 0 || call_cls > K || reply_cls == 0 || reply_cls > K)
2265 throw InputError(
"set_sync_reply: class index is out of range");
2266 const std::size_t ist = sn_.nodes[node - 1].station;
2267 if (ist == 0)
throw InputError(
"set_sync_reply: node is not a station");
2271 if (sn_.replyblock.size() < sn_.nodes.size()) sn_.replyblock.resize(sn_.nodes.size());
2272 for (std::vector<bool>& row : sn_.replyblock)
2273 if (row.size() < K) row.resize(K,
false);
2274 if (sn_.syncreply.size() < K) sn_.syncreply.resize(K, 0);
2275 sn_.replyblock[node - 1][call_cls - 1] =
true;
2276 sn_.syncreply[call_cls - 1] = reply_cls;
2299 const std::vector<double>& class_max_jobs,
2300 double global_max_jobs = -1.0,
2301 const std::vector<DropStrategy>& rule = std::vector<DropStrategy>(),
2302 const std::vector<double>& class_max_memory = std::vector<double>(),
2303 const std::vector<T>& class_size = std::vector<T>(),
2304 double global_max_memory = -1.0,
2305 const std::string& name = std::string()) {
2306 const std::size_t M = sn_.stations.size(), K = sn_.classes.size();
2309 rg.
cap.assign(M, std::vector<double>(K + 1, -1.0));
2310 rg.
maxmem.assign(M, -1.0);
2315 rg.
rule.assign(K, DropStrategy::WAITQ);
2318 for (std::size_t r = 0; r < K && r < rule.size(); ++r) rg.
rule[r] = rule[r];
2319 for (std::size_t r = 0; r < K && r < class_size.size(); ++r) rg.
size[r] = class_size[r];
2321 for (std::size_t j = 0; j < nodes.size(); ++j) {
2322 const std::size_t ist = station_of(nodes[j],
"addRegion");
2324 for (std::size_t r = 0; r < K; ++r) {
2330 if (r >= class_max_jobs.size()) {
2331 rg.
cap[ist - 1][r] = -1.0;
2334 double c = class_max_jobs[r];
2335 if (r < class_max_memory.size() && class_max_memory[r] != -1.0) {
2338 const double memjobs = std::floor(class_max_memory[r] / sz);
2339 c = c == -1.0 ? memjobs : std::min(c, memjobs);
2342 rg.
cap[ist - 1][r] = c;
2344 rg.
cap[ist - 1][K] = global_max_jobs;
2345 rg.
maxmem[ist - 1] = global_max_memory;
2347 sn_.regions.push_back(rg);
2348 return sn_.regions.size();
2357 if (region == 0 || region > sn_.regions.size())
2358 throw InputError(
"set_region_weights: region index is out of range");
2360 for (std::size_t r = 0; r < rg.
weight.size() && r < weight.size(); ++r)
2361 rg.
weight[r] = weight[r];
2366 if (region == 0 || region > sn_.regions.size())
2367 throw InputError(
"set_region_constraint: region index is out of range");
2368 if (A.
rows() != b.size())
2369 throw InputError(
"set_region_constraint: A and b disagree on the number of rows");
2370 sn_.regions[region - 1].lincon_A = A;
2371 sn_.regions[region - 1].lincon_b = b;
2383 const std::function<T(
const std::vector<T>&)>& fn,
2384 const std::string& kind = std::string(), std::size_t node = 0,
2385 std::size_t cls = 0) {
2386 for (std::size_t i = 0; i < sn_.reward.size(); ++i)
2387 if (sn_.reward[i].name == nm) {
2388 sn_.reward[i].fn = fn;
2389 sn_.reward[i].kind = kind;
2390 sn_.reward[i].node = node;
2391 sn_.reward[i].cls = cls;
2400 sn_.reward.push_back(rw);
2413 std::size_t target = 0,
2414 const std::vector<T>& remdist = std::vector<T>()) {
2415 const std::size_t K = sn_.classes.size();
2416 if (cls == 0 || cls > K)
throw InputError(
"set_signal: class index is out of range");
2417 if (target > K)
throw InputError(
"set_signal: target class index is out of range");
2419 if (sn_.issignal.size() < K) {
2420 sn_.issignal.resize(K,
false);
2422 sn_.signaltarget.resize(K, 0);
2424 sn_.signalremdist.resize(K, std::vector<T>());
2426 sn_.issignal[cls - 1] =
true;
2427 sn_.signaltype[cls - 1] = type;
2428 sn_.signaltarget[cls - 1] = target;
2429 sn_.signalrempolicy[cls - 1] = policy;
2430 sn_.signalremdist[cls - 1] = remdist;
2433 std::size_t
station_index(std::size_t node)
const {
return station_of(node,
"station_index"); }
2439 struct TransitionArc {
2440 std::size_t mode, node, cls;
2442 TransitionArc(std::size_t m, std::size_t n, std::size_t c,
const T& v)
2443 : mode(m), node(n), cls(c), count(v) {}
2447 struct TransitionArcs {
2448 std::vector<TransitionArc> enab, inhib, fire;
2459 std::map<std::size_t, TransitionArcs> pending_arcs_;
2462 TransitionParam<T>& declarative_transition(std::size_t node,
const char* what) {
2463 if (node == 0 || node > sn_.nodes.size())
2464 throw InputError(std::string(what) +
": node index is out of range");
2465 if (sn_.nodes[node - 1].nodetype != NodeType::Transition)
2466 throw InputError(std::string(what) +
": node '" + sn_.nodes[node - 1].name +
2467 "' is not a Transition");
2468 if (pending_arcs_.find(node) == pending_arcs_.end())
2469 throw InputError(std::string(what) +
": the Transition '" + sn_.nodes[node - 1].name +
2470 "' was created from a finished TransitionParam, so its modes are "
2471 "already fixed; create it as add_transition(name) to declare them "
2473 return sn_.transparam[node];
2477 TransitionParam<T>& mode_param(std::size_t node, std::size_t mode,
const char* what) {
2478 TransitionParam<T>& tp = declarative_transition(node, what);
2479 if (mode == 0 || mode > tp.nmodes)
2480 throw InputError(std::string(what) +
": the Transition '" + sn_.nodes[node - 1].name +
2481 "' has no mode " + std::to_string(mode));
2486 Station<T>& place_station(std::size_t node,
const char* what) {
2487 if (node == 0 || node > sn_.nodes.size())
2488 throw InputError(std::string(what) +
": node index is out of range");
2489 if (sn_.nodes[node - 1].nodetype != NodeType::Place)
2490 throw InputError(std::string(what) +
": node '" + sn_.nodes[node - 1].name +
2491 "' is not a Place");
2492 return sn_.stations[sn_.nodes[node - 1].station - 1];
2496 TransitionParam<T>& fluid_mode(std::size_t node, std::size_t mode,
const char* what) {
2497 TransitionParam<T>& tp = mode_param(node, mode, what);
2498 if (tp.fluidrate.size() < tp.nmodes) tp.fluidrate.resize(tp.nmodes);
2499 if (tp.fluidguard.size() < tp.nmodes) tp.fluidguard.resize(tp.nmodes);
2500 if (tp.fluidimpulse.size() < tp.nmodes) tp.fluidimpulse.resize(tp.nmodes);
2501 if (tp.fluiddep.size() < tp.nmodes) tp.fluiddep.resize(tp.nmodes);
2502 if (tp.fluidflowdep.size() < tp.nmodes) tp.fluidflowdep.resize(tp.nmodes);
2507 void fluid_place_arg(std::size_t place,
const char* what)
const {
2508 if (place == 0 || place > sn_.nodes.size() || sn_.nodes[place - 1].nodetype != NodeType::Place)
2509 throw InputError(std::string(what) +
": a fluid arc must name a Place");
2513 static void set_fluid_row(std::vector<
typename TransitionParam<T>::FluidArc>& a, std::size_t place,
2515 for (std::size_t k = a.size(); k-- > 0;)
2516 if (a[k].node == place) a.erase(a.begin() +
static_cast<std::ptrdiff_t
>(k));
2517 if (val == 0.0)
return;
2518 typename TransitionParam<T>::FluidArc fa;
2525 void arc_target(std::size_t node, std::size_t mode, std::size_t cls, std::size_t target,
2526 bool must_be_place,
const char* what) {
2527 mode_param(node, mode, what);
2528 if (cls == 0 || cls > sn_.classes.size())
2530 ": class index is out of range; declare the job class before the "
2531 "arcs that move its tokens");
2532 if (target == 0 || target > sn_.nodes.size())
2533 throw InputError(std::string(what) +
": the node the arc names is out of range");
2534 if (must_be_place && sn_.nodes[target - 1].nodetype != NodeType::Place)
2535 throw InputError(std::string(what) +
": '" + sn_.nodes[target - 1].name +
2536 "' is not a Place, and only a Place holds the tokens a mode tests");
2540 static void fill_arcs(std::vector<Matrix<T> >& dst,
const std::vector<TransitionArc>& arcs) {
2541 for (std::size_t a = 0; a < arcs.size(); ++a)
2542 dst[arcs[a].mode - 1](arcs[a].node - 1, arcs[a].cls - 1) = arcs[a].count;
2553 static void normalize_arcs(std::vector<Matrix<T> >& a, std::size_t N, std::size_t K,
2554 bool inhibitor,
const std::string& nm,
const char* what) {
2555 for (std::size_t m = 0; m < a.size(); ++m) {
2556 if (a[m].rows() == N && a[m].cols() == K)
continue;
2557 if (a[m].rows() > N || a[m].cols() > K)
2559 "the Transition '" + nm +
"' has an " + what +
" matrix of " +
2560 std::to_string(a[m].rows()) +
"x" + std::to_string(a[m].cols()) +
2561 " against a model of " + std::to_string(N) +
" nodes and " +
2562 std::to_string(K) +
" classes; an arc matrix is read row by row against "
2563 "the marking, so a wider one is read past its end");
2564 Matrix<T> grown(N, K, inhibitor ? no_inhibitor() : num_traits<T>::from_int(0));
2565 for (std::size_t q = 0; q < a[m].rows(); ++q)
2566 for (std::size_t r = 0; r < a[m].cols(); ++r) grown(q, r) = a[m](q, r);
2579 static T no_inhibitor() {
2580 return num_traits<T>::from_double(std::numeric_limits<double>::infinity());
2592 void materialize_transitions() {
2593 if (sn_.transparam.empty())
return;
2594 const std::size_t N = sn_.nodes.size(), K = sn_.classes.size();
2595 const T zero = num_traits<T>::from_int(0);
2596 for (
typename std::map<std::size_t, TransitionParam<T> >::iterator it =
2597 sn_.transparam.begin();
2598 it != sn_.transparam.end(); ++it) {
2599 TransitionParam<T>& tp = it->second;
2600 const std::string& nm = sn_.nodes[it->first - 1].name;
2601 if (pending_arcs_.find(it->first) == pending_arcs_.end()) {
2602 normalize_arcs(tp.enabling, N, K,
false, nm,
"enabling");
2603 normalize_arcs(tp.inhibiting, N, K,
true, nm,
"inhibiting");
2604 normalize_arcs(tp.firing, N, K,
false, nm,
"firing");
2608 throw InputError(
"Network '" + sn_.name +
"': the Transition '" + nm +
2609 "' has no mode, and every transition needs at least one");
2610 const TransitionArcs& pa = pending_arcs_[it->first];
2611 tp.enabling.assign(tp.nmodes, Matrix<T>(N, K, zero));
2612 tp.inhibiting.assign(tp.nmodes, Matrix<T>(N, K, no_inhibitor()));
2613 tp.firing.assign(tp.nmodes, Matrix<T>(N, K, zero));
2614 fill_arcs(tp.enabling, pa.enab);
2615 fill_arcs(tp.inhibiting, pa.inhib);
2616 fill_arcs(tp.firing, pa.fire);
2620 for (std::size_t m = 0; m < tp.nmodes; ++m)
2621 tp.firingphases[m] =
2626 resolve_fluid_flow_reads();
2634 void resolve_fluid_flow_reads() {
2635 for (
typename std::map<std::size_t, TransitionParam<T> >::iterator it = sn_.transparam.begin();
2636 it != sn_.transparam.end(); ++it) {
2637 TransitionParam<T>& tp = it->second;
2638 for (std::size_t m = 0; m < tp.fluidflowdep.size(); ++m) {
2639 for (
typename TransitionParam<T>::FluidFlowDep& fd : tp.fluidflowdep[m]) {
2640 if (fd.read_given)
continue;
2642 if (m >= tp.enabling.size())
continue;
2643 const Matrix<T>& en = tp.enabling[m];
2644 for (std::size_t q = 0; q < en.rows(); ++q)
2645 for (std::size_t r = 0; r < en.cols(); ++r)
2646 if (num_traits<T>::to_double(en(q, r)) > 0.0) {
2647 fd.read.push_back(q + 1);
2656 std::map<std::size_t, std::vector<std::size_t> > cache_item_classes_;
2659 struct CacheMissArc {
2660 std::size_t cls, from_node, to_node;
2661 CacheMissArc(std::size_t c, std::size_t f, std::size_t t)
2662 : cls(c), from_node(f), to_node(t) {}
2664 std::vector<CacheMissArc> cache_miss_arcs_;
2667 struct RetrievalRoutingEntry {
2668 std::size_t cls, from_node, to_node;
2670 RetrievalRoutingEntry(std::size_t c, std::size_t f, std::size_t t,
const T& q)
2671 : cls(c), from_node(f), to_node(t), p(q) {}
2673 std::vector<RetrievalRoutingEntry> retrieval_routing_entries_;
2676 std::size_t retrieval_class_of(std::size_t cache_node, std::size_t read_class, std::size_t item)
const {
2677 const auto it = sn_.nodeparam.find(cache_node);
2679 if (it != sn_.nodeparam.end() && item >= 1 && item <= it->second.retrieval_classes.size() &&
2680 read_class >= 1 && read_class <= it->second.retrieval_classes[item - 1].size())
2681 rc = it->second.retrieval_classes[item - 1][read_class - 1];
2683 throw InputError(
"No retrieval class defined for the given class/item; call setRetrievalSystem first.");
2688 CacheParam<T>& cache_param_for_class(std::size_t cache_node, std::size_t r,
const char* who) {
2689 auto it = sn_.nodeparam.find(cache_node);
2690 if (cache_node == 0 || cache_node > sn_.nodes.size() || it == sn_.nodeparam.end() ||
2691 sn_.nodes[cache_node - 1].nodetype != NodeType::Cache)
2692 throw InputError(std::string(who) +
": node is not a Cache");
2693 check_class(r, who);
2694 CacheParam<T>& cp = it->second;
2695 const std::size_t K = sn_.classes.size();
2696 if (cp.pread.size() < K) cp.pread.resize(K);
2697 if (cp.preadkind.size() < K) cp.preadkind.resize(K);
2698 if (cp.hitclass.size() < K) cp.hitclass.resize(K, 0);
2699 if (cp.missclass.size() < K) cp.missclass.resize(K, 0);
2700 if (cp.classitem.size() < K) cp.classitem.resize(K, 0);
2705 void check_class(std::size_t r,
const char* who)
const {
2706 if (r == 0 || r > sn_.classes.size())
2707 throw InputError(std::string(who) +
": class index " + std::to_string(r) +
2708 " names no declared class; create the class before passing it");
2712 static std::vector<std::size_t> per_item_classes(
const std::vector<std::size_t>& spec,
2713 std::size_t nitems,
const char* what) {
2714 if (spec.size() == nitems)
return spec;
2715 if (spec.size() == 1)
return std::vector<std::size_t>(nitems, spec[0]);
2717 ": pass one class per item or a single class shared by all");
2721 JobClass& class_ref(std::size_t cls,
const char* what) {
2722 if (cls == 0 || cls > sn_.classes.size())
2723 throw InputError(std::string(what) +
": class index is out of range");
2724 return sn_.classes[cls - 1];
2727 void init_node(std::size_t nd) {
2728 sn_.nodes[nd - 1].routing.assign(sn_.classes.size(), RoutingStrategy::PROB);
2732 void grow_class_vectors() {
2733 const std::size_t K = sn_.classes.size();
2734 for (NodeDef& nd : sn_.nodes) nd.routing.resize(K, RoutingStrategy::PROB);
2735 for (Station<T>& st : sn_.stations) {
2736 if (!st.classcap.empty())
2737 st.classcap.resize(K, std::numeric_limits<double>::infinity());
2738 if (!st.droprule.empty()) st.droprule.resize(K, 0);
2739 if (!st.schedparam.empty()) st.schedparam.resize(K, num_traits<T>::from_int(1));
2744 Station<T>& station_ref(std::size_t node, std::size_t cls,
const char* what) {
2745 const std::size_t ist = station_of(node, what);
2746 if (cls == 0 || cls > sn_.classes.size())
2747 throw InputError(std::string(what) +
": class index is out of range");
2748 return sn_.stations[ist - 1];
2759 void grow_class_slot(std::vector<V>& v, std::size_t cls,
const V& fill) {
2760 const std::size_t K = sn_.classes.size();
2761 if (v.size() < K) v.resize(K < cls ? cls : K, fill);
2764 std::size_t station_of(std::size_t node,
const std::string& what)
const {
2765 if (node == 0 || node > sn_.nodes.size())
2766 throw InputError(what +
": node index " + std::to_string(node) +
" does not exist");
2767 const std::size_t ist = sn_.nodes[node - 1].station;
2769 throw InputError(what +
": node '" + sn_.nodes[node - 1].name +
2770 "' is not a station (it serves no jobs)");
2775 struct ForkOverride {
2776 enum Kind { TASKS, DIST, PROB };
2778 std::size_t fork = 0, cls = 0, dest = 0;
2780 lang::Distrib<T> dist;
2782 std::vector<ForkOverride> fork_overrides_;
2784 bool routing_linked_ =
false;
2793 void record_fork_override(
const ForkOverride& ov) {
2794 if (ov.fork == 0 || ov.fork > sn_.nodes.size() ||
2795 sn_.nodes[ov.fork - 1].nodetype != NodeType::Fork)
2796 throw InputError(
"the node given is not a Fork of this model");
2797 if (ov.cls == 0 || ov.cls > sn_.classes.size())
2798 throw InputError(
"a fork override names a class that does not exist");
2799 fork_overrides_.push_back(ov);
2800 if (routing_installed()) apply_fork_override(ov);
2811 bool routing_installed()
const {
return routing_linked_; }
2814 void apply_fork_overrides() {
2815 for (std::size_t i = 0; i < fork_overrides_.size(); ++i)
2816 apply_fork_override(fork_overrides_[i]);
2819 void apply_fork_override(
const ForkOverride& ov) {
2820 qn::ForkParam<T>& f = fork_param(ov.fork);
2821 const std::vector<std::size_t> dests = fork_dests(ov.fork, ov.dest);
2822 for (std::size_t x = 0; x < dests.size(); ++x) {
2823 const std::size_t k = dests[x];
2825 case ForkOverride::TASKS:
2826 f.fan_out_link(k - 1, ov.cls - 1) = num_traits<T>::from_double(ov.value);
2828 case ForkOverride::DIST:
2829 f.fan_out_dist[k - 1][ov.cls - 1] = ov.dist;
2832 f.fan_out_link(k - 1, ov.cls - 1) = ov.dist.mean;
2834 case ForkOverride::PROB:
2835 f.fan_out_prob(k - 1, ov.cls - 1) = num_traits<T>::from_double(ov.value);
2839 refresh_fork_scalar(sn_.nodes[ov.fork - 1], f);
2851 qn::ForkParam<T>& fork_param(std::size_t fork_node) {
2852 if (fork_node == 0 || fork_node > sn_.nodes.size() ||
2853 sn_.nodes[fork_node - 1].nodetype != NodeType::Fork)
2854 throw InputError(
"the node given is not a Fork of this model");
2855 qn::ForkParam<T>& f = sn_.forkparam[fork_node];
2856 const std::size_t I = sn_.nodes.size(), K = sn_.classes.size();
2857 if (f.fan_out_link.rows() == I && f.fan_out_link.cols() == K)
return f;
2858 const T zero = num_traits<T>::from_int(0);
2861 f.fan_out_dist.assign(I, std::vector<lang::Distrib<T> >(K));
2862 const T tpl = num_traits<T>::from_double(sn_.nodes[fork_node - 1].tasks_per_link);
2863 const T one = num_traits<T>::from_int(1);
2864 for (std::size_t k = 1; k <= I; ++k)
2865 for (std::size_t r = 1; r <= K; ++r)
2866 if (fork_links_to(fork_node, k, r)) {
2867 f.fan_out_link(k - 1, r - 1) = tpl;
2868 f.fan_out_prob(k - 1, r - 1) = one;
2882 void refresh_fork_scalar(qn::NodeDef& nd,
const qn::ForkParam<T>& f) {
2884 std::size_t cnt = 0;
2885 for (std::size_t k = 0; k < f.fan_out_link.rows(); ++k)
2886 for (std::size_t r = 0; r < f.fan_out_link.cols(); ++r) {
2887 if (num_traits<T>::to_double(f.fan_out_prob(k, r)) == 0.0)
continue;
2888 acc += num_traits<T>::to_double(f.fan_out_link(k, r)) *
2889 num_traits<T>::to_double(f.fan_out_prob(k, r));
2892 if (cnt > 0) nd.tasks_per_link = acc /
static_cast<double>(cnt);
2909 bool fork_links_to(std::size_t fork_node, std::size_t k, std::size_t r)
const {
2910 const std::size_t K = sn_.classes.size();
2911 for (std::size_t s = 1; s <= K; ++s)
2912 if (num_traits<T>::to_double(sn_.route_eff(r, s, fork_node, k)) != 0.0)
return true;
2922 std::vector<std::size_t> fork_dests(std::size_t fork_node, std::size_t dest_node)
const {
2923 std::vector<std::size_t> out;
2924 const std::size_t I = sn_.nodes.size(), K = sn_.classes.size();
2925 if (dest_node != 0) {
2927 throw InputError(
"a fork override names a node that does not exist");
2928 out.push_back(dest_node);
2931 for (std::size_t k = 1; k <= I; ++k)
2932 for (std::size_t r = 1; r <= K; ++r)
2933 if (fork_links_to(fork_node, k, r)) { out.push_back(k);
break; }
2935 throw InputError(
"a fork override was set on '" + sn_.nodes[fork_node - 1].name +
2936 "', which links nowhere yet: call link() before the override");
2947 void validate()
const {
2948 if (sn_.classes.empty())
throw InputError(
"Network '" + sn_.name +
"': it has no classes");
2949 if (sn_.stations.empty())
2950 throw InputError(
"Network '" + sn_.name +
"': it has no stations");
2966 const std::size_t K = sn_.classes.size();
2967 const T zero = num_traits<T>::from_int(0);
2968 std::vector<std::vector<bool>> switches(K, std::vector<bool>(K,
false));
2969 for (std::size_t r = 0; r < K; ++r)
2970 for (std::size_t sc = 0; sc < K; ++sc) {
2971 if (r == sc)
continue;
2972 for (std::size_t i = 1; i <= sn_.nodes.size() && !switches[r][sc]; ++i)
2973 for (std::size_t j = 1; j <= sn_.nodes.size(); ++j)
2974 if (sn_.get_route(r + 1, sc + 1, i, j) > zero) {
2975 switches[r][sc] =
true;
2979 for (
const auto& kv : sn_.csmatrix) {
2980 const Matrix<T>& C = kv.second;
2981 for (std::size_t r = 0; r < K && r < C.rows(); ++r)
2982 for (std::size_t sc = 0; sc < K && sc < C.cols(); ++sc)
2983 if (r != sc && C(r, sc) > zero) switches[r][sc] =
true;
2985 for (
const auto& kv : sn_.nodeparam) {
2986 const CacheParam<T>& cp = kv.second;
2987 for (std::size_t r = 0; r < K; ++r) {
2988 if (r < cp.hitclass.size() && cp.hitclass[r] >= 1 && cp.hitclass[r] <= K)
2989 switches[r][cp.hitclass[r] - 1] =
true;
2990 if (r < cp.missclass.size() && cp.missclass[r] >= 1 && cp.missclass[r] <= K)
2991 switches[r][cp.missclass[r] - 1] =
true;
2993 for (
const auto& row : cp.retrieval_classes)
2994 for (std::size_t r = 0; r < K && r < row.size(); ++r)
2995 if (row[r] >= 1 && row[r] <= K) switches[r][row[r] - 1] =
true;
3001 for (std::size_t r = 0; r < K; ++r)
3002 if (sn_.classes[r].spawn >= 1 && sn_.classes[r].spawn <= K)
3003 switches[r][sn_.classes[r].spawn - 1] =
true;
3004 for (std::size_t r = 0; r < sn_.classes.size(); ++r) {
3008 bool switched_into =
false;
3009 for (std::size_t q = 0; q < K; ++q)
3010 if (switches[q][r]) switched_into =
true;
3011 bool served =
false;
3012 for (std::size_t i = 0; i < sn_.stations.size(); ++i)
3013 if (!sn_.service[i][r].disabled) served =
true;
3019 for (std::size_t nd = 0; nd < sn_.nodes.size(); ++nd)
3020 if (sn_.nodes[nd].nodetype == NodeType::Transition) petri =
true;
3021 if (!served && !switched_into && !petri)
3022 throw InputError(
"Network '" + sn_.name +
"': class '" + sn_.classes[r].name +
3023 "' has no service process at any station");
3052 for (std::size_t i = 0; i < sn_.nodes.size(); ++i) {
3053 if (sn_.nodes[i].nodetype != NodeType::Fork)
continue;
3054 bool closed =
false;
3055 for (
const auto& fjp : sn_.fj)
3056 if (fjp.first == i + 1) closed =
true;
3057 if (closed)
continue;
3061 std::vector<bool> seen(sn_.nodes.size(),
false);
3062 std::vector<std::size_t> stack(1, i + 1);
3064 bool departs =
false;
3065 while (!stack.empty() && !departs) {
3066 const std::size_t u = stack.back();
3068 for (std::size_t v = 1; v <= sn_.nodes.size() && !departs; ++v) {
3069 if (seen[v - 1])
continue;
3071 for (std::size_t r = 1; r <= K && !edge; ++r)
3072 for (std::size_t s = 1; s <= K; ++s)
3073 if (sn_.get_route(r, s, u, v) > zero) {
3077 if (!edge)
continue;
3078 if (sn_.nodes[v - 1].nodetype == NodeType::Sink) {
3087 throw InputError(
"Network '" + sn_.name +
"': the Fork '" + sn_.nodes[i].name +
3088 "' is not closed by a Join and no Sink is reachable from it, "
3089 "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)
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_mode_fluid_impulse(std::size_t node, std::size_t mode, std::size_t place, double delta)
Transition.setFluidImpulse(mode, place, delta): every firing of the mode moves the level of the fluid...
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.
void set_item_routing_prob(std::size_t cache_node, std::size_t read_class, std::size_t item, std::size_t source_node, std::size_t dest_node, const T &p)
Short alias of set_item_routing_probability (Cache.setItemRoutingProb).
double get_fluid_level(std::size_t node)
std::size_t add_open_class(const std::string &nm, int prio=0)
void set_cache_read(std::size_t cache_node, std::size_t r, const std::vector< T > &pmf)
Cache.setRead(jobclass, popularity): class r (1-based) reads the cache, drawing items from the pmf pm...
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.
void set_cache_miss_class(std::size_t cache_node, std::size_t r, std::size_t m)
Cache.setMissClass(jobclass, missclass): a class-r read that misses leaves as class m.
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_mode_fluid_dependence(std::size_t node, std::size_t mode, std::size_t place, const std::function< double(double)> &h)
Transition.setFluidRateDependence(mode, place, h): the firing rate of a TIMED, exponential mode is mu...
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_retrieval_system(std::size_t cache_node, std::size_t read_class, std::size_t miss_class, std::size_t queue_node)
Cache.setRetrievalSystem(readClass, missClass, queue) with a single Queue, as the reference accepts.
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...
bool is_place_fluid(std::size_t node)
Place.isFluid(), getFluidCapacity() and getFluidLevel().
void set_orbit_policy(std::size_t node, std::size_t cls, int policy, int max_orbit)
The policy and bound of Queue.setOrbit(class, dist, policy, maxOrbit) on a class already declared by ...
std::size_t add_router(const std::string &nm)
A stateless routing node.
void set_initial_marking(std::size_t node, const std::vector< T > &tokens)
Place.setState(marking): the initial token count of the place, per class.
void set_mode_fluid_rate(std::size_t node, std::size_t mode, std::size_t place, double rate)
Transition.setFluidRate(mode, place, rate): the FSPN flow of a TIMED mode into (rate > 0) or out of (...
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.
bool has_fluid_arcs(std::size_t node) const
Transition.hasFluidArcs(): any mode declares a flow, guard, impulse or fluid rate dependence.
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_item_service_rate(std::size_t queue_node, std::size_t cache_node, std::size_t read_class, std::size_t item, const T &rate)
queue.setItemServiceRate(cache, readClass, item, rate) (Queue.m): override, at queue_node,...
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.
void set_class_completes(std::size_t cls, bool flag)
jobclass.completes = flag: whether a passage through the reference station counts as a completion.
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.
void set_item_routing_probability(std::size_t cache_node, std::size_t read_class, std::size_t item, std::size_t source_node, std::size_t dest_node, const T &p)
Cache.setItemRoutingProbability(readClass, item, source, dest, p): the probability of routing the ret...
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_cache_read(std::size_t cache_node, std::size_t r, const lang::Distrib< T > &d)
Cache.setRead(jobclass, Zipf(s, n)) or DiscreteSampler(p): the law is expanded to its pmf,...
void set_reward(const std::string &nm, const std::function< T(const std::vector< T > &)> &fn, const std::string &kind=std::string(), std::size_t node=0, std::size_t cls=0)
model.setReward(name, fn): a named reward evaluated on the AGGREGATE state row, the per-(station,...
void set_fluid_capacity(std::size_t node, double cap)
Place.setFluidCapacity(B): the level ceiling of a fluid place, Inf when unbounded.
std::size_t add_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::vector< std::vector< bool > > get_class_switching_mask()
model.getClassSwitchingMask(): entry (r-1, s-1) is true if some node can switch class r into s.
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_fluid_level(std::size_t node, double level)
Place.setFluidLevel(x0): the initial level of a fluid place.
void set_state_dep_routing(std::size_t entry, std::size_t departure, const std::vector< std::vector< std::size_t > > &branches, const std::vector< std::size_t > &level, const std::vector< double > &C, const Matrix< double > &d, std::size_t cls=0)
node.setStateDepRouting(class, departure, branches, level, C, d).
std::size_t add_place(const std::string &nm)
A Place: an SPN token container.
void set_routing_weights(std::size_t node, std::size_t cls, const std::map< std::size_t, double > &weights)
The per-destination weights of a WRROBIN dispatcher, per (node, class).
void set_mode_fluid_flow_dependence(std::size_t node, std::size_t mode, std::size_t place, const std::function< double(const std::vector< double > &)> &phi, const std::vector< std::size_t > &read=std::vector< std::size_t >())
Transition.setFluidFlowDependence(mode, place, phi, readPlaces): the flow of the mode into or out of ...
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_cache_hit_class(std::size_t cache_node, std::size_t r, std::size_t h)
Cache.setHitClass(jobclass, hitclass): a class-r read that hits leaves as class h.
void set_place_fluid(std::size_t node, bool fluid)
Place.setFluid(flag): the place holds one real level in [0, fluid capacity] instead of tokens,...
void set_immediate_feedback(std::size_t node, std::size_t cls)
queue.setImmediateFeedback(class): a completing job of that class is fed straight back into service,...
void set_balking(std::size_t node, std::size_t cls, lang::BalkingStrategy strategy, const std::vector< typename Station< T >::BalkingThreshold > &thresholds)
Queue.setBalking(class, strategy, thresholds): an arrival that refuses to JOIN, on the state it finds...
void add_server_type(std::size_t node, const typename Station< T >::ServerType &stype)
Queue.addServerType(...): one heterogeneous server pool of the station.
void set_setup_delayoff(std::size_t node, std::size_t cls, const Distrib< T > &setup, const Distrib< T > &delayoff)
Queue.setDelayOff(class, setupTime, delayoffTime): the station powers down after sitting idle for the...
void set_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_mode_fluid_guard(std::size_t node, std::size_t mode, std::size_t place, const std::string &op, double threshold)
Transition.setFluidGuard(mode, place, op, threshold): the mode is enabled only while the level of the...
double get_fluid_capacity(std::size_t node)
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.
std::vector< std::string > sn_validate_contract(const SnFieldViews &views, bool error=false)
The contract violations of a field view; with error true, throws InternalError with id LINE:internal:...
void sn_fj_nodevisits_mmt(qn::NetworkStruct< T > &sn)
Rewrite sn.nodevisits with the MMT correction.
bool sn_validate_requested()
True when get_struct should validate: debug verbosity, or LINE_VALIDATE_SN=1.
mam::Map< T > dist_to_map(const Distrib< T > &d)
The (D0,D1) pair that reaches sn.proc.
SchedStrategy
Scheduling disciplines, with the values of MATLAB SchedStrategy.
DropStrategy
Blocking and loss rules, with the values of MATLAB DropStrategy.
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.
void closed_reneging_refusal(const std::string &classname, const std::string &where)
Refuses RENEGING on a class of finite population (LINE:input:Model; MATLAB closedRenegingRefusal....
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.
Check a NetworkStruct against its contract (sn_contract.h): the C++ twin of MATLAB's sn_validate.
The <kind> part of a LINE:<category>:<kind> identifier (see _kb/17-error-vocabulary....
std::vector< T > params
Constructor arguments, in MATLAB getParam order.
std::vector< T > trace
Replayer / Trace samples; empty for every other type.
Topology and coefficients of a state-dependent routing subnetwork.
std::vector< std::size_t > 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.
The popularity LAW each class declared, beside the pmf it expands to.
std::size_t n
support size of a Zipf or a DiscreteSampler
The parameters of a Cache node, MATLAB's sn.nodeparam{ind} for a Cache.
int retrieval_capacity
Delayed-hit retrieval system (Cache.setRetrievalSystem).
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 point mass at zero.
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 pass-and-swap / order-independent parameters of a PAS station, keyed by station index (Dorsman an...
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.
std::vector< int > max_orbit
MATLAB sn.orbitMaxJobs: -1 = unbounded.
std::vector< int > policy
MATLAB sn.retrialPolicy: 1 = LINEAR (per-job clocks), 2 = CONSTANT (one clock per orbit).
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.
A fluid rate dependence: the firing rate is multiplied by fun(level(node)).
A marking-dependent fluid flow (setFluidFlowDependence): the flow of the mode on fluid place node is ...
std::vector< std::size_t > read
std::function< double(const std::vector< double > &)> fun
A fluid guard: the mode is enabled only while level(node) >= thr (sgn = +1) or <= thr (sgn = -1).
The parameters of a Transition node: MATLAB sn.nodeparam{ind} for an SPN transition,...
std::vector< std::vector< FluidDep > > fluiddep
std::vector< double > firingprio
firing priority per mode
std::vector< std::vector< FluidGuard > > fluidguard
std::vector< std::vector< FluidFlowDep > > fluidflowdep
std::vector< lang::TimingStrategy > timing
immediate or timed
std::vector< std::string > modenames
std::vector< std::vector< FluidArc > > fluidimpulse
std::vector< lang::Distrib< T > > firingproc
firing distribution per mode
std::vector< double > nmodeservers
servers per mode, may be infinite
std::vector< T > fireweight
weight among simultaneously enabled modes
std::vector< Matrix< T > > firing
firing[m](p,r): class-r tokens mode m moves to/from place p when it fires.
std::vector< Matrix< T > > enabling
enabling[m](p,r): class-r tokens of place p (0-based node) mode m needs.
std::vector< std::function< T(const std::vector< T > &)> > firingdep
Marking-dependent firing-rate multiplier g_m(marking); an empty entry is the unit multiplier.
std::vector< Matrix< T > > inhibiting
inhibiting[m](p,r): class-r tokens of p that BLOCK mode m (Inf = never).
std::vector< std::vector< FluidArc > > fluidrate
FSPN arcs per mode, MATLAB sn.transparam{ind}.fluidrate/fluidguard/fluidimpulse/fluiddep.
std::vector< std::size_t > firingphases
phase count per mode, 0 when non-Markovian