5#ifndef LINE_LANG_QN_NETWORK_STRUCT_H
6#define LINE_LANG_QN_NETWORK_STRUCT_H
86using lang::GlobalConstants;
105 std::vector<std::vector<std::size_t>>
auxall;
135 std::map<std::size_t, std::vector<std::vector<std::size_t>>>
auxmatrix;
136 std::map<std::size_t, std::vector<std::size_t>>
required;
313 if (m >= a.size())
return v;
315 for (std::size_t p = 0; p < a[m].rows(); ++p) {
318 for (std::size_t r = 0; r < a[m].cols(); ++r) {
320 if (!std::isfinite(x))
continue;
324 v[p] = any || a[m].cols() == 0
340 if (m >= a.size())
return v;
341 const double inf = std::numeric_limits<double>::infinity();
343 for (std::size_t p = 0; p < a[m].rows(); ++p) {
345 for (std::size_t r = 0; r < a[m].cols(); ++r) {
347 if (std::isfinite(x) && x < best) best = x;
357 for (
int w = 0; w < 3; ++w)
358 for (std::size_t m = 0; m < all[w]->size(); ++m) {
359 std::size_t touched = 0;
360 for (std::size_t r = 0; r < (*all[w])[m].cols(); ++r) {
362 for (std::size_t p = 0; p < (*all[w])[m].rows() && !any; ++p) {
365 any = w == 1 ? (std::isfinite(v) && v > 0.0) : v > 0.0;
369 if (touched > 1)
return true;
377 for (
int w = 0; w < 3; ++w)
378 for (std::size_t m = 0; m < all[w]->size(); ++m)
379 for (std::size_t r = 0; r < (*all[w])[m].cols(); ++r)
380 for (std::size_t p = 0; p < (*all[w])[m].rows(); ++p) {
382 if (w == 1 ? (std::isfinite(v) && v > 0.0) : v > 0.0)
return r + 1;
389 std::vector<lang::TimingStrategy>
timing;
396 std::vector<std::function<T(
const std::vector<T>&)>>
firingdep;
430 std::vector<lang::Distrib<T>>
setup;
434 for (std::size_t r =
setup.size(); r > 0; --r)
435 if (!
setup[r - 1].disabled) {
522 std::vector<std::vector<Matrix<T>>>
accost;
562 std::vector<std::size_t>& rc_items,
563 std::vector<std::size_t>& rc_orig) {
571 rc_items.push_back(k + 1);
572 rc_orig.push_back(c + 1);
576 std::vector<std::size_t> ord(rc_list.size());
577 for (std::size_t i = 0; i < ord.size(); ++i) ord[i] = i;
578 std::stable_sort(ord.begin(), ord.end(),
579 [&](std::size_t a, std::size_t b) { return rc_list[a] < rc_list[b]; });
580 std::vector<std::size_t> l(rc_list.size()), it(rc_list.size()), oc(rc_list.size());
581 for (std::size_t i = 0; i < ord.size(); ++i) {
582 l[i] = rc_list[ord[i]];
583 it[i] = rc_items[ord[i]];
584 oc[i] = rc_orig[ord[i]];
611 double cap = std::numeric_limits<double>::infinity();
836 double deadline = std::numeric_limits<double>::infinity();
866 std::vector<std::vector<std::size_t>>
pools;
867 std::vector<std::vector<std::size_t>>
off,
len;
868 std::vector<std::vector<std::vector<T>>>
alpha;
869 std::vector<std::vector<std::vector<T>>>
exit;
870 std::vector<std::vector<Matrix<T>>>
D0;
874 std::vector<std::vector<std::size_t>>
perms;
876 std::size_t
perm_index(
const std::vector<std::size_t>& order)
const {
877 for (std::size_t p = 0; p <
perms.size(); ++p)
878 if (
perms[p] == order)
return p + 1;
899 std::vector<std::pair<std::size_t, std::size_t>>
pairs;
971 if (i >=
stations.size() ||
stations[i].nodetype != NodeType::Place)
return false;
972 for (std::size_t r = 0; r <
service[i].size(); ++r)
1000 std::vector<std::pair<std::size_t, std::size_t>>
fj;
1088 const typename std::map<std::size_t, ForkParam<T> >::const_iterator it =
1107 std::function<T(
const std::vector<T>&)>
fn;
1144 std::vector<std::vector<double>>
cap;
1177 if (ind == 0 || ind >
nodes.size())
return false;
1178 const std::size_t ist =
nodes[ind - 1].station;
1290 std::vector<std::vector<std::size_t>>
nvars;
1396 if (ist == 0 || ist >
stations.size())
return pp;
1397 const typename std::map<std::size_t, PollingParam>::const_iterator it =
1422 if (ind == 0 || ind >
nvars.size())
return 0;
1424 for (std::size_t j = 0; j <
nvars[ind - 1].size(); ++j) w +=
nvars[ind - 1][j];
1435 std::vector<double> v;
1446 return service[ist - 1][r - 1].type;
1471 std::size_t
phases_of(std::size_t ist, std::size_t r)
const {
1508 const std::size_t p =
phases_of(ist, r);
1509 return p > 0 ? p : 1;
1521 if (ist == 0 || ist >
stations.size())
return 0;
1522 const std::vector<std::size_t>&
mc =
stations[ist - 1].marked_classes;
1523 for (std::size_t k = 0; k <
mc.size(); ++k)
1524 if (
mc[k] == r)
return k + 1;
1533 if (ist == 0 || ist >
stations.size())
return 0;
1534 const std::vector<std::size_t>&
mc =
stations[ist - 1].marked_classes;
1535 return mc.empty() ? 0 :
mc[0];
1539 if (n.nodetype == NodeType::Fork)
return true;
1555 if (rs == RoutingStrategy::SDR)
return true;
1574 std::vector<std::size_t>
rr_outlinks(std::size_t ind, std::size_t r)
const {
1575 std::vector<std::size_t> out;
1577 const std::size_t K =
classes.size(), I =
nodes.size();
1578 for (std::size_t j = 1; j <= I; ++j) {
1579 bool linked =
false;
1580 for (std::size_t s = 1; s <= K && !linked; ++s)
1581 if (
get_route(r, s, ind, j) > zero) linked =
true;
1582 if (linked) out.push_back(j);
1589 const std::vector<std::size_t> ol =
rr_outlinks(ind, r);
1590 const std::map<std::size_t, double>* w =
1591 (ind <=
nodes.size() &&
nodes[ind - 1].routing_weights.size() >= r)
1592 ? &
nodes[ind - 1].routing_weights[r - 1]
1594 std::vector<std::size_t> cycle;
1595 for (std::size_t d = 0; d < ol.size(); ++d) {
1598 const std::map<std::size_t, double>::const_iterator it = w->find(ol[d]);
1599 if (it != w->end() && it->second > 0.0)
1600 reps = std::lround(it->second) < 1 ? 1 : std::lround(it->second);
1602 for (
long q = 0; q < reps; ++q) cycle.push_back(ol[d]);
1609 if (
ctmcmapc.empty())
return nullptr;
1610 const auto it =
ctmcmapc.find(std::make_pair(ist, r));
1611 return it ==
ctmcmapc.end() ? nullptr : &it->second;
1616 std::size_t slot = 0;
1617 for (std::size_t j = 0; j + 1 < r && j <
nvars[ind - 1].size(); ++j) slot +=
nvars[ind - 1][j];
1631 const std::size_t R =
classes.size();
1632 if (ind == 0 || ind >
nodes.size() || r == 0 || r > R)
return 0;
1633 const std::vector<RoutingStrategy>& rt_i =
nodes[ind - 1].routing;
1634 if (rt_i.size() < r)
return 0;
1635 if (rt_i[r - 1] != RoutingStrategy::RROBIN && rt_i[r - 1] != RoutingStrategy::WRROBIN)
1637 if (
nvars.size() < ind)
return 0;
1638 std::size_t slot = 0;
1639 for (std::size_t j = 0; j < R + r && j <
nvars[ind - 1].size(); ++j)
1640 slot +=
nvars[ind - 1][j];
1650 std::size_t
rr_dest(std::size_t ind, std::size_t r,
const std::vector<T>& varrow)
const {
1652 if (slot == 0 || slot > varrow.size())
return 0;
1654 if (
nodes[ind - 1].routing[r - 1] == RoutingStrategy::RROBIN)
1655 return v > 0 ?
static_cast<std::size_t
>(v) : 0;
1657 if (v < 1 ||
static_cast<std::size_t
>(v) > cycle.size())
return 0;
1658 return cycle[
static_cast<std::size_t
>(v) - 1];
1667 void rr_advance(std::size_t ind, std::size_t r, std::vector<T>& varrow)
const {
1669 if (slot == 0 || slot > varrow.size())
return;
1670 if (
nodes[ind - 1].routing[r - 1] == RoutingStrategy::RROBIN) {
1671 const std::vector<std::size_t> ol =
rr_outlinks(ind, r);
1672 if (ol.empty())
return;
1674 std::size_t idx = ol.size();
1675 for (std::size_t d = 0; d < ol.size(); ++d)
1676 if (
static_cast<long>(ol[d]) == cur) { idx = d;
break; }
1677 const std::size_t nxt = (idx + 1 < ol.size()) ? idx + 1 : 0;
1682 if (cycle.empty())
return;
1684 const long nxt = (pos < 1 || static_cast<std::size_t>(pos) >= cycle.size()) ? 1 : pos + 1;
1692 if (rs == RoutingStrategy::RROBIN || rs == RoutingStrategy::WRROBIN)
return true;
1716 bool declared =
false;
1717 for (
const std::vector<bool>& row :
immfeed) {
1723 if (declared)
break;
1725 if (!declared)
return false;
1730 for (std::size_t ist = 1; ist <=
immfeed.size(); ++ist) {
1732 if (ind == 0 || ind >
nodes.size())
continue;
1734 if (isf == 0)
continue;
1735 for (std::size_t r = 1; r <=
immfeed[ist - 1].size() && r <= R; ++r) {
1736 if (!
immfeed[ist - 1][r - 1])
continue;
1737 const std::size_t col = (isf - 1) * R + (r - 1);
1738 if (col >=
rt.
cols())
continue;
1739 for (std::size_t sc = 0; sc < R; ++sc) {
1740 const std::size_t row = (isf - 1) * R + sc;
1768 throw InputError(
"network: a station is not a stateful node");
1780 nodes.push_back(nd);
1794 nodes.push_back(nd);
1797 return nodes.size();
1808 service[station - 1][cls - 1] = d;
1812 void set_route(std::size_t r, std::size_t s, std::size_t i, std::size_t j,
const T& p) {
1813 auto key = std::make_pair(r, s);
1814 auto it =
P.find(key);
1819 it->second(i - 1, j - 1) = p;
1835 auto key = std::make_pair(r, s);
1836 auto it =
Peff.find(key);
1837 if (it ==
Peff.end())
1840 if (it->second.rows() !=
nodes.size()) {
1842 for (std::size_t a = 0; a < it->second.rows(); ++a)
1843 for (std::size_t b = 0; b < it->second.cols(); ++b) g(a, b) = it->second(a, b);
1846 it->second(i - 1, j - 1) = p;
1857 T
get_route(std::size_t r, std::size_t s, std::size_t i, std::size_t j)
const {
1858 auto it =
P.find(std::make_pair(r, s));
1860 return it->second(i - 1, j - 1);
1864 T
route_eff(std::size_t r, std::size_t s, std::size_t i, std::size_t j)
const {
1866 auto it =
Peff.find(std::make_pair(r, s));
1868 return it->second(i - 1, j - 1);
1916 if (ind == 0 || ind >
replyblock.size())
return false;
1917 for (std::size_t k = 0; k <
syncreply.size(); ++k)
1935 if (ind == 0 || ind >
nodes.size())
return true;
1937 if (nt != NodeType::Queue && nt != NodeType::Delay)
return true;
1938 const std::size_t sti =
nodes[ind - 1].station;
1939 if (sti == 0 || sti >
stations.size())
return true;
1940 if (!
stations[sti - 1].server_types.empty())
return true;
1941 if (
procid(sti, r + 1) != ProcessType::DISABLED)
return true;
1959 std::vector<std::vector<bool>> reached;
1960 if (N == 0 || K == 0)
return reached;
1962 reached.assign(N, std::vector<bool>(K,
false));
1963 std::vector<std::vector<bool>> seed(N, std::vector<bool>(K,
false));
1964 for (std::size_t i = 0; i <
stations.size(); ++i) {
1965 if (
stations[i].nodetype != NodeType::Source)
continue;
1967 if (ind == 0 || ind > N)
continue;
1968 for (std::size_t r = 0; r < K; ++r)
1969 if (
procid(i + 1, r + 1) != ProcessType::DISABLED) seed[ind - 1][r] =
true;
1971 for (std::size_t r = 0; r < K && r <
classes.size(); ++r) {
1972 const double pop =
classes[r].population;
1973 if (!std::isfinite(pop) || pop <= 0.0)
continue;
1975 if (ind == 0 || ind > N)
continue;
1976 seed[ind - 1][r] =
true;
1978 std::vector<std::pair<std::size_t, std::size_t>> stack;
1979 for (std::size_t i = 0; i < N; ++i)
1980 for (std::size_t r = 0; r < K; ++r)
1982 reached[i][r] =
true;
1983 stack.push_back(std::make_pair(i, r));
1985 while (!stack.empty()) {
1986 const std::size_t i = stack.back().first;
1987 const std::size_t r = stack.back().second;
1990 if (nt == NodeType::Sink)
continue;
1991 if (nt == NodeType::Source && !seed[i][r])
continue;
1993 const std::size_t row = i * K + r;
1994 for (std::size_t col = 0; col < N * K; ++col) {
1998 const std::size_t j = col / K, sIdx = col % K;
1999 if (!reached[j][sIdx]) {
2000 reached[j][sIdx] =
true;
2001 stack.push_back(std::make_pair(j, sIdx));
2060 for (std::size_t ind = 0; ind <
nodes.size(); ++ind) {
2062 if (nt == NodeType::Cache || nt == NodeType::Place ||
2063 nt == NodeType::Transition || nt == NodeType::Fork ||
2064 nt == NodeType::Join)
2069 if (reached.empty())
return;
2070 for (std::size_t i = 0; i <
nstations; ++i) {
2072 if (nt != NodeType::Queue && nt != NodeType::Delay)
continue;
2076 if (!
stations[i].server_types.empty())
continue;
2078 if (ind == 0)
continue;
2079 for (std::size_t r = 0; r < K; ++r) {
2080 if (
procid(i + 1, r + 1) != ProcessType::DISABLED)
continue;
2086 if (ind - 1 >= reached.size() || r >= reached[ind - 1].size())
continue;
2087 if (!reached[ind - 1][r])
continue;
2088 const std::string kind = (nt == NodeType::Delay) ?
"Delay" :
"Queue";
2090 "' has no service configured for job class '" +
2092 "', but the class is routed to it. Jobs would arrive and "
2093 "never leave. Configure a service for that class, or route "
2110 immfeed.assign(M, std::vector<bool>(R,
false));
2111 for (std::size_t i = 0; i < M; ++i)
2112 for (std::size_t r = 0; r < R; ++r) {
2113 const bool station_has =
2149 if (ind == 0 || ind >
nodes.size() ||
nodes[ind - 1].stateful)
return;
2150 nodes[ind - 1].stateful =
true;
2156 for (std::size_t ind = 1; ind <=
nodes.size(); ++ind) {
2160 for (std::size_t r = 0; r < nd.
routing.size(); ++r)
2161 if (nd.
routing[r] == RoutingStrategy::RROBIN ||
2162 nd.
routing[r] == RoutingStrategy::WRROBIN)
2172 const std::size_t R =
classes.size();
2177 nvars.assign(
nodes.size(), std::vector<std::size_t>(3 * R + 1, 0));
2178 for (std::size_t ind = 1; ind <=
nodes.size(); ++ind) {
2179 const std::size_t ist =
nodes[ind - 1].station;
2183 for (std::size_t r = 1; r <= R; ++r) {
2185 if (pt == ProcessType::MAP || pt == ProcessType::MMPP2)
2186 nvars[ind - 1][r - 1] += 1;
2190 const std::vector<RoutingStrategy>& rt_i =
nodes[ind - 1].routing;
2191 for (std::size_t r = 1; r <= R && r <= rt_i.size(); ++r)
2192 if (rt_i[r - 1] == RoutingStrategy::RROBIN ||
2193 rt_i[r - 1] == RoutingStrategy::WRROBIN)
2194 nvars[ind - 1][R + r - 1] += 1;
2203 if (ist != 0 &&
stations[ist - 1].sched == SchedStrategy::POLLING) {
2206 for (std::size_t r = 0; r < pp.
switchover.size(); ++r) {
2208 if (!d.
disabled && d.
D0.rows() > 0 && d.
type != ProcessType::IMMEDIATE)
2211 std::size_t w = anysw ? 2 : 0;
2213 nvars[ind - 1][2 * R] = w;
2216 for (std::size_t r = 1; r <= R; ++r)
2219 nvars[ind - 1][2 * R + r] = 1;
2228 if (
stations[ist - 1].sched == SchedStrategy::POLLING)
2230 "Server breakdowns are not supported at a polling station (" +
2232 "): the polling controller and the breakdown status share one "
2233 "local-state column.");
2234 for (std::size_t r = 1; r <= R; ++r)
2238 "Server breakdowns are not supported at station '" +
2240 "', which also holds servers for a synchronous reply: the reply "
2241 "counters would trail the breakdown status the service handlers read");
2242 nvars[ind - 1][2 * R] = 1;
2244 const typename std::map<std::size_t, CacheParam<T>>::const_iterator ci =
2251 for (std::size_t u = 0; u < ci->second.itemcap.size(); ++u)
2252 if (ci->second.itemcap[u] > 0)
2253 w +=
static_cast<std::size_t
>(ci->second.itemcap[u]);
2254 if (ci->second.retrieval_capacity > 0) {
2255 w += ci->second.nitems;
2256 std::vector<std::size_t> rcl, rci, rco;
2260 nvars[ind - 1][2 * R] = w;
2282 const std::size_t R =
classes.size(), N =
nodes.size();
2284 for (std::size_t r = 0; r < R && r <
syncreply.size(); ++r)
2287 replyblock.assign(N, std::vector<bool>(R,
false));
2288 for (std::size_t r = 1; r <= R; ++r) {
2291 if (s < 1 || s > R)
continue;
2292 for (std::size_t ind = 1; ind <= N; ++ind) {
2293 const std::size_t ist =
nodes[ind - 1].station;
2295 if (
stations[ist - 1].sched == SchedStrategy::INF)
continue;
2296 bool arrives_here =
false;
2297 for (std::size_t i = 1; i <= N && !arrives_here; ++i)
2298 for (std::size_t q = 1; q <= R; ++q)
2301 rtnodes((i - 1) * R + q - 1, (ind - 1) * R + s - 1)) > 0) {
2302 arrives_here =
true;
2305 if (!arrives_here)
continue;
2306 if (
stations[ist - 1].sched != SchedStrategy::FCFS)
2308 "network '" +
name +
2309 "': synchronous calls (REPLY signals) are supported only at FCFS "
2312 "' uses another discipline. A held server is encoded as a per-class "
2313 "counter, which is exact only where servers are interchangeable (FCFS) "
2314 "or unlimited (INF); the other disciplines are not yet encoded rather than "
2315 "infeasible. Set this station to FCFS or INF, or simulate the layered model "
2316 "directly with SolverLDES.");
2337 const std::size_t R =
classes.size();
2338 const std::size_t N =
nodes.size();
2339 std::vector<std::size_t> out;
2341 std::vector<bool> seen(N + 1,
false);
2342 std::vector<std::size_t> frontier(1, ind);
2344 while (!frontier.empty()) {
2345 const std::size_t i = frontier.back();
2346 frontier.pop_back();
2347 for (std::size_t j = 1; j <= N; ++j) {
2348 if (seen[j])
continue;
2349 bool linked =
false;
2350 for (std::size_t r = 0; r < R && !linked; ++r)
2351 for (std::size_t s = 0; s < R && !linked; ++s)
2353 rtnodes((i - 1) * R + r, (j - 1) * R + s)) > 0)
2355 if (!linked)
continue;
2357 if (
nodes[j - 1].station != 0) {
2360 frontier.push_back(j);
2372 const std::size_t R =
classes.size();
2373 const std::size_t N =
nodes.size();
2377 for (std::size_t ind = 1; ind <= N; ++ind) {
2378 const std::size_t ist =
nodes[ind - 1].station;
2379 if (ist == 0)
continue;
2381 if (nt == NodeType::Source || nt == NodeType::Cache)
continue;
2383 bool declares =
false;
2384 for (std::size_t r = 1; r <= R; ++r) {
2385 const bool here_bas =
droprule.size() >= ist &&
droprule[ist - 1].size() >= r &&
2386 droprule[ist - 1][r - 1] == DropStrategy::BAS;
2387 for (std::size_t d = 0; d < dests.size(); ++d) {
2388 const std::size_t jst =
nodes[dests[d] - 1].station;
2389 if (jst == 0 ||
nodes[dests[d] - 1].nodetype == NodeType::Source)
continue;
2400 const double dcap =
stations[jst - 1].cap;
2401 if (!std::isfinite(dcap) || !(dcap > 0))
continue;
2402 const bool there_bas =
2404 droprule[jst - 1][r - 1] == DropStrategy::BAS;
2405 if (!here_bas && !there_bas)
continue;
2413 if (!declares)
continue;
2420 "' combines server breakdowns with true-BAS blocking: the breakdown status "
2421 "and the BAS blocked marker share one local-state column. Remove the BAS drop "
2422 "rule or the breakdown");
2423 if (
stations[ist - 1].sched == SchedStrategy::POLLING)
2425 "true BAS blocking is not supported at the polling station '" +
2427 "': the polling controller and the BAS blocked marker share one local-state "
2428 "column. Use a non-polling discipline at the blocking station, or remove the "
2430 for (std::size_t r = 1; r <= R; ++r)
2434 "true BAS blocking is not supported at station '" +
nodes[ind - 1].
name +
2435 "', which also holds servers for a synchronous reply: fromMarginal appends "
2436 "the reply counters AFTER the blocked marker, so the marker would no "
2437 "longer be the trailing column the departure handler reads");
2438 nvars[ind - 1][2 * R] = 1;
2459 for (std::size_t i = 0; i <
stations.size(); ++i) {
2461 if (st.
sched == SchedStrategy::DPS || st.
sched == SchedStrategy::GPS) {
2466 if (st.
sched == SchedStrategy::SEPT || st.
sched == SchedStrategy::LEPT) {
2468 std::vector<double> means;
2469 for (std::size_t r = 0; r <
classes.size(); ++r)
2471 std::vector<double> sorted = means;
2472 std::sort(sorted.begin(), sorted.end());
2473 sorted.erase(std::unique(sorted.begin(), sorted.end()), sorted.end());
2474 if (st.
sched == SchedStrategy::LEPT)
2475 std::reverse(sorted.begin(), sorted.end());
2477 for (std::size_t r = 0; r <
classes.size(); ++r)
2478 for (std::size_t k = 0; k < sorted.size(); ++k)
2479 if (sorted[k] == means[r])
2516 const std::size_t K =
classes.size(), I =
nodes.size();
2518 bool trivial =
true;
2521 if (rs != RoutingStrategy::PROB && rs != RoutingStrategy::DISABLED) trivial =
false;
2528 bool has_cs =
false;
2530 if (nd.nodetype == NodeType::ClassSwitch) has_cs =
true;
2533 const bool has_cache = !
nodeparam.empty();
2534 if (trivial && !has_cs && !has_cache)
return;
2536 for (
const auto& kv :
P)
Peff[kv.first] = kv.second;
2540 std::vector<char> conn(I * I, 0);
2541 for (
const auto& kv :
P) {
2543 if (M.
rows() < I || M.
cols() < I)
continue;
2544 for (std::size_t a = 0; a < I; ++a)
2545 for (std::size_t b = 0; b < I; ++b)
2546 if (M(a, b) > zero) conn[a * I + b] = 1;
2548 auto eff_at = [&](std::size_t r, std::size_t s) ->
Matrix<T>& {
2549 auto key = std::make_pair(r, s);
2550 auto it =
Peff.find(key);
2551 if (it ==
Peff.end())
2553 if (it->second.rows() != I) {
2555 for (std::size_t a = 0; a < it->second.rows(); ++a)
2556 for (std::size_t b = 0; b < it->second.cols(); ++b) g(a, b) = it->second(a, b);
2562 for (std::size_t i = 1; i <= I; ++i) {
2564 for (std::size_t r = 1; r <= K; ++r) {
2566 nd.
routing.size() >= r ? nd.
routing[r - 1] : RoutingStrategy::PROB;
2567 if (rs == RoutingStrategy::PROB || rs == RoutingStrategy::DISABLED)
continue;
2568 if (rs != RoutingStrategy::RAND && rs != RoutingStrategy::RROBIN &&
2569 rs != RoutingStrategy::JSQ && rs != RoutingStrategy::SQ &&
2570 rs != RoutingStrategy::WRROBIN && rs != RoutingStrategy::SDR)
2573 "' has no routing-matrix expansion in this port");
2589 const bool open_class = !std::isfinite(
2591 const bool from_source = nd.
nodetype == NodeType::Source;
2592 const bool from_sink = nd.
nodetype == NodeType::Sink;
2593 if (!open_class && (from_source || from_sink))
continue;
2594 std::vector<std::pair<std::size_t, std::size_t>> dest;
2595 for (std::size_t j = 1; j <= I; ++j) {
2596 if (!open_class &&
nodes[j - 1].nodetype == NodeType::Sink)
continue;
2597 if (conn[(i - 1) * I + (j - 1)]) dest.emplace_back(j, r);
2599 if (dest.empty())
continue;
2604 std::vector<T> share(dest.size(),
2607 if (rs == RoutingStrategy::WRROBIN) {
2608 const std::map<std::size_t, double>* w =
2611 std::vector<double> raw(dest.size(), 0.0);
2613 for (std::size_t d = 0; d < dest.size(); ++d) {
2614 const std::map<std::size_t, double>::const_iterator it =
2615 w->find(dest[d].first);
2616 raw[d] = (it == w->end()) ? 0.0 : it->second;
2620 for (std::size_t d = 0; d < dest.size(); ++d)
2625 for (std::size_t s = 1; s <= K; ++s) {
2626 if (
Peff.find(std::make_pair(r, s)) ==
Peff.end())
continue;
2628 for (std::size_t j = 1; j <= I; ++j) B(i - 1, j - 1) = zero;
2630 for (std::size_t d = 0; d < dest.size(); ++d)
2631 eff_at(r, dest[d].second)(i - 1, dest[d].first - 1) = share[d];
2637 const std::size_t cs = kv.first;
2639 if (C.
rows() != K || C.
cols() != K)
2640 throw InputError(
"network: the class-switch matrix of node '" +
2641 nodes[cs - 1].
name +
"' is not (nclasses x nclasses)");
2656 std::vector<std::vector<T>> diag(K, std::vector<T>(I, zero));
2657 for (std::size_t s = 1; s <= K; ++s) {
2658 if (!
Peff.empty() &&
Peff.find(std::make_pair(s, s)) ==
Peff.end())
continue;
2659 for (std::size_t j = 1; j <= I; ++j)
2660 diag[s - 1][j - 1] =
2661 Peff.empty() ?
get_route(s, s, cs, j) : eff_at(s, s)(cs - 1, j - 1);
2664 for (std::size_t r = 1; r <= K; ++r)
2665 for (std::size_t s = 1; s <= K; ++s) {
2666 if (
Peff.find(std::make_pair(r, s)) ==
Peff.end())
continue;
2668 for (std::size_t j = 1; j <= I; ++j) B(cs - 1, j - 1) = zero;
2670 for (std::size_t r = 1; r <= K; ++r)
2671 for (std::size_t s = 1; s <= K; ++s) {
2672 if (!(C(r - 1, s - 1) > zero))
continue;
2674 for (std::size_t j = 1; j <= I; ++j)
2675 if (diag[s - 1][j - 1] > zero)
2676 B(cs - 1, j - 1) = T(diag[s - 1][j - 1] * C(r - 1, s - 1));
2690 const std::size_t ci = kv.first;
2692 for (std::size_t r = 0; r < cp.
hitclass.size() && r < K; ++r) {
2695 for (std::size_t s = 1; s <= K; ++s) {
2697 for (std::size_t j = 1; j <= I; ++j) B(ci - 1, j - 1) = zero;
2699 eff_at(r + 1, cp.
hitclass[r])(ci - 1, ci - 1) = half;
2701 eff_at(r + 1, cp.
missclass[r])(ci - 1, ci - 1) = half;
2746 const std::size_t I =
nodes.size();
2747 if (joinNode == 0 || joinNode > I)
return 0;
2748 std::size_t forkNode = 0;
2749 for (std::size_t a = 0; a <
fj.size(); ++a)
2750 if (
fj[a].second == joinNode) {
2751 forkNode =
fj[a].first;
2755 std::vector<bool> seen(I + 1,
false);
2756 std::size_t deg = 0;
2757 for (
typename std::map<std::pair<std::size_t, std::size_t>,
Matrix<T> >::const_iterator
2759 it !=
P.end(); ++it) {
2760 if (it->second.rows() < I || it->second.cols() < I)
continue;
2761 for (std::size_t b = 1; b <= I; ++b) {
2762 if (seen[b])
continue;
2763 const T v = (forkNode != 0) ? it->second(forkNode - 1, b - 1)
2764 : it->second(b - 1, joinNode - 1);
2771 if (forkNode == 0 || forkNode > I)
return deg;
2785 const std::size_t lo = (r != 0 && r <= fol.
cols()) ? r - 1 : 0;
2786 const std::size_t hi = (r != 0 && r <= fol.
cols()) ? r - 1 : fol.
cols() - 1;
2788 for (std::size_t c = lo; c <= hi && c < fol.
cols(); ++c) {
2790 for (std::size_t d = 0; d < fol.
rows(); ++d) {
2792 if (!(link > 0.0))
continue;
2796 if (acc > best) best = acc;
2798 if (best > 0.0)
return static_cast<std::size_t
>(best + 0.5);
2801 double w =
nodes[forkNode - 1].tasks_per_link;
2802 if (!(w >= 1.0)) w = 1.0;
2803 return deg *
static_cast<std::size_t
>(w + 0.5);
2814 std::vector<std::size_t> out;
2815 for (
typename std::map<std::size_t, JoinDecl>::const_iterator it =
joindecl.begin();
2818 if (it->second.quorum <= 0.0)
continue;
2820 const std::size_t k =
static_cast<std::size_t
>(it->second.quorum + 0.5);
2821 if (n == 0 || k < n) out.push_back(it->first);
2828 const double inf = std::numeric_limits<double>::infinity();
2830 classcap.assign(M, std::vector<double>(K, inf));
2831 droprule.assign(M, std::vector<DropStrategy>(K, DropStrategy::WAITQ));
2832 std::vector<std::vector<double>> chaincap(M, std::vector<double>(std::max(
nchains, K), inf));
2840 std::vector<char> quorum_class(K + 1, 0);
2844 for (std::size_t x = 0; x < qj.size(); ++x)
2845 for (
typename std::map<std::pair<std::size_t, std::size_t>,
2847 it !=
P.end(); ++it) {
2848 if (it->second.rows() <
nodes.size() || qj[x] >
nodes.size())
continue;
2849 for (std::size_t a = 1; a <=
nodes.size(); ++a)
2850 if (it->second(a - 1, qj[x] - 1) > zero) {
2851 if (it->first.first <= K) quorum_class[it->first.first] = 1;
2852 if (it->first.second <= K) quorum_class[it->first.second] = 1;
2864 double fork_task_factor = 1.0;
2865 for (std::size_t i = 0; i <
nodes.size(); ++i) {
2866 if (
nodes[i].nodetype != NodeType::Fork)
continue;
2867 const double w =
nodes[i].tasks_per_link;
2868 if (w >= 1.0 && std::isfinite(w)) fork_task_factor *= std::floor(w + 0.5);
2871 for (std::size_t c = 0; c <
nchains; ++c) {
2872 double chain_cap = 0.0;
2874 bool quorum =
false;
2875 for (std::size_t r :
inchain[c]) {
2876 if (std::isinf(
classes[r - 1].population)) open =
true;
2877 else chain_cap +=
classes[r - 1].population;
2878 if (r <= K && quorum_class[r]) quorum =
true;
2882 bool spawn_fed =
false;
2883 for (std::size_t q = 0; q < K && !spawn_fed; ++q)
2884 for (std::size_t r :
inchain[c])
2885 if (
classes[q].spawn == r) spawn_fed =
true;
2886 chain_cap *= fork_task_factor;
2887 if (open || quorum || spawn_fed) chain_cap = inf;
2888 for (std::size_t r :
inchain[c])
2889 for (std::size_t i = 0; i < M; ++i) {
2892 if (st.
nodetype != NodeType::Source) {
2893 const bool cap_finite = st.
cap >= 0.0 && !std::isinf(st.
cap);
2894 const bool classcap_finite = st.
classcap.size() >= r &&
2898 st.
droprule[r - 1] ==
static_cast<int>(DropStrategy::WAITQ) &&
2899 std::isinf(
classes[r - 1].population) &&
2900 (cap_finite || classcap_finite))
2902 "station '" + st.
name +
"' declares setDropRule(WAITQ) for the "
2903 "open class '" +
classes[r - 1].name +
"' at a finite capacity: "
2904 "LINE does not implement waiting-room blocking for an open "
2905 "arrival at a plain finite buffer. Use DropStrategy.DROP for a "
2906 "loss station, or BAS / BBS / RSRD for blocking between "
2910 }
else if (std::isinf(st.
cap)) {
2911 droprule[i][r - 1] = DropStrategy::WAITQ;
2912 }
else if (!std::isinf(
classes[r - 1].population)) {
2913 droprule[i][r - 1] = DropStrategy::WAITQ;
2915 droprule[i][r - 1] = DropStrategy::DROP;
2921 chaincap[i][c] = 0.0;
2924 chaincap[i][c] = chain_cap;
2931 for (std::size_t i = 0; i < M; ++i) {
2936 double sc = 0.0, scl = 0.0;
2937 for (std::size_t c = 0; c < chaincap[i].size(); ++c) sc += chaincap[i][c];
2938 for (std::size_t r = 0; r < K; ++r) scl +=
classcap[i][r];
2939 cap[i] = std::min(sc, scl);
2953 const std::size_t K =
classes.size(), I =
nodes.size();
2954 std::vector<std::size_t> all(K);
2955 for (std::size_t r = 0; r < K; ++r) all[r] = r + 1;
2959 for (std::size_t a = 0; a < I; ++a)
2960 for (std::size_t b = 0; b < I; ++b)
rtnodes(a * K + r - 1, b * K + s - 1) = M(a, b);
2977 const std::size_t K =
classes.size();
2978 std::size_t cidx = 0;
2980 const std::size_t ci = kv.first;
2982 for (std::size_t r = 0; r < cp.
hitclass.size() && r < K; ++r) {
2994 for (std::size_t c = 0; c <
nchains; ++c) {
3012 for (std::size_t i = 0; i <
nstations; ++i)
3013 for (std::size_t r = 0; r <
nclasses; ++r) {
3015 if (
stations[i].nodetype == NodeType::Join) {
3018 std::numeric_limits<double>::infinity());
3030 const std::size_t mk =
markidx_of(i + 1, r + 1);
3031 if (mk > 0 && mk <= d.
Dmark.size()) {
3036 scv(i, r) = ms.second;
3062 for (std::size_t a = 0; a < m.
D0.rows(); ++a)
3063 for (std::size_t b = 0; b < m.
D0.cols(); ++b)
3064 m.
D0(a, b) += T(d.
D1(a, b) - m.
D1(a, b));
3084 std::vector<std::vector<bool>> cs(K, std::vector<bool>(K,
false));
3085 for (std::size_t r = 0; r < K; ++r) cs[r][r] =
true;
3086 for (
const auto& kv : (
Peff.empty() ?
P :
Peff)) {
3088 for (std::size_t a = 0; a < kv.second.rows() && !any; ++a)
3089 for (std::size_t b = 0; b < kv.second.cols() && !any; ++b)
3090 if (kv.second(a, b) > zero) any =
true;
3091 if (any) cs[kv.first.first - 1][kv.first.second - 1] =
true;
3101 for (std::size_t r = 0; r < K && r < C.
rows(); ++r)
3102 for (std::size_t s = 0; s < K && s < C.
cols(); ++s)
3103 if (C(r, s) > zero) cs[r][s] =
true;
3111 for (std::size_t r = 0; r < cp.
hitclass.size() && r < K; ++r) {
3125 for (std::size_t r = 0; r < row.size() && r < K; ++r)
3126 if (row[r] != 0) cs[r][row[r] - 1] =
true;
3128 std::vector<std::size_t> comp(K, K);
3129 std::size_t ncomp = 0;
3130 for (std::size_t r = 0; r < K; ++r) {
3131 if (comp[r] != K)
continue;
3132 std::vector<std::size_t> stack{r};
3134 while (!stack.empty()) {
3135 const std::size_t v = stack.back();
3137 for (std::size_t w = 0; w < K; ++w)
3138 if (comp[w] == K && (cs[v][w] || cs[w][v])) {
3150 for (std::size_t r = 0; r < K; ++r) {
3151 chains[comp[r]][r] =
true;
3152 inchain[comp[r]].push_back(r + 1);
3157 for (std::size_t c = 0; c <
nchains; ++c)
3158 for (std::size_t k :
inchain[c])
3161 for (std::size_t c = 0; c <
nchains; ++c) {
3163 for (std::size_t k :
inchain[c])
3164 if (
classes[k - 1].refstat != rs)
3165 throw InputError(
"network '" +
name +
"': classes within a chain have different "
3166 "reference stations");
3175 for (std::size_t c = 0; c <
nchains; ++c) {
3210 for (
auto& kv :
P) {
3211 if (kv.second.rows() <
nodes.size())
continue;
3212 kv.second(
sinkNode - 1, src - 1) = zero;
3214 for (
auto& kv :
Peff) {
3215 if (kv.second.rows() <
nodes.size())
continue;
3216 kv.second(
sinkNode - 1, src - 1) = zero;
3218 for (std::size_t c = 0; c <
nchains; ++c) {
3220 for (std::size_t k :
inchain[c])
3221 if (std::isinf(
classes[k - 1].population)) open =
true;
3222 if (!open)
continue;
3224 for (std::size_t k :
inchain[c])
3228 for (std::size_t s :
inchain[c]) {
3235 if (!(p > zero))
continue;
3243 std::vector<std::size_t> v(
nodes.size());
3244 for (std::size_t i = 0; i <
nodes.size(); ++i) v[i] = i + 1;
3264 const std::vector<std::size_t>& ic =
inchain[c];
3265 const std::size_t nIC = ic.size();
3266 const std::size_t dim = sel.size() * nIC;
3273 std::vector<std::size_t> cpos(
nclasses + 1, nIC);
3274 for (std::size_t x = 0; x < nIC; ++x) cpos[ic[x]] = x;
3277 const std::size_t x = cpos[r], y = cpos[s];
3278 if (x == nIC || y == nIC)
return;
3279 for (std::size_t a = 0; a < sel.size(); ++a)
3280 for (std::size_t b = 0; b < sel.size(); ++b)
3281 Pc(a * nIC + x, b * nIC + y) = M(sel[a] - 1, sel[b] - 1);
3307 std::vector<bool> served(dim,
true);
3309 for (std::size_t a = 0; a < sel.size(); ++a) {
3310 const std::size_t sti =
nodes[sel[a] - 1].station;
3311 if (sti == 0 || sti >
stations.size())
continue;
3313 if (nt == NodeType::Place || nt == NodeType::Transition)
continue;
3314 if (!
stations[sti - 1].server_types.empty())
continue;
3315 if (sti >
disabled.size())
continue;
3322 for (std::size_t x = 0; x < nIC; ++x)
3324 served[a * nIC + x] =
false;
3326 for (std::size_t row = 0; row < dim; ++row)
3328 for (std::size_t col = 0; col < dim; ++col) Pc(row, col) = zero;
3329 for (std::size_t col = 0; col < dim; ++col)
3331 for (std::size_t row = 0; row < dim; ++row) Pc(row, col) = zero;
3334 std::vector<std::size_t> visited;
3335 std::vector<T> rowsum(dim, zero);
3336 for (std::size_t row = 0; row < dim; ++row) {
3338 for (std::size_t col = 0; col < dim; ++col) s += Pc(row, col);
3340 if (s > zero) visited.push_back(row);
3342 bool oversum =
false;
3344 for (std::size_t row = 0; row < dim; ++row) {
3348 for (std::size_t col = 0; col < dim; ++col)
3349 Pc(row, col) = T(Pc(row, col) / rowsum[row]);
3379 bool reducible =
false;
3380 if (!fork && !visited.empty()) {
3381 Matrix<T> Pv0(visited.size(), visited.size(), zero);
3382 for (std::size_t a = 0; a < visited.size(); ++a)
3383 for (std::size_t b = 0; b < visited.size(); ++b) Pv0(a, b) = Pc(visited[a], visited[b]);
3385 std::size_t nrec = 0;
3388 reducible = (nrec > 1) && mc::detail::weak_components(Pv0).size() == 1;
3397 std::vector<std::size_t> nconn(sel.size(), 0);
3398 std::vector<std::vector<bool>> conn(sel.size(), std::vector<bool>(sel.size(),
false));
3399 for (std::size_t a = 0; a < sel.size(); ++a)
3400 for (std::size_t b = 0; b < sel.size(); ++b) {
3402 for (std::size_t x = 0; x < nIC && !cbit; ++x)
3403 for (std::size_t y = 0; y < nIC && !cbit; ++y)
3404 if (Pc(a * nIC + x, b * nIC + y) > zero) cbit =
true;
3406 if (cbit) ++nconn[a];
3408 for (std::size_t a = 0; a < sel.size(); ++a) {
3409 if (nconn[a] == 0)
continue;
3410 for (std::size_t x = 0; x < nIC; ++x) {
3412 for (std::size_t col = 0; col < dim; ++col) outsum += Pc(a * nIC + x, col);
3413 if (outsum > zero)
continue;
3416 for (std::size_t b = 0; b < sel.size(); ++b)
3417 if (conn[a][b]) Pc(a * nIC + x, b * nIC + x) = p;
3429 for (std::size_t row = 0; row < dim; ++row)
3431 for (std::size_t col = 0; col < dim; ++col) Pc(row, col) = zero;
3432 for (std::size_t col = 0; col < dim; ++col)
3434 for (std::size_t row = 0; row < dim; ++row) Pc(row, col) = zero;
3436 for (std::size_t row = 0; row < dim; ++row) {
3438 for (std::size_t col = 0; col < dim; ++col) s += Pc(row, col);
3439 if (s > zero) visited.push_back(row);
3443 std::vector<T> alpha(dim, zero);
3444 if (!visited.empty()) {
3445 Matrix<T> Pv(visited.size(), visited.size(), zero);
3446 for (std::size_t a = 0; a < visited.size(); ++a)
3447 for (std::size_t b = 0; b < visited.size(); ++b)
3448 Pv(a, b) = Pc(visited[a], visited[b]);
3455 bool allzero =
true, hasnan =
false;
3456 for (
const T& x : av) {
3457 if (x != zero) allzero =
false;
3460 if (allzero || hasnan) ok =
false;
3461 }
catch (
const Error&) {
3469 for (std::size_t a = 0; a < visited.size(); ++a) alpha[visited[a]] = av[a];
3489 for (std::size_t a = 0; a < visited.size(); ++a)
3490 if (!scc.
recurrent[scc.
scc[a] - 1]) alpha[visited[a]] = zero;
3494 if (fork && oversum) {
3495 for (std::size_t idx = 0; idx < dim; ++idx) {
3497 const std::size_t nd = sel[idx / nIC];
3498 if (!complement &&
nodes[nd - 1].nodetype == NodeType::Join) {
3505 const std::size_t r = ic[idx % nIC];
3506 std::size_t nsrc = 0;
3507 for (std::size_t src = 1; src <=
nodes.size(); ++src)
3508 for (std::size_t q = 1; q <=
nclasses; ++q)
3520 for (std::size_t a = 0; a < sel.size(); ++a)
3521 for (std::size_t x = 0; x < nIC; ++x) out(a, ic[x] - 1) = alpha[a * nIC + x];
3524 const std::size_t rstat =
classes[ic[0] - 1].refstat;
3526 std::size_t refrow = sel.size();
3527 for (std::size_t a = 0; a < sel.size(); ++a)
3528 if (sel[a] == refnode) refrow = a;
3529 if (refrow < sel.size()) {
3531 for (std::size_t x = 0; x < nIC; ++x) norm += out(refrow, ic[x] - 1);
3533 for (std::size_t a = 0; a < sel.size(); ++a)
3534 for (std::size_t x = 0; x < nIC; ++x)
3535 out(a, ic[x] - 1) = T(out(a, ic[x] - 1) / norm);
3537 for (std::size_t a = 0; a < sel.size(); ++a)
3538 for (std::size_t x = 0; x < nIC; ++x)
3539 if (out(a, ic[x] - 1) < zero) out(a, ic[x] - 1) = T(-out(a, ic[x] - 1));
3553 const std::vector<std::size_t>& ic, std::size_t refnode)
const {
3555 const std::size_t nIC = ic.size();
3556 const std::size_t dim = Pc_in.
rows();
3559 for (std::size_t row = 0; row < dim; ++row) {
3561 std::size_t nnan = 0;
3562 for (std::size_t col = 0; col < dim; ++col) {
3565 else nonnan = T(nonnan + Pc(row, col));
3567 if (nnan == 0)
continue;
3570 for (std::size_t col = 0; col < dim; ++col) {
3572 if (v != v) Pc(row, col) = fill;
3575 std::vector<std::size_t> visited;
3576 for (std::size_t row = 0; row < dim; ++row) {
3578 for (std::size_t col = 0; col < dim; ++col) s += Pc(row, col);
3579 if (s > zero) visited.push_back(row);
3581 std::vector<T> alpha(dim, zero);
3582 if (!visited.empty()) {
3583 Matrix<T> Pv(visited.size(), visited.size(), zero);
3584 for (std::size_t a = 0; a < visited.size(); ++a)
3585 for (std::size_t b = 0; b < visited.size(); ++b) Pv(a, b) = Pc(visited[a], visited[b]);
3591 bool allzero =
true, hasnan =
false;
3592 for (
const T& x : av) {
3593 if (x != zero) allzero =
false;
3596 if (allzero || hasnan) ok =
false;
3597 }
catch (
const Error&) {
3601 for (std::size_t a = 0; a < visited.size(); ++a) alpha[visited[a]] = av[a];
3604 for (std::size_t a = 0; a < sel.size(); ++a)
3605 for (std::size_t x = 0; x < nIC; ++x) out(a, ic[x] - 1) = alpha[a * nIC + x];
3606 std::size_t refrow = sel.size();
3607 for (std::size_t a = 0; a < sel.size(); ++a)
3608 if (sel[a] == refnode) refrow = a;
3609 if (refrow < sel.size()) {
3611 for (std::size_t x = 0; x < nIC; ++x) norm += out(refrow, ic[x] - 1);
3613 for (std::size_t a = 0; a < sel.size(); ++a)
3614 for (std::size_t x = 0; x < nIC; ++x)
3615 out(a, ic[x] - 1) = T(out(a, ic[x] - 1) / norm);
3617 for (std::size_t a = 0; a < sel.size(); ++a)
3618 for (std::size_t x = 0; x < nIC; ++x)
3619 if (out(a, ic[x] - 1) < zero) out(a, ic[x] - 1) = T(-out(a, ic[x] - 1));
3635 if (nd.nodetype == NodeType::Fork)
3636 throw UnsupportedError(
"da_recompute_visits_from_rtnodes: fork models keep the "
3637 "route_eff visit path");
3638 std::vector<std::size_t> keep;
3639 keep.reserve(M * K);
3640 for (std::size_t p = 0; p < M; ++p) {
3642 for (std::size_t r = 0; r < K; ++r) keep.push_back(base + r);
3646 std::vector<std::size_t> allnodes(I);
3647 for (std::size_t a = 0; a < I; ++a) allnodes[a] = a + 1;
3650 for (std::size_t c = 0; c <
nchains; ++c) {
3651 const std::vector<std::size_t>& ic =
inchain[c];
3652 const std::size_t nIC = ic.size();
3655 for (std::size_t a = 0; a < M; ++a)
3656 for (std::size_t x = 0; x < nIC; ++x)
3657 for (std::size_t b = 0; b < M; ++b)
3658 for (std::size_t y = 0; y < nIC; ++y)
3659 Ps(a * nIC + x, b * nIC + y) =
rt(a * K + (ic[x] - 1), b * K + (ic[y] - 1));
3660 const std::size_t rstat =
classes[ic[0] - 1].refstat;
3667 for (std::size_t a = 0; a < I; ++a)
3668 for (std::size_t x = 0; x < nIC; ++x)
3669 for (std::size_t b = 0; b < I; ++b)
3670 for (std::size_t y = 0; y < nIC; ++y)
3671 Pn(a * nIC + x, b * nIC + y) =
3672 rtnodes(a * K + (ic[x] - 1), b * K + (ic[y] - 1));
3686 const std::size_t nIC = ic.size();
3687 const std::size_t I =
nodes.size();
3689 std::vector<std::size_t> pos(
classes.size() + 1, 0);
3690 for (std::size_t x = 0; x < nIC; ++x) pos[ic[x]] = x + 1;
3692 if (r >= pos.size() || s >= pos.size() || pos[r] == 0 || pos[s] == 0)
return;
3693 const std::size_t x = pos[r] - 1, y = pos[s] - 1;
3694 for (std::size_t a = 0; a < I; ++a)
3695 for (std::size_t b = 0; b < I; ++b) full(a * nIC + x, b * nIC + y) = M(a, b);
3706 const std::map<std::pair<std::size_t, std::size_t>,
Matrix<T>>& src =
Peff.empty() ?
P :
Peff;
3707 for (
const auto& kv : src)
3708 if (kv.second.rows() >=
nodes.size() && kv.second.cols() >=
nodes.size())
3709 f(kv.first.first, kv.first.second, kv.second);
3728 const std::size_t I =
nodes.size();
3729 std::vector<std::size_t> keep, drop;
3730 for (std::size_t a = 0; a < I; ++a) {
3731 const bool st =
nodes[a].stateful;
3732 for (std::size_t x = 0; x < nIC; ++x) (st ? keep : drop).push_back(a * nIC + x);
3734 if (drop.empty())
return full;
3739 std::vector<char> isdrop(full.
rows(), 0), seen(full.
rows(), 0);
3740 for (std::size_t d : drop) isdrop[d] = 1;
3741 std::vector<std::size_t> stack;
3742 for (std::size_t k : keep)
3743 for (std::size_t d : drop)
3744 if (!seen[d] && full(k, d) != zero) { seen[d] = 1; stack.push_back(d); }
3745 while (!stack.empty()) {
3746 const std::size_t u = stack.back();
3748 for (std::size_t d : drop)
3749 if (!seen[d] && full(u, d) != zero) { seen[d] = 1; stack.push_back(d); }
3751 std::vector<std::size_t> reach;
3752 for (std::size_t d : drop)
3753 if (seen[d]) reach.push_back(d);
3757 Matrix<T> S(keep.size(), keep.size(), zero);
3758 for (std::size_t a = 0; a < keep.size(); ++a)
3759 for (std::size_t b = 0; b < keep.size(); ++b) S(a, b) = full(keep[a], keep[b]);
3763 const std::size_t nk = keep.size(), nd = drop.size();
3764 Matrix<T> P11(nk, nk, zero), P12(nk, nd, zero), P21(nd, nk, zero), A(nd, nd, zero);
3765 for (std::size_t a = 0; a < nk; ++a) {
3766 for (std::size_t b = 0; b < nk; ++b) P11(a, b) = full(keep[a], keep[b]);
3767 for (std::size_t b = 0; b < nd; ++b) P12(a, b) = full(keep[a], drop[b]);
3769 for (std::size_t a = 0; a < nd; ++a) {
3770 for (std::size_t b = 0; b < nk; ++b) P21(a, b) = full(drop[a], keep[b]);
3771 for (std::size_t b = 0; b < nd; ++b)
3772 A(a, b) = T((a == b ? one : zero) - full(drop[a], drop[b]));
3776 std::vector<std::size_t> piv(nd);
3777 for (std::size_t i = 0; i < nd; ++i) piv[i] = i;
3778 for (std::size_t col = 0; col < nd; ++col) {
3779 std::size_t best = col;
3781 for (std::size_t r2 = col + 1; r2 < nd; ++r2) {
3783 if (v > bv) { bv = v; best = r2; }
3786 for (std::size_t b = 0; b < nd; ++b) std::swap(A(col, b), A(best, b));
3787 for (std::size_t b = 0; b < nk; ++b) std::swap(X(col, b), X(best, b));
3789 if (A(col, col) == zero)
3790 throw NumericError(
"network: the stochastic complement is singular");
3792 std::vector<std::size_t> nzA, nzX;
3793 for (std::size_t b = 0; b < nd; ++b)
3794 if (A(col, b) != zero) nzA.push_back(b);
3795 for (std::size_t b = 0; b < nk; ++b)
3796 if (X(col, b) != zero) nzX.push_back(b);
3797 for (std::size_t r2 = 0; r2 < nd; ++r2) {
3798 if (r2 == col)
continue;
3799 const T f = T(A(r2, col) / A(col, col));
3800 if (f == zero)
continue;
3801 for (std::size_t b : nzA) A(r2, b) = T(A(r2, b) - f * A(col, b));
3802 for (std::size_t b : nzX) X(r2, b) = T(X(r2, b) - f * X(col, b));
3805 for (std::size_t r2 = 0; r2 < nd; ++r2)
3806 for (std::size_t b = 0; b < nk; ++b) X(r2, b) = T(X(r2, b) / A(r2, r2));
3810 std::vector<T> acc(nk, zero);
3811 for (std::size_t a = 0; a < nk; ++a) {
3812 std::fill(acc.begin(), acc.end(), zero);
3813 for (std::size_t m = 0; m < nd; ++m) {
3814 const T w = P12(a, m);
3815 if (w == zero)
continue;
3816 for (std::size_t b = 0; b < nk; ++b) acc[b] += w * X(m, b);
3818 for (std::size_t b = 0; b < nk; ++b) S(a, b) = T(S(a, b) + acc[b]);
3827 if (std::isinf(c.population))
return true;
3839 if (!std::isinf(c.population))
return false;
3845 if (std::isfinite(s.nservers) && s.nservers > 1.0)
return true;
3851 if (std::isfinite(c.population) && c.population != std::floor(c.population + 0.5))
3860 if (c.prio > 0)
return true;
3873 if (
classes.empty())
return false;
3875 if (c.prio !=
classes.front().prio)
return true;
3891 case SchedStrategy::HOL:
3892 case SchedStrategy::PSPRIO:
3893 case SchedStrategy::DPSPRIO:
3894 case SchedStrategy::GPSPRIO:
3895 case SchedStrategy::LCFSPRIO:
3896 case SchedStrategy::LCFSPRPRIO:
3897 case SchedStrategy::LCFSPIPRIO:
3898 case SchedStrategy::FCFSPRPRIO:
3899 case SchedStrategy::FCFSPIPRIO:
3900 case SchedStrategy::SRPTPRIO:
3930 for (std::size_t i = 0; i <
nstations; ++i) {
3931 if (
stations[i].sched != SchedStrategy::FCFS)
continue;
3934 for (std::size_t r = 0; r <
nclasses; ++r) {
3937 lo = hi =
rates(i, r);
3954 if (!(s.sched == SchedStrategy::INF || s.sched == SchedStrategy::PS ||
3955 s.sched == SchedStrategy::FCFS || s.sched == SchedStrategy::LCFSPR ||
3956 s.sched == SchedStrategy::LCFS || s.sched == SchedStrategy::EXT))
3968 for (std::size_t i = 0; i <
nstations; ++i) {
3969 if (
stations[i].sched != SchedStrategy::FCFS)
continue;
3970 for (std::size_t r = 0; r <
nclasses; ++r) {
3972 if (std::isinf(v) || !(v > 0.0))
continue;
3986 double k = std::numeric_limits<double>::infinity();
3987 if (
cap[ist - 1] >= 0.0) k = std::min(k,
cap[ist - 1]);
3989 bool anyServed =
false;
3990 for (std::size_t r = 0; r <
nclasses; ++r)
3995 if (anyServed) k = std::min(k, ccap);
3996 double reachable = 0.0;
3997 for (std::size_t r = 0; r <
nclasses; ++r)
3998 if (!anyServed ||
classcap[ist - 1][r] > 0.0) reachable +=
classes[r].population;
3999 return (k >= reachable) ? std::numeric_limits<double>::infinity() : k;
4005 for (std::size_t k = 0; k <
classes.size(); ++k)
4006 if (std::isfinite(
classes[k].population))
return false;
4007 std::size_t nq = 0, nsrc = 0, nsink = 0, qnode = 0, snode = 0;
4008 for (std::size_t a = 0; a <
nodes.size(); ++a) {
4009 switch (
nodes[a].nodetype) {
4010 case NodeType::Queue: ++nq; qnode = a + 1;
break;
4011 case NodeType::Source: ++nsrc; snode = a + 1;
break;
4012 case NodeType::Sink: ++nsink;
break;
4013 default:
return false;
4016 if (nq != 1 || nsrc != 1 || nsink != 1)
return false;
4017 const std::size_t qist =
nodes[qnode - 1].station;
4018 const std::size_t sist =
nodes[snode - 1].station;
4019 if (qist == 0 || sist == 0)
return false;
4020 if (
stations[qist - 1].nservers != 1.0)
return false;
4022 droprule[qist - 1][0] != DropStrategy::DROP)
4024 if (!(qist <=
cap.size()) || !std::isfinite(
cap[qist - 1]) || !(
cap[qist - 1] > 0.0))
4046 if (!
regions.empty())
return true;
4047 for (std::size_t a = 0; a <
nodes.size(); ++a)
4048 if (
nodes[a].nodetype == NodeType::Cache)
return false;
4050 for (std::size_t ist = 1; ist <=
stations.size(); ++ist)
4081 if (!(s.sched == SchedStrategy::INF || s.sched == SchedStrategy::PS ||
4082 s.sched == SchedStrategy::FCFS || s.sched == SchedStrategy::LCFSPR ||
4083 s.sched == SchedStrategy::EXT))
4086 for (std::size_t i = 0; i <
nstations; ++i) {
4087 if (
stations[i].sched != SchedStrategy::FCFS)
continue;
4088 for (std::size_t r = 0; r <
nclasses; ++r) {
4091 if (!std::isinf(v) && v > 0.0 &&
4095 if (!check_means ||
visits.empty())
continue;
4097 double stmin = 0.0, stmax = 0.0;
4098 bool anyserved =
false;
4099 for (std::size_t c = 0; c <
nchains && c <
visits.size(); ++c) {
4100 double num = 0.0, den = 0.0;
4101 for (std::size_t r = 0; r <
nclasses; ++r) {
4112 const double st = num / den;
4117 stmin = std::min(stmin, st);
4118 stmax = std::max(stmax, st);
4134 for (std::size_t a = 0; a < B.
rows(); ++a)
4135 for (std::size_t b = 0; b < B.
cols(); ++b) g(a, b) = B(a, b);
4143 if (std::isfinite(c.population)) s += c.population;
4166 const std::size_t R =
sn.nclasses;
4170 if (st.
sched != SchedStrategy::PAS)
return Matrix<T>(R, R, zero);
4172 for (std::size_t r = 0; r < R; ++r) g(r, r) = zero;
4181 for (std::size_t a = 0; a < g.
rows(); ++a)
4182 for (std::size_t b = 0; b < g.
cols(); ++b)
4183 if (g(a, b) != zero)
return false;
Base error for the multiprecision C++ port.
NumericError(const std::string &what)
UnsupportedError(const std::string &what)
A network plus its refreshed NetworkStruct.
void set_route(std::size_t r, std::size_t s, std::size_t i, std::size_t j, const T &p)
P{r,s}(i,j) = p, with 1-based NODE and class indices.
std::size_t sourceIdx
1-based station index of the Source, 0 = none
bool has_multi_class_heter_fcfs() const
bool has_distinct_priorities() const
Whether the classes carry more than one priority level.
std::size_t nvars_of(std::size_t ind) const
Total local-variable width of node ind (1-based).
pfqn::SdrStruct sdr_nodes
Node-indexed twin of sdr.
std::size_t add_class(const JobClass &cl)
Add a class and grow the service table.
T get_route(std::size_t r, std::size_t s, std::size_t i, std::size_t j) const
P{r,s}(i,j), AS THE USER SET IT.
std::vector< Matrix< T > > nodevisits
(nchains) each (nnodes x nclasses)
void refresh_rates()
Port of MNetwork.refreshRates: lower each service process onto a rate and an SCV.
Matrix< T > chain_visits(std::size_t c, const std::vector< std::size_t > &sel, bool complement, bool fork) const
Port of the per-chain body of sn_refresh_visits, over an arbitrary node subset – the stateful nodes f...
std::size_t stateful_index(std::size_t ind) const
1-based stateful index of node ind, 0 when the node is not stateful.
void refresh_cacheqn_actual_visits(const Matrix< T > &hitprob, const Matrix< T > &missprob)
Re-resolve the cache read self-switch from the offered 1/2-1/2 to the ACTUAL hit/miss probabilities t...
bool has_immediate_feedback() const
Whether immediate feedback is EFFECTIVE anywhere in the model.
bool has_class_switching() const
std::vector< lang::RemovalPolicy > signalrempolicy
std::vector< std::vector< bool > > immfeed
sn.immfeed: (nstations x nclasses) IMMEDIATE FEEDBACK, the reference's refreshStruct field.
const ForkParam< T > * fork_param_of(std::size_t node) const
The fork's override block, or null when it declares none.
std::size_t stateful_of_station(std::size_t st) const
std::vector< std::size_t > downstream_stations(std::size_t ind) const
The nodes directly downstream of ind, walking THROUGH stateless nodes and stopping at the first stati...
std::size_t nof_nodes() const
std::size_t nof_stateful() const
bool has_rr_routing() const
Whether ANY (node, class) pair dispatches round-robin.
std::map< std::pair< std::size_t, std::size_t >, Matrix< T > > P
P[(r,s)] is an (nnodes x nnodes) block; absent means all zero.
std::size_t phases_of(std::size_t ist, std::size_t r) const
sn.phases(i,r): the order of the process representation.
std::map< std::size_t, TransitionParam< T > > transparam
Transition (SPN) parameters, keyed by 1-based node index.
std::size_t mark_carrier_of(std::size_t ist) const
The CARRIER of station i's marked arrival: the class of mark 1, whose phase block holds the one modul...
std::vector< std::vector< bool > > replyblock
sn.replyblock (nnodes x nclasses) and sn.syncreply (nclasses).
std::vector< std::size_t > all_nodes() const
Every node index, 1-based, for the node-level visit computation.
std::vector< std::vector< bool > > isbasdestination
sn.isbasdestination, (nstations x nclasses): true where a refusal at this station must BLOCK an upstr...
bool is_mm1k_loss() const
Implementation of api::sn_is_mm1k_loss; that free function delegates here.
void for_each_route_block(F f) const
Visit every (r, s) block of the routing in force, the one route_eff reads: Peff when the expansion ra...
bool has_sdr_routing() const
MATLAB's any(sn.isstatedep(:,3)) NARROWED TO THE ONE STRATEGY THIS PORT EVALUATES PER STATE: Krzesins...
std::vector< Reward > reward
std::vector< std::size_t > stateful_nodes
1-based node indices, ascending
bool sched_has_priority_aware() const
Whether some station runs a discipline that READS the class priorities.
bool holds_reply_for(std::size_t ind, std::size_t r) const
True when node ind (1-based) holds a server across a synchronous call whose reply class is r (1-based...
void refresh_replyblock()
sn.replyblock, DERIVED from sn.syncreply and the routing.
std::vector< std::vector< Distrib< T > > > service
service[i][r], 0-based station and class; a disabled entry marks a pair never visited.
void refresh_chains()
Port of MNetwork.refreshChains followed by sn_refresh_visits.
std::vector< std::vector< bool > > chains
(nchains x nclasses)
bool has_homogeneous_scheduling(SchedStrategy) const
Port of sn_has_homogeneous_scheduling.
void apply_sink_closure()
Route every open chain from the Sink back into the Source, as the tail of MATLAB's getRoutingMatrix d...
std::vector< std::size_t > refclass
(nchains) 1-based class, 0 = none
int gdscalingcutoff
sn.gdscalingcutoff: the per-slot OPEN-class truncation used when gdscaling is materialized onto the J...
std::map< std::size_t, SetupDelayOffParam< T > > setupparam
Setup / delay-off, keyed by 1-based STATION index.
std::map< std::size_t, BreakdownParam< T > > breakdownparam
Server breakdown / repair, keyed by 1-based STATION index.
bool has_exponential_fcfs() const
BCMP type 1 asks the FCFS service to be exponential.
void check_service_reachable() const
Refuses a class that is ROUTED TO a station which cannot serve it.
bool has_breakdown() const
Any station at all, the guard every solver gate needs first.
GdScaling< T > gdscaling
sn.gdscaling: the network-level globally state-dependent (Whittle) rate scaling phi(n).
std::vector< bool > fjauxclass
sn.fjauxclass: (nclasses+1, 1-based) true where the class is an MMT AUXILIARY OPEN class,...
std::vector< std::vector< std::size_t > > nvars
sn.nvars, (nnodes x 3R+1): the LOCAL VARIABLE columns each node appends to its state,...
bool has_service_law(std::size_t i, std::size_t r) const
Does (station i, class r) have a service law an analyzer may convert?
std::vector< std::vector< bool > > disabled
Matrix< T > visits_from_block(const Matrix< T > &Pc_in, const std::vector< std::size_t > &sel, const std::vector< std::size_t > &ic, std::size_t refnode) const
The visit ratios of one chain from an already-formed chain routing block Pc (dim = sel....
pfqn::SdrStruct sdr
Krzesinski (1987) product-form state-dependent routing, in 0-based STATION indices; empty unless a no...
std::vector< std::vector< bool > > reached_node_classes() const
The (node, class) pairs a job can actually ARRIVE at, 0-based on both axes.
void refresh_sched_param()
Port of MNetwork.refreshScheduling's schedparam half.
std::map< std::size_t, FjJoinParam > fjjoinparam
sn.nodeparam{j}.fj for each Join node: the tag matrix and the required sibling multiplicity after_eve...
bool serves_class(std::size_t ind, std::size_t r) const
Whether a job of class r (0-based) can LEAVE node ind (1-based) again.
double total_jobs() const
Total population, as MATLAB's getNumberOfJobs summed.
Matrix< T > rt
sn.rt and sn.rtnodes: the class-expanded routing.
const CtmcMapc< T > * mapc_of(std::size_t ist, std::size_t r) const
The pair form of class r (1-based) at station ist (1-based), or null when there is none.
std::map< std::size_t, RetrialParam< T > > retrialparam
Retrial parameters, keyed by 1-based STATION index.
void promote_stateful(std::size_t ind)
Port of MNetwork.refreshLocalVars: the per-node local-variable widths.
std::vector< bool > issignal
void grow_routing()
Grow every routing block to the current node count.
std::map< std::size_t, CacheParam< T > > nodeparam
Cache parameters by 1-based NODE index; only Cache nodes have an entry.
void grow_block(Matrix< T > &B) const
std::size_t map_var_slot(std::size_t ind, std::size_t r) const
0-based column of class r's MAP memory inside node ind's local-variable block.
bool has_priorities() const
std::size_t add_node(const std::string &nm, NodeType ty, bool stateful)
Add a non-station node (a Fork, a Router).
std::map< std::size_t, PasParam > pasparam
std::vector< double > cap
sn.cap and sn.classcap: the total and per-class buffers.
std::map< std::pair< std::size_t, std::size_t >, CtmcMapc< T > > ctmcmapc
MAP services in CTMC pair form, keyed by (1-based station, 1-based class).
std::vector< JobClass > classes
void rr_advance(std::size_t ind, std::size_t r, std::vector< T > &varrow) const
Advance the pointer of (ind, r) in VARROW by one position, cyclically.
std::vector< bool > isbasblocking
sn.isbasblocking, per NODE: true where the node is the BLOCKING (upstream) side of a true-BAS relatio...
bool has_product_form_not_het_fcfs(bool check_means=true) const
Port of sn_has_product_form_not_het_fcfs: LCFS is excluded, and at FCFS the service must be exponenti...
std::size_t sinkNode
1-based NODE index of the Sink, 0 = none (it is not a station)
std::map< std::size_t, JoinDecl > joindecl
std::vector< std::size_t > signaltarget
void refresh_local_vars()
ProcessType procid(std::size_t ist, std::size_t r) const
sn.procid(i,r): the process type of a (station, class) pair.
std::size_t rr_dest(std::size_t ind, std::size_t r, const std::vector< T > &varrow) const
The destination node the pointer in VARROW names, or 0 when (ind, r) does not dispatch round-robin or...
PollingParam effective_polling(std::size_t ist) const
The polling controller of station ist, from whichever API declared it.
std::vector< T > gdscalingpeak
sn.gdscalingpeak: the declared (nstations x nclasses) peak of gdscaling, row-major,...
std::size_t rr_var_slot(std::size_t ind, std::size_t r) const
1-BASED index of the pointer of (ind, r) INSIDE the node's local-variable block, or 0 when that pair ...
void refresh_bas_blocking()
Port of refreshLocalVars' true-BAS block and its declaresBlockedMarker helper: which nodes carry the ...
std::vector< Station< T > > stations
stations[k-1] is the k-th station
std::vector< std::size_t > rr_outlinks(std::size_t ind, std::size_t r) const
ROUND-ROBIN DISPATCH, the state that makes it deterministic.
Matrix< T > rates
(nstations x nclasses) service rates and SCVs, with a PARALLEL disabled flag instead of MATLAB's NaN ...
void refresh_struct()
The whole chain, in MATLAB's refreshStruct order.
T route_eff(std::size_t r, std::size_t s, std::size_t i, std::size_t j) const
The routing actually in force: the expansion when there is one, else P.
bool is_queueing_place(std::size_t i) const
Is station i (0-based) a QUEUEING PLACE, i.e.
void set_service(std::size_t station, std::size_t cls, const Distrib< T > &d)
std::vector< std::size_t > syncreply
std::map< std::pair< std::size_t, std::size_t >, Matrix< T > > Peff
The routing after refresh_routing() has expanded the non-PROB strategies and folded the class switche...
std::vector< std::vector< std::size_t > > inchain
1-based class indices per chain
std::size_t node_of_station(std::size_t st) const
1-based node index of a station, and the reverse; 0 when absent.
bool has_multi_server() const
void da_recompute_visits_from_rtnodes()
Recompute rt, visits and nodevisits after the caller has rewritten rtnodes in place – the cacheqn dri...
std::vector< NodeDef > nodes
every node, in creation order
std::vector< std::vector< double > > classcap
std::vector< std::size_t > rr_weighted_outlinks(std::size_t ind, std::size_t r) const
The WRROBIN cycle: each outlink repeated by its weight, weight 0 once.
std::map< std::size_t, Matrix< T > > csmatrix
The class-switch matrix of a ClassSwitch node, by 1-based NODE index.
std::vector< double > njobs() const
sn.njobs: the population of each class, infinite for an open one.
bool has_breakdown_node(std::size_t ind) const
sn.hasbreakdown(ind): does the NODE's server break down?
void refresh_router_stateful()
bool is_open_model() const
sn_is_open_model: EVERY class is open, which is not has_open_classes.
bool has_open_classes() const
bool sched_is_product_form() const
std::vector< std::vector< DropStrategy > > droprule
std::string log_path
Network.setLogPath / getLogPath: the directory every Logger writes into, and the logPath attribute of...
bool has_blocking() const
Some station can REFUSE a job: its own buffer BINDS, or a finite capacity region caps a set of statio...
void refresh_rt()
sn.rt and sn.rtnodes: the class-expanded routing matrices.
bool priorities_ignored() const
Priorities were declared and no station will read them.
std::vector< Matrix< T > > visits
(nchains) each (nstateful x nclasses)
double buffer_size(std::size_t ist) const
Kendall's K of station IST (1-based), +inf when unbounded.
std::map< std::size_t, std::vector< T > > initmarking
The DECLARED initial state of a stateful node, by 1-based node index.
std::pair< T, T > mark_marginal_moments(const Distrib< T > &d, std::size_t mk) const
Mean and SCV of ONE MARK'S MARGINAL MAP, MarkedMAP.toMAPs(k).
std::map< std::size_t, std::vector< T > > stateprior
std::map< std::size_t, CtmcPool< T > > ctmcpool
Heterogeneous server pools rewritten for the CTMC, by 1-based NODE index.
double nclosedjobs() const
sn.nclosedjobs: the total population of the closed classes.
std::vector< std::pair< std::size_t, std::size_t > > fj
fj(f,j): the Join node j that closes the Fork node f, 1-based.
std::size_t nof_stations() const
void set_route_effective(std::size_t r, std::size_t s, std::size_t i, std::size_t j, const T &p)
Write into the routing the consumers actually read.
std::size_t join_siblings(std::size_t joinNode, std::size_t r=0) const
Port of MNetwork.refreshCapacity.
std::size_t phasessz_of(std::size_t ist, std::size_t r) const
sn.phasessz(i,r) = max(sn.phases(i,r),1): THE WIDTH of class r's phase block in a state row,...
bool has_product_form() const
std::vector< lang::SignalType > signaltype
std::vector< std::size_t > fjclassmap
sn.fjclassmap: the ORIGINAL class of each auxiliary sibling class, 0 for an original class.
std::map< std::size_t, PollingParam > pollingparam
std::vector< std::size_t > quorum_joins() const
The 1-based Join nodes that fire on a STRICT quorum, i.e.
bool has_fractional_populations() const
std::map< std::size_t, Matrix< T > > statespace
std::size_t markidx_of(std::size_t ist, std::size_t r) const
sn.markidx(i,r): the 1-based MARK that class r carries in station i's marked arrival process,...
Matrix< T > stoch_comp_stateful(const Matrix< T > &full, std::size_t nIC) const
The stochastic complement of a NODE-level routing block over the stateful nodes, S = P11 + P12 (I - P...
std::size_t add_station(const Station< T > &st)
Add a station, which is also a node, and grow the service table.
std::vector< std::vector< T > > signalremdist
Matrix< T > station_routing(const std::vector< std::size_t > &ic) const
The chain-restricted routing over the STATEFUL nodes: the node-level routing with the non-stateful no...
bool isfjaugmented
sn.isfjaugmented: this struct came out of fj_tag, so its Fork nodes are STATEFUL and its Join nodes c...
std::vector< std::size_t > station_to_node
(nstations) 1-based node index
void refresh_routing()
Port of the part of MNetwork.refreshRoutingMatrix this port reaches: the expansion of a routing STRAT...
std::vector< Region > regions
std::size_t nof_classes() const
void refresh_immfeed()
Port of the sn.immfeed block of @@MNetwork/refreshStruct.m.
std::map< std::size_t, ForkParam< T > > forkparam
Variable forking levels, by 1-based Fork node; absent on a plain fork.
static std::string plural(long n, const std::string &singular, const std::string &plural_form)
Count with an agreeing noun, e.g.
static void compiling(const std::string &name)
Announce the compilation of a model structure.
static void compile_detail(const char *fmt,...)
One stage line of a structure compile: silenced inside a Quiet scope, and inside an open run,...
Equilibrium distribution of a discrete-time Markov chain, and stochastic complementation.
Limiting distribution of a discrete-time Markov chain whose transition matrix may be reducible.
Stochastic complement of a DTMC partition, a port of matlab/lib/kpctoolbox/mc/dtmc_stochcomp....
The exception types the port throws.
Enumerations and the minimal distribution descriptor shared by the model layer of the C++ port.
Running progress log of a LINE solver run (the "solver console").
Markovian arrival process descriptors: stationary vectors, rate, moments, autocorrelation and the ind...
Dense matrix and non-owning view.
SchedStrategy
Scheduling disciplines, with the values of MATLAB SchedStrategy.
DropStrategy
Blocking and loss rules, with the values of MATLAB DropStrategy.
BalkingStrategy
Balking rules, with the values of MATLAB BalkingStrategy.
JoinStrategy
Join rules, with the values of MATLAB JoinStrategy.
RoutingStrategy
Routing strategies, with the values of MATLAB RoutingStrategy.
HeteroSchedPolicy
How a heterogeneous station picks among its server types, MATLAB HeteroSchedPolicy.
PollingType
Polling service disciplines, with the values of MATLAB PollingType.
@ KLIMITED
serve at most K per visit (K in pollingPar)
@ EXHAUSTIVE
serve until the queue empties
JobClassType
Job class kinds, with the values of MATLAB JobClassType.
ProcessType
Distribution kinds, with the values of MATLAB ProcessType.
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.
const char * routing_to_text(RoutingStrategy r)
NodeType
Node kinds, with the values of MATLAB NodeType.
ReplacementStrategy
Cache replacement policies, with the values of MATLAB ReplacementStrategy.
T map_mean(const Map< T > &m)
Mean inter-arrival time, 1/lambda.
T map_scv(const Map< T > &m)
Squared coefficient of variation.
ReducibleResult< T > dtmc_solve_reducible(const Matrix< T > &P, const std::vector< T > &pin, double zeroColTol=1e-12)
Limiting distribution of a discrete-time Markov chain whose transition matrix may be reducible.
Matrix< T > dtmc_stochcomp(const Matrix< T > &P, const std::vector< std::size_t > &keep)
Stochastic complement of a DTMC partition, a port of matlab/lib/kpctoolbox/mc/dtmc_stochcomp....
SccResult stronglyconncomp(const Matrix< T > &A)
Strongly connected components of a directed graph, and which of them are recurrent (closed under the ...
std::vector< T > dtmc_solve(const Matrix< T > &P)
Stationary distribution of a stochastic matrix P.
void cache_retrieval_class_map(const CacheParam< T > &cp, std::vector< std::size_t > &rc_list, std::vector< std::size_t > &rc_items, std::vector< std::size_t > &rc_orig)
Port of State.cacheRetrievalClassMap: the canonical order of a cache's retrieval classes,...
Matrix< T > station_swap_graph(const NetworkStruct< T > &sn, std::size_t ist)
The swap graph of a PAS / OI station, with the defaults refreshLocalVars.m installs applied.
bool station_swap_graph_is_zero(const NetworkStruct< T > &sn, std::size_t ist)
True when the station's materialized swap graph is entirely zero.
const char * routing_to_text(RoutingStrategy r)
Conservation laws of a layered queueing network, enumerated from its structure.
Number-type abstraction for the templated API port.
Product-form state-dependent routing.
Strongly connected components of a directed graph, and which of them are recurrent (closed under the ...
The DECLARED join rule of a Join node, by 1-based node index.
The G-network signal declaration, per CLASS.
The polling controller of a POLLING station, keyed by station index.
FINITE CAPACITY REGIONS, MATLAB's refreshRegions output.
sn.reward: the user-declared reward functions, MATLAB's model.setReward(name, fn).
Matrix< T > D0
The (D0,D1) pair when the type carries one directly.
std::vector< Matrix< T > > Dmark
MMAP per-class D1 blocks / BMAP per-batch-size blocks; empty otherwise.
T rate() const
The rate MATLAB's refreshRates would store: 1/mean, with the Immediate singleton short-circuited to i...
std::size_t phases() const
The order of the representation, MATLAB's sn.phases.
A MAP as the pair of matrices (D0, D1).
std::vector< bool > recurrent
recurrent[c-1] is true when component c has no edge leaving it.
std::vector< std::size_t > scc
Component index of each state, 1-based as in MATLAB (0 is never used).
Topology and coefficients of a state-dependent routing subnetwork.
Server breakdown and repair of a station whose server fails and is repaired.
T failure_rate
sn.breakdownMu: 1 / mean failure time
T down_rate_of(std::size_t cls_1based) const
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
T qlru
Delayed-hit retrieval system (Cache.setRetrievalSystem).
std::vector< Popularity > preadkind
per class, parallel to pread
std::vector< T > initstate
The DECLARED initial contents of the cache, as the reference dumps the node's state row: the per-clas...
std::vector< int > itemsize
Per-item storage cost (size) and per-list cap on the total cost of the resident items (ton21cache Sec...
std::vector< int > costcap
std::map< std::size_t, std::vector< std::size_t > > retrieval_queues
read class(0-based)->nodes
std::vector< std::vector< Matrix< T > > > accost
(u) x (n) of (h+1)x(h+1), or empty
long max_pending_retrieval
Truncation level of block B: how many secondary requests may be merged onto the in-flight fetches of ...
std::vector< std::vector< std::size_t > > retrieval_classes
(nitems x nclasses), 1-based
std::vector< int > itemcap
std::vector< std::size_t > missclass
std::vector< std::size_t > hitclass
lang::ReplacementStrategy replacestrat
std::vector< std::size_t > classitem
Item read by each per-item class of a cache network (MATLAB Cache.setItemReadClasses,...
std::vector< std::vector< T > > pread
(u) x (n), empty row = NaN
CTMC pair form of a MAP service at an FCFS station, written by ctmc::ctmc_mapc on the solver's privat...
Matrix< T > V
landing law (-D0)^-1 D1
std::size_t p
phases of the original MAP
std::vector< std::pair< std::size_t, std::size_t > > pairs
0-based (i,j), i-major, V(i,j) > 0
std::vector< T > done
completion rate D1(i,j)/V(i,j)
bool single
c = 1: carried phase, no pairs
CTMC bookkeeping of a heterogeneous-server station, written by ctmc::ctmc_pools on the solver's priva...
std::vector< std::vector< std::size_t > > len
offset and length of block k of class r
std::vector< std::vector< std::vector< T > > > alpha
entry vector of block k of class r
std::vector< std::vector< std::vector< T > > > exit
exit rates of block k of class r
std::vector< std::vector< bool > > compat
compat[t-1][r-1]
std::vector< double > count
servers per pool
std::vector< std::size_t > alfsorder
pools by ascending class count (ALFS)
std::vector< std::vector< Matrix< T > > > D0
D0 of block k of class r.
std::vector< std::vector< std::size_t > > pools
pools[r-1]: compatible pools of r, ascending
lang::HeteroSchedPolicy policy
bool rotate
ALIS/FAIRNESS order is part of the state.
std::vector< std::vector< std::size_t > > off
std::size_t perm_index(const std::vector< std::size_t > &order) const
1-based index of order in perms, 0 when absent.
std::vector< std::vector< double > > fsfrate
fsfrate[t-1][r-1] = 1/mean (FSF)
std::vector< std::vector< std::size_t > > perms
every pool order, identity first
static Distrib disabled_dist()
sn.nodeparam{j}.fj: what a Join node needs to fire on identity.
std::vector< std::size_t > origclasses
std::map< std::size_t, std::vector< std::vector< std::size_t > > > auxmatrix
std::map< std::size_t, std::vector< std::size_t > > required
One fork firing synchronization: sn.fjsync{k}.
std::size_t fork
1-based Fork node
std::vector< std::size_t > weightlink
Per-branch tasksPerLink, EMPTY when every branch carries weight.
std::size_t weight
tasksPerLink: siblings emitted per branch
std::vector< std::size_t > branchheads
1-based node per branch
std::size_t join
1-based Join node that closes it
std::size_t cls
1-based ORIGINAL class being forked
std::vector< std::vector< std::size_t > > auxall
(B x T) every auxiliary class of this (fork, class), for the tag scan.
std::vector< std::size_t > auxclasses
the tag's auxiliary class per branch
std::size_t tag
1-based tag this entry allocates
Variable forking levels, the twin of MATLAB sn.nodeparam{f}.fanOutLink / .fanOutProb / ....
std::vector< std::vector< lang::Distrib< T > > > fan_out_dist
static constexpr double Immediate
Rate of an Immediate distribution; its mean is 1/Immediate = 1e-8.
static constexpr double FineTol
static constexpr double Zero
static constexpr double CoarseTol
One job class of the network.
std::size_t refstat
1-based reference station
bool completes
Whether passage through the reference station is a COMPLETION.
double deadline
sn.classdeadline(r): the soft deadline EDD and EDF order by, and the tardiness JMT reports.
int attr_kind
LayeredNetworkElement of the element it stands for.
std::size_t attr_idx
index of that element
double population
infinite for an open class
bool immfeed
Class-level immediate feedback, ORed with the station's own setting into sn.immfeed.
std::size_t spawn
sn.classspawn(r): the 1-based class injected at the SAME station on every completion of this class,...
bool self_looping
A SelfLoopingClass: a closed class that perpetually cycles at its reference station.
bool is_ref_class
marks the chain's reference class
lang::JoinStrategy strategy
double quorum
0 = every sibling
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
std::vector< T > lincon_b
Matrix< T > lincon_A
optional linear constraint A n <= b
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
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,...
A Logger node's trace configuration, MATLAB's Logger properties and sn.nodeparam{ind}...
std::string file_name
base name, no directory
std::string file_path
directory, MATLAB's model.getLogPath
std::vector< int > routing_param
The scalar parameter of a parameterized dispatcher, per class: the d of a power-of-d (SQ) choice.
std::vector< std::map< std::size_t, double > > routing_weights
The per-destination weights of a WRROBIN dispatcher, per class: a map from 1-based destination NODE i...
bool queue_object
Declared as a Queue although its INF discipline makes nodetype Delay (Network::add_queue).
std::vector< RoutingStrategy > routing
sn.routing, per class.
double tasks_per_link
Fork.output.tasksPerLink == MATLAB sn.nodeparam{f}.fanOut: how many tasks a fork emits per outgoing l...
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.
Setup and delay-off of a station that powers down when it falls idle.
bool last(lang::Distrib< T > &su, lang::Distrib< T > &doff) const
The pair the solvers use: the last class that declares one.
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.
lang::BalkingStrategy strategy
std::vector< BalkingThreshold > thresholds
One balking threshold: with min_jobs <= n <= max_jobs at the station, an arriving job of the class re...
A heterogeneous server pool: count servers that serve only compatible classes, each with its own serv...
std::vector< bool > compatible
per class; empty = every class
std::vector< Distrib< T > > service
per class
One station of the network.
std::vector< Distrib< T > > orbit_impatience
Queue.setOrbitImpatience(class, dist): abandonment from the RETRIAL ORBIT, which is a different popul...
double cap
Station capacity in Kendall's K, as setCapacity sets it.
std::vector< T > jdscalingpeak
sn.jdscalingpeak for this station: the declared peak joint-dependent scaling per class.
std::vector< BalkingParam > balking
std::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< std::size_t > marked_classes
Source.markedClasses: the 1-based class of each mark of an MMAP arrival.
std::vector< T > lldscaling
sn.lldscaling for this station: the multiplier at population 1, 2, ... Empty when the station is not ...
std::vector< lang::ImpatienceType > impatience
std::vector< T > schedparam
sn.schedparam, per class: the DPS / GPS weight, or the SEPT / LEPT rank.
lang::HeteroSchedPolicy hetero_policy
CdScaling< T > cdscaling
sn.cdscaling for this station: the class-dependence map, empty when unset.
std::vector< double > classcap
Per-class buffer from setChainCapacity; infinite where unset.
std::vector< lang::DepartureDiscipline > departure_discipline
Place.departureDiscipline, per class.
std::vector< Distrib< T > > arrival_batch
Source.setArrivalBatch(class, dist): the batch-size law released at each arrival epoch.
std::vector< Distrib< T > > switchover
std::vector< bool > immfeed
Node-level immediate feedback, per class; empty when the station sets none.
std::vector< ServerType > server_types
std::size_t attr_idx
LQN element this station stands for.
The parameters of a Cache node, MATLAB's sn.nodeparam{ind} for a Cache.
static std::vector< T > inhibit_total(const std::vector< Matrix< T > > &a, std::size_t m)
The inhibiting THRESHOLD of one mode per place, class blind.
static std::vector< T > arc_total(const std::vector< Matrix< T > > &a, std::size_t m)
The arcs of one mode summed over classes, for a consumer that is class blind BECAUSE THE NET IS SINGL...
bool is_multiclass() const
True when any mode's arcs touch more than one class.
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::size_t single_class() const
The one class every arc of every mode touches, 1-based; 0 when none does.
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