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();
919 if (i >=
stations.size() ||
stations[i].nodetype != NodeType::Place)
return false;
920 for (std::size_t r = 0; r <
service[i].size(); ++r)
948 std::vector<std::pair<std::size_t, std::size_t>>
fj;
1024 const typename std::map<std::size_t, ForkParam<T> >::const_iterator it =
1043 std::function<T(
const std::vector<T>&)>
fn;
1080 std::vector<std::vector<double>>
cap;
1113 if (ind == 0 || ind >
nodes.size())
return false;
1114 const std::size_t ist =
nodes[ind - 1].station;
1226 std::vector<std::vector<std::size_t>>
nvars;
1332 if (ist == 0 || ist >
stations.size())
return pp;
1333 const typename std::map<std::size_t, PollingParam>::const_iterator it =
1358 if (ind == 0 || ind >
nvars.size())
return 0;
1360 for (std::size_t j = 0; j <
nvars[ind - 1].size(); ++j) w +=
nvars[ind - 1][j];
1371 std::vector<double> v;
1382 return service[ist - 1][r - 1].type;
1407 std::size_t
phases_of(std::size_t ist, std::size_t r)
const {
1444 const std::size_t p =
phases_of(ist, r);
1445 return p > 0 ? p : 1;
1457 if (ist == 0 || ist >
stations.size())
return 0;
1458 const std::vector<std::size_t>&
mc =
stations[ist - 1].marked_classes;
1459 for (std::size_t k = 0; k <
mc.size(); ++k)
1460 if (
mc[k] == r)
return k + 1;
1469 if (ist == 0 || ist >
stations.size())
return 0;
1470 const std::vector<std::size_t>&
mc =
stations[ist - 1].marked_classes;
1471 return mc.empty() ? 0 :
mc[0];
1475 if (n.nodetype == NodeType::Fork)
return true;
1491 if (rs == RoutingStrategy::SDR)
return true;
1510 std::vector<std::size_t>
rr_outlinks(std::size_t ind, std::size_t r)
const {
1511 std::vector<std::size_t> out;
1513 const std::size_t K =
classes.size(), I =
nodes.size();
1514 for (std::size_t j = 1; j <= I; ++j) {
1515 bool linked =
false;
1516 for (std::size_t s = 1; s <= K && !linked; ++s)
1517 if (
get_route(r, s, ind, j) > zero) linked =
true;
1518 if (linked) out.push_back(j);
1525 const std::vector<std::size_t> ol =
rr_outlinks(ind, r);
1526 const std::map<std::size_t, double>* w =
1527 (ind <=
nodes.size() &&
nodes[ind - 1].routing_weights.size() >= r)
1528 ? &
nodes[ind - 1].routing_weights[r - 1]
1530 std::vector<std::size_t> cycle;
1531 for (std::size_t d = 0; d < ol.size(); ++d) {
1534 const std::map<std::size_t, double>::const_iterator it = w->find(ol[d]);
1535 if (it != w->end() && it->second > 0.0)
1536 reps = std::lround(it->second) < 1 ? 1 : std::lround(it->second);
1538 for (
long q = 0; q < reps; ++q) cycle.push_back(ol[d]);
1553 const std::size_t R =
classes.size();
1554 if (ind == 0 || ind >
nodes.size() || r == 0 || r > R)
return 0;
1555 const std::vector<RoutingStrategy>& rt_i =
nodes[ind - 1].routing;
1556 if (rt_i.size() < r)
return 0;
1557 if (rt_i[r - 1] != RoutingStrategy::RROBIN && rt_i[r - 1] != RoutingStrategy::WRROBIN)
1559 if (
nvars.size() < ind)
return 0;
1560 std::size_t slot = 0;
1561 for (std::size_t j = 0; j < R + r && j <
nvars[ind - 1].size(); ++j)
1562 slot +=
nvars[ind - 1][j];
1572 std::size_t
rr_dest(std::size_t ind, std::size_t r,
const std::vector<T>& varrow)
const {
1574 if (slot == 0 || slot > varrow.size())
return 0;
1576 if (
nodes[ind - 1].routing[r - 1] == RoutingStrategy::RROBIN)
1577 return v > 0 ?
static_cast<std::size_t
>(v) : 0;
1579 if (v < 1 ||
static_cast<std::size_t
>(v) > cycle.size())
return 0;
1580 return cycle[
static_cast<std::size_t
>(v) - 1];
1589 void rr_advance(std::size_t ind, std::size_t r, std::vector<T>& varrow)
const {
1591 if (slot == 0 || slot > varrow.size())
return;
1592 if (
nodes[ind - 1].routing[r - 1] == RoutingStrategy::RROBIN) {
1593 const std::vector<std::size_t> ol =
rr_outlinks(ind, r);
1594 if (ol.empty())
return;
1596 std::size_t idx = ol.size();
1597 for (std::size_t d = 0; d < ol.size(); ++d)
1598 if (
static_cast<long>(ol[d]) == cur) { idx = d;
break; }
1599 const std::size_t nxt = (idx + 1 < ol.size()) ? idx + 1 : 0;
1604 if (cycle.empty())
return;
1606 const long nxt = (pos < 1 || static_cast<std::size_t>(pos) >= cycle.size()) ? 1 : pos + 1;
1614 if (rs == RoutingStrategy::RROBIN || rs == RoutingStrategy::WRROBIN)
return true;
1638 bool declared =
false;
1639 for (
const std::vector<bool>& row :
immfeed) {
1645 if (declared)
break;
1647 if (!declared)
return false;
1652 for (std::size_t ist = 1; ist <=
immfeed.size(); ++ist) {
1654 if (ind == 0 || ind >
nodes.size())
continue;
1656 if (isf == 0)
continue;
1657 for (std::size_t r = 1; r <=
immfeed[ist - 1].size() && r <= R; ++r) {
1658 if (!
immfeed[ist - 1][r - 1])
continue;
1659 const std::size_t col = (isf - 1) * R + (r - 1);
1660 if (col >=
rt.
cols())
continue;
1661 for (std::size_t sc = 0; sc < R; ++sc) {
1662 const std::size_t row = (isf - 1) * R + sc;
1690 throw InputError(
"network: a station is not a stateful node");
1702 nodes.push_back(nd);
1716 nodes.push_back(nd);
1719 return nodes.size();
1730 service[station - 1][cls - 1] = d;
1734 void set_route(std::size_t r, std::size_t s, std::size_t i, std::size_t j,
const T& p) {
1735 auto key = std::make_pair(r, s);
1736 auto it =
P.find(key);
1741 it->second(i - 1, j - 1) = p;
1757 auto key = std::make_pair(r, s);
1758 auto it =
Peff.find(key);
1759 if (it ==
Peff.end())
1762 if (it->second.rows() !=
nodes.size()) {
1764 for (std::size_t a = 0; a < it->second.rows(); ++a)
1765 for (std::size_t b = 0; b < it->second.cols(); ++b) g(a, b) = it->second(a, b);
1768 it->second(i - 1, j - 1) = p;
1779 T
get_route(std::size_t r, std::size_t s, std::size_t i, std::size_t j)
const {
1780 auto it =
P.find(std::make_pair(r, s));
1782 return it->second(i - 1, j - 1);
1786 T
route_eff(std::size_t r, std::size_t s, std::size_t i, std::size_t j)
const {
1788 auto it =
Peff.find(std::make_pair(r, s));
1790 return it->second(i - 1, j - 1);
1838 if (ind == 0 || ind >
replyblock.size())
return false;
1839 for (std::size_t k = 0; k <
syncreply.size(); ++k)
1857 if (ind == 0 || ind >
nodes.size())
return true;
1859 if (nt != NodeType::Queue && nt != NodeType::Delay)
return true;
1860 const std::size_t sti =
nodes[ind - 1].station;
1861 if (sti == 0 || sti >
stations.size())
return true;
1862 if (!
stations[sti - 1].server_types.empty())
return true;
1863 if (
procid(sti, r + 1) != ProcessType::DISABLED)
return true;
1881 std::vector<std::vector<bool>> reached;
1882 if (N == 0 || K == 0)
return reached;
1884 reached.assign(N, std::vector<bool>(K,
false));
1885 std::vector<std::vector<bool>> seed(N, std::vector<bool>(K,
false));
1886 for (std::size_t i = 0; i <
stations.size(); ++i) {
1887 if (
stations[i].nodetype != NodeType::Source)
continue;
1889 if (ind == 0 || ind > N)
continue;
1890 for (std::size_t r = 0; r < K; ++r)
1891 if (
procid(i + 1, r + 1) != ProcessType::DISABLED) seed[ind - 1][r] =
true;
1893 for (std::size_t r = 0; r < K && r <
classes.size(); ++r) {
1894 const double pop =
classes[r].population;
1895 if (!std::isfinite(pop) || pop <= 0.0)
continue;
1897 if (ind == 0 || ind > N)
continue;
1898 seed[ind - 1][r] =
true;
1900 std::vector<std::pair<std::size_t, std::size_t>> stack;
1901 for (std::size_t i = 0; i < N; ++i)
1902 for (std::size_t r = 0; r < K; ++r)
1904 reached[i][r] =
true;
1905 stack.push_back(std::make_pair(i, r));
1907 while (!stack.empty()) {
1908 const std::size_t i = stack.back().first;
1909 const std::size_t r = stack.back().second;
1912 if (nt == NodeType::Sink)
continue;
1913 if (nt == NodeType::Source && !seed[i][r])
continue;
1915 const std::size_t row = i * K + r;
1916 for (std::size_t col = 0; col < N * K; ++col) {
1920 const std::size_t j = col / K, sIdx = col % K;
1921 if (!reached[j][sIdx]) {
1922 reached[j][sIdx] =
true;
1923 stack.push_back(std::make_pair(j, sIdx));
1982 for (std::size_t ind = 0; ind <
nodes.size(); ++ind) {
1984 if (nt == NodeType::Cache || nt == NodeType::Place ||
1985 nt == NodeType::Transition || nt == NodeType::Fork ||
1986 nt == NodeType::Join)
1991 if (reached.empty())
return;
1992 for (std::size_t i = 0; i <
nstations; ++i) {
1994 if (nt != NodeType::Queue && nt != NodeType::Delay)
continue;
1998 if (!
stations[i].server_types.empty())
continue;
2000 if (ind == 0)
continue;
2001 for (std::size_t r = 0; r < K; ++r) {
2002 if (
procid(i + 1, r + 1) != ProcessType::DISABLED)
continue;
2008 if (ind - 1 >= reached.size() || r >= reached[ind - 1].size())
continue;
2009 if (!reached[ind - 1][r])
continue;
2010 const std::string kind = (nt == NodeType::Delay) ?
"Delay" :
"Queue";
2012 "' has no service configured for job class '" +
2014 "', but the class is routed to it. Jobs would arrive and "
2015 "never leave. Configure a service for that class, or route "
2032 immfeed.assign(M, std::vector<bool>(R,
false));
2033 for (std::size_t i = 0; i < M; ++i)
2034 for (std::size_t r = 0; r < R; ++r) {
2035 const bool station_has =
2071 if (ind == 0 || ind >
nodes.size() ||
nodes[ind - 1].stateful)
return;
2072 nodes[ind - 1].stateful =
true;
2078 for (std::size_t ind = 1; ind <=
nodes.size(); ++ind) {
2082 for (std::size_t r = 0; r < nd.
routing.size(); ++r)
2083 if (nd.
routing[r] == RoutingStrategy::RROBIN ||
2084 nd.
routing[r] == RoutingStrategy::WRROBIN)
2094 const std::size_t R =
classes.size();
2099 nvars.assign(
nodes.size(), std::vector<std::size_t>(3 * R + 1, 0));
2100 for (std::size_t ind = 1; ind <=
nodes.size(); ++ind) {
2101 const std::size_t ist =
nodes[ind - 1].station;
2105 for (std::size_t r = 1; r <= R; ++r) {
2107 if (pt == ProcessType::MAP || pt == ProcessType::MMPP2)
2108 nvars[ind - 1][r - 1] += 1;
2112 const std::vector<RoutingStrategy>& rt_i =
nodes[ind - 1].routing;
2113 for (std::size_t r = 1; r <= R && r <= rt_i.size(); ++r)
2114 if (rt_i[r - 1] == RoutingStrategy::RROBIN ||
2115 rt_i[r - 1] == RoutingStrategy::WRROBIN)
2116 nvars[ind - 1][R + r - 1] += 1;
2125 if (ist != 0 &&
stations[ist - 1].sched == SchedStrategy::POLLING) {
2128 for (std::size_t r = 0; r < pp.
switchover.size(); ++r) {
2130 if (!d.
disabled && d.
D0.rows() > 0 && d.
type != ProcessType::IMMEDIATE)
2133 std::size_t w = anysw ? 2 : 0;
2135 nvars[ind - 1][2 * R] = w;
2138 for (std::size_t r = 1; r <= R; ++r)
2141 nvars[ind - 1][2 * R + r] = 1;
2150 if (
stations[ist - 1].sched == SchedStrategy::POLLING)
2152 "Server breakdowns are not supported at a polling station (" +
2154 "): the polling controller and the breakdown status share one "
2155 "local-state column.");
2156 for (std::size_t r = 1; r <= R; ++r)
2160 "Server breakdowns are not supported at station '" +
2162 "', which also holds servers for a synchronous reply: the reply "
2163 "counters would trail the breakdown status the service handlers read");
2164 nvars[ind - 1][2 * R] = 1;
2166 const typename std::map<std::size_t, CacheParam<T>>::const_iterator ci =
2173 for (std::size_t u = 0; u < ci->second.itemcap.size(); ++u)
2174 if (ci->second.itemcap[u] > 0)
2175 w +=
static_cast<std::size_t
>(ci->second.itemcap[u]);
2176 if (ci->second.retrieval_capacity > 0) {
2177 w += ci->second.nitems;
2178 std::vector<std::size_t> rcl, rci, rco;
2182 nvars[ind - 1][2 * R] = w;
2204 const std::size_t R =
classes.size(), N =
nodes.size();
2206 for (std::size_t r = 0; r < R && r <
syncreply.size(); ++r)
2209 replyblock.assign(N, std::vector<bool>(R,
false));
2210 for (std::size_t r = 1; r <= R; ++r) {
2213 if (s < 1 || s > R)
continue;
2214 for (std::size_t ind = 1; ind <= N; ++ind) {
2215 const std::size_t ist =
nodes[ind - 1].station;
2217 if (
stations[ist - 1].sched == SchedStrategy::INF)
continue;
2218 bool arrives_here =
false;
2219 for (std::size_t i = 1; i <= N && !arrives_here; ++i)
2220 for (std::size_t q = 1; q <= R; ++q)
2223 rtnodes((i - 1) * R + q - 1, (ind - 1) * R + s - 1)) > 0) {
2224 arrives_here =
true;
2227 if (!arrives_here)
continue;
2228 if (
stations[ist - 1].sched != SchedStrategy::FCFS)
2230 "network '" +
name +
2231 "': synchronous calls (REPLY signals) are supported only at FCFS "
2234 "' uses another discipline. A held server is encoded as a per-class "
2235 "counter, which is exact only where servers are interchangeable (FCFS) "
2236 "or unlimited (INF); the other disciplines are not yet encoded rather than "
2237 "infeasible. Set this station to FCFS or INF, or simulate the layered model "
2238 "directly with SolverLDES.");
2259 const std::size_t R =
classes.size();
2260 const std::size_t N =
nodes.size();
2261 std::vector<std::size_t> out;
2263 std::vector<bool> seen(N + 1,
false);
2264 std::vector<std::size_t> frontier(1, ind);
2266 while (!frontier.empty()) {
2267 const std::size_t i = frontier.back();
2268 frontier.pop_back();
2269 for (std::size_t j = 1; j <= N; ++j) {
2270 if (seen[j])
continue;
2271 bool linked =
false;
2272 for (std::size_t r = 0; r < R && !linked; ++r)
2273 for (std::size_t s = 0; s < R && !linked; ++s)
2275 rtnodes((i - 1) * R + r, (j - 1) * R + s)) > 0)
2277 if (!linked)
continue;
2279 if (
nodes[j - 1].station != 0) {
2282 frontier.push_back(j);
2294 const std::size_t R =
classes.size();
2295 const std::size_t N =
nodes.size();
2299 for (std::size_t ind = 1; ind <= N; ++ind) {
2300 const std::size_t ist =
nodes[ind - 1].station;
2301 if (ist == 0)
continue;
2303 if (nt == NodeType::Source || nt == NodeType::Cache)
continue;
2305 bool declares =
false;
2306 for (std::size_t r = 1; r <= R; ++r) {
2307 const bool here_bas =
droprule.size() >= ist &&
droprule[ist - 1].size() >= r &&
2308 droprule[ist - 1][r - 1] == DropStrategy::BAS;
2309 for (std::size_t d = 0; d < dests.size(); ++d) {
2310 const std::size_t jst =
nodes[dests[d] - 1].station;
2311 if (jst == 0 ||
nodes[dests[d] - 1].nodetype == NodeType::Source)
continue;
2322 const double dcap =
stations[jst - 1].cap;
2323 if (!std::isfinite(dcap) || !(dcap > 0))
continue;
2324 const bool there_bas =
2326 droprule[jst - 1][r - 1] == DropStrategy::BAS;
2327 if (!here_bas && !there_bas)
continue;
2335 if (!declares)
continue;
2342 "' combines server breakdowns with true-BAS blocking: the breakdown status "
2343 "and the BAS blocked marker share one local-state column. Remove the BAS drop "
2344 "rule or the breakdown");
2345 if (
stations[ist - 1].sched == SchedStrategy::POLLING)
2347 "true BAS blocking is not supported at the polling station '" +
2349 "': the polling controller and the BAS blocked marker share one local-state "
2350 "column. Use a non-polling discipline at the blocking station, or remove the "
2352 for (std::size_t r = 1; r <= R; ++r)
2356 "true BAS blocking is not supported at station '" +
nodes[ind - 1].
name +
2357 "', which also holds servers for a synchronous reply: fromMarginal appends "
2358 "the reply counters AFTER the blocked marker, so the marker would no "
2359 "longer be the trailing column the departure handler reads");
2360 nvars[ind - 1][2 * R] = 1;
2381 for (std::size_t i = 0; i <
stations.size(); ++i) {
2383 if (st.
sched == SchedStrategy::DPS || st.
sched == SchedStrategy::GPS) {
2388 if (st.
sched == SchedStrategy::SEPT || st.
sched == SchedStrategy::LEPT) {
2390 std::vector<double> means;
2391 for (std::size_t r = 0; r <
classes.size(); ++r)
2393 std::vector<double> sorted = means;
2394 std::sort(sorted.begin(), sorted.end());
2395 sorted.erase(std::unique(sorted.begin(), sorted.end()), sorted.end());
2396 if (st.
sched == SchedStrategy::LEPT)
2397 std::reverse(sorted.begin(), sorted.end());
2399 for (std::size_t r = 0; r <
classes.size(); ++r)
2400 for (std::size_t k = 0; k < sorted.size(); ++k)
2401 if (sorted[k] == means[r])
2438 const std::size_t K =
classes.size(), I =
nodes.size();
2440 bool trivial =
true;
2443 if (rs != RoutingStrategy::PROB && rs != RoutingStrategy::DISABLED) trivial =
false;
2450 bool has_cs =
false;
2452 if (nd.nodetype == NodeType::ClassSwitch) has_cs =
true;
2455 const bool has_cache = !
nodeparam.empty();
2456 if (trivial && !has_cs && !has_cache)
return;
2458 for (
const auto& kv :
P)
Peff[kv.first] = kv.second;
2462 std::vector<char> conn(I * I, 0);
2463 for (
const auto& kv :
P) {
2465 if (M.
rows() < I || M.
cols() < I)
continue;
2466 for (std::size_t a = 0; a < I; ++a)
2467 for (std::size_t b = 0; b < I; ++b)
2468 if (M(a, b) > zero) conn[a * I + b] = 1;
2470 auto eff_at = [&](std::size_t r, std::size_t s) ->
Matrix<T>& {
2471 auto key = std::make_pair(r, s);
2472 auto it =
Peff.find(key);
2473 if (it ==
Peff.end())
2475 if (it->second.rows() != I) {
2477 for (std::size_t a = 0; a < it->second.rows(); ++a)
2478 for (std::size_t b = 0; b < it->second.cols(); ++b) g(a, b) = it->second(a, b);
2484 for (std::size_t i = 1; i <= I; ++i) {
2486 for (std::size_t r = 1; r <= K; ++r) {
2488 nd.
routing.size() >= r ? nd.
routing[r - 1] : RoutingStrategy::PROB;
2489 if (rs == RoutingStrategy::PROB || rs == RoutingStrategy::DISABLED)
continue;
2490 if (rs != RoutingStrategy::RAND && rs != RoutingStrategy::RROBIN &&
2491 rs != RoutingStrategy::JSQ && rs != RoutingStrategy::SQ &&
2492 rs != RoutingStrategy::WRROBIN && rs != RoutingStrategy::SDR)
2495 "' has no routing-matrix expansion in this port");
2511 const bool open_class = !std::isfinite(
2513 const bool from_source = nd.
nodetype == NodeType::Source;
2514 const bool from_sink = nd.
nodetype == NodeType::Sink;
2515 if (!open_class && (from_source || from_sink))
continue;
2516 std::vector<std::pair<std::size_t, std::size_t>> dest;
2517 for (std::size_t j = 1; j <= I; ++j) {
2518 if (!open_class &&
nodes[j - 1].nodetype == NodeType::Sink)
continue;
2519 if (conn[(i - 1) * I + (j - 1)]) dest.emplace_back(j, r);
2521 if (dest.empty())
continue;
2526 std::vector<T> share(dest.size(),
2529 if (rs == RoutingStrategy::WRROBIN) {
2530 const std::map<std::size_t, double>* w =
2533 std::vector<double> raw(dest.size(), 0.0);
2535 for (std::size_t d = 0; d < dest.size(); ++d) {
2536 const std::map<std::size_t, double>::const_iterator it =
2537 w->find(dest[d].first);
2538 raw[d] = (it == w->end()) ? 0.0 : it->second;
2542 for (std::size_t d = 0; d < dest.size(); ++d)
2547 for (std::size_t s = 1; s <= K; ++s) {
2548 if (
Peff.find(std::make_pair(r, s)) ==
Peff.end())
continue;
2550 for (std::size_t j = 1; j <= I; ++j) B(i - 1, j - 1) = zero;
2552 for (std::size_t d = 0; d < dest.size(); ++d)
2553 eff_at(r, dest[d].second)(i - 1, dest[d].first - 1) = share[d];
2559 const std::size_t cs = kv.first;
2561 if (C.
rows() != K || C.
cols() != K)
2562 throw InputError(
"network: the class-switch matrix of node '" +
2563 nodes[cs - 1].
name +
"' is not (nclasses x nclasses)");
2578 std::vector<std::vector<T>> diag(K, std::vector<T>(I, zero));
2579 for (std::size_t s = 1; s <= K; ++s) {
2580 if (!
Peff.empty() &&
Peff.find(std::make_pair(s, s)) ==
Peff.end())
continue;
2581 for (std::size_t j = 1; j <= I; ++j)
2582 diag[s - 1][j - 1] =
2583 Peff.empty() ?
get_route(s, s, cs, j) : eff_at(s, s)(cs - 1, j - 1);
2586 for (std::size_t r = 1; r <= K; ++r)
2587 for (std::size_t s = 1; s <= K; ++s) {
2588 if (
Peff.find(std::make_pair(r, s)) ==
Peff.end())
continue;
2590 for (std::size_t j = 1; j <= I; ++j) B(cs - 1, j - 1) = zero;
2592 for (std::size_t r = 1; r <= K; ++r)
2593 for (std::size_t s = 1; s <= K; ++s) {
2594 if (!(C(r - 1, s - 1) > zero))
continue;
2596 for (std::size_t j = 1; j <= I; ++j)
2597 if (diag[s - 1][j - 1] > zero)
2598 B(cs - 1, j - 1) = T(diag[s - 1][j - 1] * C(r - 1, s - 1));
2612 const std::size_t ci = kv.first;
2614 for (std::size_t r = 0; r < cp.
hitclass.size() && r < K; ++r) {
2617 for (std::size_t s = 1; s <= K; ++s) {
2619 for (std::size_t j = 1; j <= I; ++j) B(ci - 1, j - 1) = zero;
2621 eff_at(r + 1, cp.
hitclass[r])(ci - 1, ci - 1) = half;
2623 eff_at(r + 1, cp.
missclass[r])(ci - 1, ci - 1) = half;
2668 const std::size_t I =
nodes.size();
2669 if (joinNode == 0 || joinNode > I)
return 0;
2670 std::size_t forkNode = 0;
2671 for (std::size_t a = 0; a <
fj.size(); ++a)
2672 if (
fj[a].second == joinNode) {
2673 forkNode =
fj[a].first;
2677 std::vector<bool> seen(I + 1,
false);
2678 std::size_t deg = 0;
2679 for (
typename std::map<std::pair<std::size_t, std::size_t>,
Matrix<T> >::const_iterator
2681 it !=
P.end(); ++it) {
2682 if (it->second.rows() < I || it->second.cols() < I)
continue;
2683 for (std::size_t b = 1; b <= I; ++b) {
2684 if (seen[b])
continue;
2685 const T v = (forkNode != 0) ? it->second(forkNode - 1, b - 1)
2686 : it->second(b - 1, joinNode - 1);
2693 if (forkNode == 0 || forkNode > I)
return deg;
2707 const std::size_t lo = (r != 0 && r <= fol.
cols()) ? r - 1 : 0;
2708 const std::size_t hi = (r != 0 && r <= fol.
cols()) ? r - 1 : fol.
cols() - 1;
2710 for (std::size_t c = lo; c <= hi && c < fol.
cols(); ++c) {
2712 for (std::size_t d = 0; d < fol.
rows(); ++d) {
2714 if (!(link > 0.0))
continue;
2718 if (acc > best) best = acc;
2720 if (best > 0.0)
return static_cast<std::size_t
>(best + 0.5);
2723 double w =
nodes[forkNode - 1].tasks_per_link;
2724 if (!(w >= 1.0)) w = 1.0;
2725 return deg *
static_cast<std::size_t
>(w + 0.5);
2736 std::vector<std::size_t> out;
2737 for (
typename std::map<std::size_t, JoinDecl>::const_iterator it =
joindecl.begin();
2740 if (it->second.quorum <= 0.0)
continue;
2742 const std::size_t k =
static_cast<std::size_t
>(it->second.quorum + 0.5);
2743 if (n == 0 || k < n) out.push_back(it->first);
2750 const double inf = std::numeric_limits<double>::infinity();
2752 classcap.assign(M, std::vector<double>(K, inf));
2753 droprule.assign(M, std::vector<DropStrategy>(K, DropStrategy::WAITQ));
2754 std::vector<std::vector<double>> chaincap(M, std::vector<double>(std::max(
nchains, K), inf));
2762 std::vector<char> quorum_class(K + 1, 0);
2766 for (std::size_t x = 0; x < qj.size(); ++x)
2767 for (
typename std::map<std::pair<std::size_t, std::size_t>,
2769 it !=
P.end(); ++it) {
2770 if (it->second.rows() <
nodes.size() || qj[x] >
nodes.size())
continue;
2771 for (std::size_t a = 1; a <=
nodes.size(); ++a)
2772 if (it->second(a - 1, qj[x] - 1) > zero) {
2773 if (it->first.first <= K) quorum_class[it->first.first] = 1;
2774 if (it->first.second <= K) quorum_class[it->first.second] = 1;
2786 double fork_task_factor = 1.0;
2787 for (std::size_t i = 0; i <
nodes.size(); ++i) {
2788 if (
nodes[i].nodetype != NodeType::Fork)
continue;
2789 const double w =
nodes[i].tasks_per_link;
2790 if (w >= 1.0 && std::isfinite(w)) fork_task_factor *= std::floor(w + 0.5);
2793 for (std::size_t c = 0; c <
nchains; ++c) {
2794 double chain_cap = 0.0;
2796 bool quorum =
false;
2797 for (std::size_t r :
inchain[c]) {
2798 if (std::isinf(
classes[r - 1].population)) open =
true;
2799 else chain_cap +=
classes[r - 1].population;
2800 if (r <= K && quorum_class[r]) quorum =
true;
2804 bool spawn_fed =
false;
2805 for (std::size_t q = 0; q < K && !spawn_fed; ++q)
2806 for (std::size_t r :
inchain[c])
2807 if (
classes[q].spawn == r) spawn_fed =
true;
2808 chain_cap *= fork_task_factor;
2809 if (open || quorum || spawn_fed) chain_cap = inf;
2810 for (std::size_t r :
inchain[c])
2811 for (std::size_t i = 0; i < M; ++i) {
2814 if (st.
nodetype != NodeType::Source) {
2815 const bool cap_finite = st.
cap >= 0.0 && !std::isinf(st.
cap);
2816 const bool classcap_finite = st.
classcap.size() >= r &&
2820 st.
droprule[r - 1] ==
static_cast<int>(DropStrategy::WAITQ) &&
2821 std::isinf(
classes[r - 1].population) &&
2822 (cap_finite || classcap_finite))
2824 "station '" + st.
name +
"' declares setDropRule(WAITQ) for the "
2825 "open class '" +
classes[r - 1].name +
"' at a finite capacity: "
2826 "LINE does not implement waiting-room blocking for an open "
2827 "arrival at a plain finite buffer. Use DropStrategy.DROP for a "
2828 "loss station, or BAS / BBS / RSRD for blocking between "
2832 }
else if (std::isinf(st.
cap)) {
2833 droprule[i][r - 1] = DropStrategy::WAITQ;
2834 }
else if (!std::isinf(
classes[r - 1].population)) {
2835 droprule[i][r - 1] = DropStrategy::WAITQ;
2837 droprule[i][r - 1] = DropStrategy::DROP;
2843 chaincap[i][c] = 0.0;
2846 chaincap[i][c] = chain_cap;
2853 for (std::size_t i = 0; i < M; ++i) {
2858 double sc = 0.0, scl = 0.0;
2859 for (std::size_t c = 0; c < chaincap[i].size(); ++c) sc += chaincap[i][c];
2860 for (std::size_t r = 0; r < K; ++r) scl +=
classcap[i][r];
2861 cap[i] = std::min(sc, scl);
2875 const std::size_t K =
classes.size(), I =
nodes.size();
2876 std::vector<std::size_t> all(K);
2877 for (std::size_t r = 0; r < K; ++r) all[r] = r + 1;
2881 for (std::size_t a = 0; a < I; ++a)
2882 for (std::size_t b = 0; b < I; ++b)
rtnodes(a * K + r - 1, b * K + s - 1) = M(a, b);
2899 const std::size_t K =
classes.size();
2900 std::size_t cidx = 0;
2902 const std::size_t ci = kv.first;
2904 for (std::size_t r = 0; r < cp.
hitclass.size() && r < K; ++r) {
2916 for (std::size_t c = 0; c <
nchains; ++c) {
2934 for (std::size_t i = 0; i <
nstations; ++i)
2935 for (std::size_t r = 0; r <
nclasses; ++r) {
2937 if (
stations[i].nodetype == NodeType::Join) {
2940 std::numeric_limits<double>::infinity());
2952 const std::size_t mk =
markidx_of(i + 1, r + 1);
2953 if (mk > 0 && mk <= d.
Dmark.size()) {
2958 scv(i, r) = ms.second;
2984 for (std::size_t a = 0; a < m.
D0.rows(); ++a)
2985 for (std::size_t b = 0; b < m.
D0.cols(); ++b)
2986 m.
D0(a, b) += T(d.
D1(a, b) - m.
D1(a, b));
3006 std::vector<std::vector<bool>> cs(K, std::vector<bool>(K,
false));
3007 for (std::size_t r = 0; r < K; ++r) cs[r][r] =
true;
3008 for (
const auto& kv : (
Peff.empty() ?
P :
Peff)) {
3010 for (std::size_t a = 0; a < kv.second.rows() && !any; ++a)
3011 for (std::size_t b = 0; b < kv.second.cols() && !any; ++b)
3012 if (kv.second(a, b) > zero) any =
true;
3013 if (any) cs[kv.first.first - 1][kv.first.second - 1] =
true;
3023 for (std::size_t r = 0; r < K && r < C.
rows(); ++r)
3024 for (std::size_t s = 0; s < K && s < C.
cols(); ++s)
3025 if (C(r, s) > zero) cs[r][s] =
true;
3033 for (std::size_t r = 0; r < cp.
hitclass.size() && r < K; ++r) {
3047 for (std::size_t r = 0; r < row.size() && r < K; ++r)
3048 if (row[r] != 0) cs[r][row[r] - 1] =
true;
3050 std::vector<std::size_t> comp(K, K);
3051 std::size_t ncomp = 0;
3052 for (std::size_t r = 0; r < K; ++r) {
3053 if (comp[r] != K)
continue;
3054 std::vector<std::size_t> stack{r};
3056 while (!stack.empty()) {
3057 const std::size_t v = stack.back();
3059 for (std::size_t w = 0; w < K; ++w)
3060 if (comp[w] == K && (cs[v][w] || cs[w][v])) {
3072 for (std::size_t r = 0; r < K; ++r) {
3073 chains[comp[r]][r] =
true;
3074 inchain[comp[r]].push_back(r + 1);
3079 for (std::size_t c = 0; c <
nchains; ++c)
3080 for (std::size_t k :
inchain[c])
3083 for (std::size_t c = 0; c <
nchains; ++c) {
3085 for (std::size_t k :
inchain[c])
3086 if (
classes[k - 1].refstat != rs)
3087 throw InputError(
"network '" +
name +
"': classes within a chain have different "
3088 "reference stations");
3097 for (std::size_t c = 0; c <
nchains; ++c) {
3132 for (
auto& kv :
P) {
3133 if (kv.second.rows() <
nodes.size())
continue;
3134 kv.second(
sinkNode - 1, src - 1) = zero;
3136 for (
auto& kv :
Peff) {
3137 if (kv.second.rows() <
nodes.size())
continue;
3138 kv.second(
sinkNode - 1, src - 1) = zero;
3140 for (std::size_t c = 0; c <
nchains; ++c) {
3142 for (std::size_t k :
inchain[c])
3143 if (std::isinf(
classes[k - 1].population)) open =
true;
3144 if (!open)
continue;
3146 for (std::size_t k :
inchain[c])
3150 for (std::size_t s :
inchain[c]) {
3157 if (!(p > zero))
continue;
3165 std::vector<std::size_t> v(
nodes.size());
3166 for (std::size_t i = 0; i <
nodes.size(); ++i) v[i] = i + 1;
3186 const std::vector<std::size_t>& ic =
inchain[c];
3187 const std::size_t nIC = ic.size();
3188 const std::size_t dim = sel.size() * nIC;
3195 std::vector<std::size_t> cpos(
nclasses + 1, nIC);
3196 for (std::size_t x = 0; x < nIC; ++x) cpos[ic[x]] = x;
3199 const std::size_t x = cpos[r], y = cpos[s];
3200 if (x == nIC || y == nIC)
return;
3201 for (std::size_t a = 0; a < sel.size(); ++a)
3202 for (std::size_t b = 0; b < sel.size(); ++b)
3203 Pc(a * nIC + x, b * nIC + y) = M(sel[a] - 1, sel[b] - 1);
3229 std::vector<bool> served(dim,
true);
3231 for (std::size_t a = 0; a < sel.size(); ++a) {
3232 const std::size_t sti =
nodes[sel[a] - 1].station;
3233 if (sti == 0 || sti >
stations.size())
continue;
3235 if (nt == NodeType::Place || nt == NodeType::Transition)
continue;
3236 if (!
stations[sti - 1].server_types.empty())
continue;
3237 if (sti >
disabled.size())
continue;
3244 for (std::size_t x = 0; x < nIC; ++x)
3246 served[a * nIC + x] =
false;
3248 for (std::size_t row = 0; row < dim; ++row)
3250 for (std::size_t col = 0; col < dim; ++col) Pc(row, col) = zero;
3251 for (std::size_t col = 0; col < dim; ++col)
3253 for (std::size_t row = 0; row < dim; ++row) Pc(row, col) = zero;
3256 std::vector<std::size_t> visited;
3257 std::vector<T> rowsum(dim, zero);
3258 for (std::size_t row = 0; row < dim; ++row) {
3260 for (std::size_t col = 0; col < dim; ++col) s += Pc(row, col);
3262 if (s > zero) visited.push_back(row);
3264 bool oversum =
false;
3266 for (std::size_t row = 0; row < dim; ++row) {
3270 for (std::size_t col = 0; col < dim; ++col)
3271 Pc(row, col) = T(Pc(row, col) / rowsum[row]);
3301 bool reducible =
false;
3302 if (!fork && !visited.empty()) {
3303 Matrix<T> Pv0(visited.size(), visited.size(), zero);
3304 for (std::size_t a = 0; a < visited.size(); ++a)
3305 for (std::size_t b = 0; b < visited.size(); ++b) Pv0(a, b) = Pc(visited[a], visited[b]);
3307 std::size_t nrec = 0;
3310 reducible = (nrec > 1) && mc::detail::weak_components(Pv0).size() == 1;
3319 std::vector<std::size_t> nconn(sel.size(), 0);
3320 std::vector<std::vector<bool>> conn(sel.size(), std::vector<bool>(sel.size(),
false));
3321 for (std::size_t a = 0; a < sel.size(); ++a)
3322 for (std::size_t b = 0; b < sel.size(); ++b) {
3324 for (std::size_t x = 0; x < nIC && !cbit; ++x)
3325 for (std::size_t y = 0; y < nIC && !cbit; ++y)
3326 if (Pc(a * nIC + x, b * nIC + y) > zero) cbit =
true;
3328 if (cbit) ++nconn[a];
3330 for (std::size_t a = 0; a < sel.size(); ++a) {
3331 if (nconn[a] == 0)
continue;
3332 for (std::size_t x = 0; x < nIC; ++x) {
3334 for (std::size_t col = 0; col < dim; ++col) outsum += Pc(a * nIC + x, col);
3335 if (outsum > zero)
continue;
3338 for (std::size_t b = 0; b < sel.size(); ++b)
3339 if (conn[a][b]) Pc(a * nIC + x, b * nIC + x) = p;
3351 for (std::size_t row = 0; row < dim; ++row)
3353 for (std::size_t col = 0; col < dim; ++col) Pc(row, col) = zero;
3354 for (std::size_t col = 0; col < dim; ++col)
3356 for (std::size_t row = 0; row < dim; ++row) Pc(row, col) = zero;
3358 for (std::size_t row = 0; row < dim; ++row) {
3360 for (std::size_t col = 0; col < dim; ++col) s += Pc(row, col);
3361 if (s > zero) visited.push_back(row);
3365 std::vector<T> alpha(dim, zero);
3366 if (!visited.empty()) {
3367 Matrix<T> Pv(visited.size(), visited.size(), zero);
3368 for (std::size_t a = 0; a < visited.size(); ++a)
3369 for (std::size_t b = 0; b < visited.size(); ++b)
3370 Pv(a, b) = Pc(visited[a], visited[b]);
3377 bool allzero =
true, hasnan =
false;
3378 for (
const T& x : av) {
3379 if (x != zero) allzero =
false;
3382 if (allzero || hasnan) ok =
false;
3383 }
catch (
const Error&) {
3391 for (std::size_t a = 0; a < visited.size(); ++a) alpha[visited[a]] = av[a];
3411 for (std::size_t a = 0; a < visited.size(); ++a)
3412 if (!scc.
recurrent[scc.
scc[a] - 1]) alpha[visited[a]] = zero;
3416 if (fork && oversum) {
3417 for (std::size_t idx = 0; idx < dim; ++idx) {
3419 const std::size_t nd = sel[idx / nIC];
3420 if (!complement &&
nodes[nd - 1].nodetype == NodeType::Join) {
3427 const std::size_t r = ic[idx % nIC];
3428 std::size_t nsrc = 0;
3429 for (std::size_t src = 1; src <=
nodes.size(); ++src)
3430 for (std::size_t q = 1; q <=
nclasses; ++q)
3442 for (std::size_t a = 0; a < sel.size(); ++a)
3443 for (std::size_t x = 0; x < nIC; ++x) out(a, ic[x] - 1) = alpha[a * nIC + x];
3446 const std::size_t rstat =
classes[ic[0] - 1].refstat;
3448 std::size_t refrow = sel.size();
3449 for (std::size_t a = 0; a < sel.size(); ++a)
3450 if (sel[a] == refnode) refrow = a;
3451 if (refrow < sel.size()) {
3453 for (std::size_t x = 0; x < nIC; ++x) norm += out(refrow, ic[x] - 1);
3455 for (std::size_t a = 0; a < sel.size(); ++a)
3456 for (std::size_t x = 0; x < nIC; ++x)
3457 out(a, ic[x] - 1) = T(out(a, ic[x] - 1) / norm);
3459 for (std::size_t a = 0; a < sel.size(); ++a)
3460 for (std::size_t x = 0; x < nIC; ++x)
3461 if (out(a, ic[x] - 1) < zero) out(a, ic[x] - 1) = T(-out(a, ic[x] - 1));
3475 const std::vector<std::size_t>& ic, std::size_t refnode)
const {
3477 const std::size_t nIC = ic.size();
3478 const std::size_t dim = Pc_in.
rows();
3481 for (std::size_t row = 0; row < dim; ++row) {
3483 std::size_t nnan = 0;
3484 for (std::size_t col = 0; col < dim; ++col) {
3487 else nonnan = T(nonnan + Pc(row, col));
3489 if (nnan == 0)
continue;
3492 for (std::size_t col = 0; col < dim; ++col) {
3494 if (v != v) Pc(row, col) = fill;
3497 std::vector<std::size_t> visited;
3498 for (std::size_t row = 0; row < dim; ++row) {
3500 for (std::size_t col = 0; col < dim; ++col) s += Pc(row, col);
3501 if (s > zero) visited.push_back(row);
3503 std::vector<T> alpha(dim, zero);
3504 if (!visited.empty()) {
3505 Matrix<T> Pv(visited.size(), visited.size(), zero);
3506 for (std::size_t a = 0; a < visited.size(); ++a)
3507 for (std::size_t b = 0; b < visited.size(); ++b) Pv(a, b) = Pc(visited[a], visited[b]);
3513 bool allzero =
true, hasnan =
false;
3514 for (
const T& x : av) {
3515 if (x != zero) allzero =
false;
3518 if (allzero || hasnan) ok =
false;
3519 }
catch (
const Error&) {
3523 for (std::size_t a = 0; a < visited.size(); ++a) alpha[visited[a]] = av[a];
3526 for (std::size_t a = 0; a < sel.size(); ++a)
3527 for (std::size_t x = 0; x < nIC; ++x) out(a, ic[x] - 1) = alpha[a * nIC + x];
3528 std::size_t refrow = sel.size();
3529 for (std::size_t a = 0; a < sel.size(); ++a)
3530 if (sel[a] == refnode) refrow = a;
3531 if (refrow < sel.size()) {
3533 for (std::size_t x = 0; x < nIC; ++x) norm += out(refrow, ic[x] - 1);
3535 for (std::size_t a = 0; a < sel.size(); ++a)
3536 for (std::size_t x = 0; x < nIC; ++x)
3537 out(a, ic[x] - 1) = T(out(a, ic[x] - 1) / norm);
3539 for (std::size_t a = 0; a < sel.size(); ++a)
3540 for (std::size_t x = 0; x < nIC; ++x)
3541 if (out(a, ic[x] - 1) < zero) out(a, ic[x] - 1) = T(-out(a, ic[x] - 1));
3557 if (nd.nodetype == NodeType::Fork)
3558 throw UnsupportedError(
"da_recompute_visits_from_rtnodes: fork models keep the "
3559 "route_eff visit path");
3560 std::vector<std::size_t> keep;
3561 keep.reserve(M * K);
3562 for (std::size_t p = 0; p < M; ++p) {
3564 for (std::size_t r = 0; r < K; ++r) keep.push_back(base + r);
3568 std::vector<std::size_t> allnodes(I);
3569 for (std::size_t a = 0; a < I; ++a) allnodes[a] = a + 1;
3572 for (std::size_t c = 0; c <
nchains; ++c) {
3573 const std::vector<std::size_t>& ic =
inchain[c];
3574 const std::size_t nIC = ic.size();
3577 for (std::size_t a = 0; a < M; ++a)
3578 for (std::size_t x = 0; x < nIC; ++x)
3579 for (std::size_t b = 0; b < M; ++b)
3580 for (std::size_t y = 0; y < nIC; ++y)
3581 Ps(a * nIC + x, b * nIC + y) =
rt(a * K + (ic[x] - 1), b * K + (ic[y] - 1));
3582 const std::size_t rstat =
classes[ic[0] - 1].refstat;
3589 for (std::size_t a = 0; a < I; ++a)
3590 for (std::size_t x = 0; x < nIC; ++x)
3591 for (std::size_t b = 0; b < I; ++b)
3592 for (std::size_t y = 0; y < nIC; ++y)
3593 Pn(a * nIC + x, b * nIC + y) =
3594 rtnodes(a * K + (ic[x] - 1), b * K + (ic[y] - 1));
3608 const std::size_t nIC = ic.size();
3609 const std::size_t I =
nodes.size();
3611 std::vector<std::size_t> pos(
classes.size() + 1, 0);
3612 for (std::size_t x = 0; x < nIC; ++x) pos[ic[x]] = x + 1;
3614 if (r >= pos.size() || s >= pos.size() || pos[r] == 0 || pos[s] == 0)
return;
3615 const std::size_t x = pos[r] - 1, y = pos[s] - 1;
3616 for (std::size_t a = 0; a < I; ++a)
3617 for (std::size_t b = 0; b < I; ++b) full(a * nIC + x, b * nIC + y) = M(a, b);
3628 const std::map<std::pair<std::size_t, std::size_t>,
Matrix<T>>& src =
Peff.empty() ?
P :
Peff;
3629 for (
const auto& kv : src)
3630 if (kv.second.rows() >=
nodes.size() && kv.second.cols() >=
nodes.size())
3631 f(kv.first.first, kv.first.second, kv.second);
3650 const std::size_t I =
nodes.size();
3651 std::vector<std::size_t> keep, drop;
3652 for (std::size_t a = 0; a < I; ++a) {
3653 const bool st =
nodes[a].stateful;
3654 for (std::size_t x = 0; x < nIC; ++x) (st ? keep : drop).push_back(a * nIC + x);
3656 if (drop.empty())
return full;
3661 std::vector<char> isdrop(full.
rows(), 0), seen(full.
rows(), 0);
3662 for (std::size_t d : drop) isdrop[d] = 1;
3663 std::vector<std::size_t> stack;
3664 for (std::size_t k : keep)
3665 for (std::size_t d : drop)
3666 if (!seen[d] && full(k, d) != zero) { seen[d] = 1; stack.push_back(d); }
3667 while (!stack.empty()) {
3668 const std::size_t u = stack.back();
3670 for (std::size_t d : drop)
3671 if (!seen[d] && full(u, d) != zero) { seen[d] = 1; stack.push_back(d); }
3673 std::vector<std::size_t> reach;
3674 for (std::size_t d : drop)
3675 if (seen[d]) reach.push_back(d);
3679 Matrix<T> S(keep.size(), keep.size(), zero);
3680 for (std::size_t a = 0; a < keep.size(); ++a)
3681 for (std::size_t b = 0; b < keep.size(); ++b) S(a, b) = full(keep[a], keep[b]);
3685 const std::size_t nk = keep.size(), nd = drop.size();
3686 Matrix<T> P11(nk, nk, zero), P12(nk, nd, zero), P21(nd, nk, zero), A(nd, nd, zero);
3687 for (std::size_t a = 0; a < nk; ++a) {
3688 for (std::size_t b = 0; b < nk; ++b) P11(a, b) = full(keep[a], keep[b]);
3689 for (std::size_t b = 0; b < nd; ++b) P12(a, b) = full(keep[a], drop[b]);
3691 for (std::size_t a = 0; a < nd; ++a) {
3692 for (std::size_t b = 0; b < nk; ++b) P21(a, b) = full(drop[a], keep[b]);
3693 for (std::size_t b = 0; b < nd; ++b)
3694 A(a, b) = T((a == b ? one : zero) - full(drop[a], drop[b]));
3698 std::vector<std::size_t> piv(nd);
3699 for (std::size_t i = 0; i < nd; ++i) piv[i] = i;
3700 for (std::size_t col = 0; col < nd; ++col) {
3701 std::size_t best = col;
3703 for (std::size_t r2 = col + 1; r2 < nd; ++r2) {
3705 if (v > bv) { bv = v; best = r2; }
3708 for (std::size_t b = 0; b < nd; ++b) std::swap(A(col, b), A(best, b));
3709 for (std::size_t b = 0; b < nk; ++b) std::swap(X(col, b), X(best, b));
3711 if (A(col, col) == zero)
3712 throw NumericError(
"network: the stochastic complement is singular");
3714 std::vector<std::size_t> nzA, nzX;
3715 for (std::size_t b = 0; b < nd; ++b)
3716 if (A(col, b) != zero) nzA.push_back(b);
3717 for (std::size_t b = 0; b < nk; ++b)
3718 if (X(col, b) != zero) nzX.push_back(b);
3719 for (std::size_t r2 = 0; r2 < nd; ++r2) {
3720 if (r2 == col)
continue;
3721 const T f = T(A(r2, col) / A(col, col));
3722 if (f == zero)
continue;
3723 for (std::size_t b : nzA) A(r2, b) = T(A(r2, b) - f * A(col, b));
3724 for (std::size_t b : nzX) X(r2, b) = T(X(r2, b) - f * X(col, b));
3727 for (std::size_t r2 = 0; r2 < nd; ++r2)
3728 for (std::size_t b = 0; b < nk; ++b) X(r2, b) = T(X(r2, b) / A(r2, r2));
3732 std::vector<T> acc(nk, zero);
3733 for (std::size_t a = 0; a < nk; ++a) {
3734 std::fill(acc.begin(), acc.end(), zero);
3735 for (std::size_t m = 0; m < nd; ++m) {
3736 const T w = P12(a, m);
3737 if (w == zero)
continue;
3738 for (std::size_t b = 0; b < nk; ++b) acc[b] += w * X(m, b);
3740 for (std::size_t b = 0; b < nk; ++b) S(a, b) = T(S(a, b) + acc[b]);
3749 if (std::isinf(c.population))
return true;
3761 if (!std::isinf(c.population))
return false;
3767 if (std::isfinite(s.nservers) && s.nservers > 1.0)
return true;
3773 if (std::isfinite(c.population) && c.population != std::floor(c.population + 0.5))
3782 if (c.prio > 0)
return true;
3795 if (
classes.empty())
return false;
3797 if (c.prio !=
classes.front().prio)
return true;
3813 case SchedStrategy::HOL:
3814 case SchedStrategy::PSPRIO:
3815 case SchedStrategy::DPSPRIO:
3816 case SchedStrategy::GPSPRIO:
3817 case SchedStrategy::LCFSPRIO:
3818 case SchedStrategy::LCFSPRPRIO:
3819 case SchedStrategy::LCFSPIPRIO:
3820 case SchedStrategy::FCFSPRPRIO:
3821 case SchedStrategy::FCFSPIPRIO:
3822 case SchedStrategy::SRPTPRIO:
3852 for (std::size_t i = 0; i <
nstations; ++i) {
3853 if (
stations[i].sched != SchedStrategy::FCFS)
continue;
3856 for (std::size_t r = 0; r <
nclasses; ++r) {
3859 lo = hi =
rates(i, r);
3866 if (any && hi > lo) bad =
true;
3873 if (!(s.sched == SchedStrategy::INF || s.sched == SchedStrategy::PS ||
3874 s.sched == SchedStrategy::FCFS || s.sched == SchedStrategy::LCFSPR ||
3875 s.sched == SchedStrategy::LCFS || s.sched == SchedStrategy::EXT))
3887 for (std::size_t i = 0; i <
nstations; ++i) {
3888 if (
stations[i].sched != SchedStrategy::FCFS)
continue;
3889 for (std::size_t r = 0; r <
nclasses; ++r) {
3891 if (std::isinf(v) || !(v > 0.0))
continue;
3905 double k = std::numeric_limits<double>::infinity();
3906 if (
cap[ist - 1] >= 0.0) k = std::min(k,
cap[ist - 1]);
3908 bool anyServed =
false;
3909 for (std::size_t r = 0; r <
nclasses; ++r)
3914 if (anyServed) k = std::min(k, ccap);
3915 double reachable = 0.0;
3916 for (std::size_t r = 0; r <
nclasses; ++r)
3917 if (!anyServed ||
classcap[ist - 1][r] > 0.0) reachable +=
classes[r].population;
3918 return (k >= reachable) ? std::numeric_limits<double>::infinity() : k;
3924 for (std::size_t k = 0; k <
classes.size(); ++k)
3925 if (std::isfinite(
classes[k].population))
return false;
3926 std::size_t nq = 0, nsrc = 0, nsink = 0, qnode = 0, snode = 0;
3927 for (std::size_t a = 0; a <
nodes.size(); ++a) {
3928 switch (
nodes[a].nodetype) {
3929 case NodeType::Queue: ++nq; qnode = a + 1;
break;
3930 case NodeType::Source: ++nsrc; snode = a + 1;
break;
3931 case NodeType::Sink: ++nsink;
break;
3932 default:
return false;
3935 if (nq != 1 || nsrc != 1 || nsink != 1)
return false;
3936 const std::size_t qist =
nodes[qnode - 1].station;
3937 const std::size_t sist =
nodes[snode - 1].station;
3938 if (qist == 0 || sist == 0)
return false;
3939 if (
stations[qist - 1].nservers != 1.0)
return false;
3941 droprule[qist - 1][0] != DropStrategy::DROP)
3943 if (!(qist <=
cap.size()) || !std::isfinite(
cap[qist - 1]) || !(
cap[qist - 1] > 0.0))
3965 if (!
regions.empty())
return true;
3966 for (std::size_t a = 0; a <
nodes.size(); ++a)
3967 if (
nodes[a].nodetype == NodeType::Cache)
return false;
3969 for (std::size_t ist = 1; ist <=
stations.size(); ++ist)
4000 if (!(s.sched == SchedStrategy::INF || s.sched == SchedStrategy::PS ||
4001 s.sched == SchedStrategy::FCFS || s.sched == SchedStrategy::LCFSPR ||
4002 s.sched == SchedStrategy::EXT))
4005 for (std::size_t i = 0; i <
nstations; ++i) {
4006 if (
stations[i].sched != SchedStrategy::FCFS)
continue;
4007 for (std::size_t r = 0; r <
nclasses; ++r) {
4010 if (!std::isinf(v) && v > 0.0 &&
4014 if (!check_means ||
visits.empty())
continue;
4016 double stmin = 0.0, stmax = 0.0;
4017 bool anyserved =
false;
4018 for (std::size_t c = 0; c <
nchains && c <
visits.size(); ++c) {
4019 double num = 0.0, den = 0.0;
4020 for (std::size_t r = 0; r <
nclasses; ++r) {
4031 const double st = num / den;
4036 stmin = std::min(stmin, st);
4037 stmax = std::max(stmax, st);
4053 for (std::size_t a = 0; a < B.
rows(); ++a)
4054 for (std::size_t b = 0; b < B.
cols(); ++b) g(a, b) = B(a, b);
4062 if (std::isfinite(c.population)) s += c.population;
4085 const std::size_t R =
sn.nclasses;
4089 if (st.
sched != SchedStrategy::PAS)
return Matrix<T>(R, R, zero);
4091 for (std::size_t r = 0; r < R; ++r) g(r, r) = zero;
4100 for (std::size_t a = 0; a < g.
rows(); ++a)
4101 for (std::size_t b = 0; b < g.
cols(); ++b)
4102 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.
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
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::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
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
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