5#ifndef LINE_SOLVERS_SOLVER_CHAIN_TABLES_H
6#define LINE_SOLVERS_SOLVER_CHAIN_TABLES_H
63namespace chain_detail {
88T exp_max_mean(
const std::vector<T>& branch_times) {
89 const std::size_t n = branch_times.size();
90 const T zero = num_traits<T>::from_int(0), one = num_traits<T>::from_int(1);
91 if (n == 0)
return zero;
94 "getAvgSys: the fork-join section has " + std::to_string(n) +
95 " parallel paths, and the reference's synchronization delay is an "
96 "inclusion-exclusion sum of 2^n alternating terms; past 20 paths that sum has lost "
97 "every significant digit to cancellation, so it is refused rather than reported");
99 std::vector<T> lambda(n);
100 for (std::size_t i = 0; i < n; ++i) {
101 const double ri = num_traits<T>::to_double(branch_times[i]);
104 "getAvgSys: a path from the fork to the join has non-positive total response "
105 "time, so its exponential rate is not defined; the branch carries no station "
106 "with a finite response time");
107 lambda[i] = T(one / branch_times[i]);
111 const unsigned long long total = 1ull << n;
112 for (
unsigned long long mask = 1; mask < total; ++mask) {
114 std::size_t bits = 0;
115 for (std::size_t i = 0; i < n; ++i)
116 if (mask & (1ull << i)) {
117 rate = T(rate + lambda[i]);
120 const T term = T(one / rate);
132 std::vector<T> times;
133 std::vector<std::size_t> stations;
162PathsResult<T> paths_cs(
const qn::NetworkStruct<T>& sn,
const Matrix<T>& P, std::size_t cur,
163 std::size_t stop, std::size_t cls, Matrix<T>& RN,
const T& elapsed,
164 const std::vector<std::size_t>& acc_stations,
165 std::vector<std::pair<std::size_t, std::size_t>>& on_path) {
166 const std::size_t K = sn.nclasses, N = sn.nodes.size();
167 const T zero = num_traits<T>::from_int(0);
171 out.times.push_back(elapsed);
172 out.stations = acc_stations;
175 for (std::size_t i = 0; i < on_path.size(); ++i)
176 if (on_path[i].first == cur && on_path[i].second == cls)
178 "getAvgSys: the fork-join section of node '" + sn.nodes[cur - 1].name +
179 "' contains a routing cycle, so the set of paths from the fork to the join is "
180 "infinite and the synchronization delay has no inclusion-exclusion form");
181 on_path.push_back(std::make_pair(cur, cls));
184 std::vector<std::size_t> stations = acc_stations;
185 const std::size_t ist = sn.nodes[cur - 1].station;
187 here = RN(ist - 1, cls - 1);
188 stations.push_back(ist);
191 const std::size_t row = (cur - 1) * K + (cls - 1);
192 for (std::size_t nxt = 1; nxt <= N; ++nxt) {
193 for (std::size_t s = 1; s <= K; ++s) {
194 if (row >=
P.rows())
break;
195 const std::size_t col = (nxt - 1) * K + (s - 1);
196 if (col >=
P.cols())
continue;
197 if (num_traits<T>::to_double(
P(row, col)) == 0.0)
continue;
199 std::size_t hop = nxt;
200 T entry = T(elapsed + here);
202 std::size_t inner_join = 0;
203 for (std::size_t k = 0; k < sn.fj.size(); ++k)
204 if (sn.fj[k].first == nxt) inner_join = sn.fj[k].second;
205 if (inner_join != 0) {
206 const std::size_t jst = sn.nodes[inner_join - 1].station;
207 if (jst != 0 && num_traits<T>::to_double(RN(jst - 1, s - 1)) == 0.0) {
208 std::vector<std::pair<std::size_t, std::size_t>> inner_path;
209 const PathsResult<T>
in =
210 paths_cs(sn, P, nxt, inner_join, s, RN, zero,
211 std::vector<std::size_t>(), inner_path);
212 RN(jst - 1, s - 1) = exp_max_mean(
in.times);
213 for (std::size_t q = 0; q <
in.stations.size(); ++q)
214 RN(
in.stations[q] - 1, s - 1) = zero;
219 const PathsResult<T> sub = paths_cs(sn, P, hop, stop, s, RN, entry, stations, on_path);
220 out.times.insert(out.times.end(), sub.times.begin(), sub.times.end());
221 out.stations.insert(out.stations.end(), sub.stations.begin(), sub.stations.end());
258 const std::size_t M =
sn.nstations, K =
sn.nclasses, C =
sn.nchains;
260 const std::vector<double> njobs =
sn.njobs();
262 bool has_join =
false, has_fork =
false;
263 for (std::size_t i = 0; i <
sn.nodes.size(); ++i) {
273 for (std::size_t i = 0; i < M; ++i)
275 for (std::size_t c = 0; c < K; ++c) RN(i, c) = zero;
286 if (has_fork && has_join) {
287 for (std::size_t f = 0; f <
sn.fj.size(); ++f) {
288 const std::size_t fork =
sn.fj[f].first, join =
sn.fj[f].second;
289 if (fork == 0 || join == 0)
continue;
290 const std::size_t jst =
sn.nodes[join - 1].station;
291 if (jst == 0)
continue;
292 for (std::size_t c = 0; c < C; ++c) {
293 double nJobsChain = 0.0;
294 for (std::size_t k = 0; k < K; ++k)
295 if (
sn.chains[c][k]) nJobsChain += njobs[k];
296 if (!std::isinf(nJobsChain))
continue;
297 for (std::size_t q = 0; q <
sn.inchain[c].size(); ++q) {
298 const std::size_t rr =
sn.inchain[c][q];
300 std::vector<std::pair<std::size_t, std::size_t>> on_path;
301 const chain_detail::PathsResult<T> paths =
302 chain_detail::paths_cs(
sn,
sn.rtnodes, fork, join, rr, RN, zero,
303 std::vector<std::size_t>(), on_path);
304 if (paths.times.empty())
continue;
307 RN(jst - 1, rr - 1) = chain_detail::exp_max_mean(paths.times);
308 for (std::size_t j = 0; j < paths.stations.size(); ++j)
309 RN(paths.stations[j] - 1, rr - 1) = zero;
318 std::vector<T> CNclass(K, zero);
319 for (std::size_t c = 0; c < C; ++c) {
320 for (std::size_t j = 0; j <
sn.inchain[c].size(); ++j) {
321 const std::size_t rr =
sn.inchain[c][j];
322 const std::size_t refst =
sn.classes[rr - 1].refstat;
323 if (refst == 0)
continue;
324 const std::size_t refsf =
sn.stateful_of_station(refst);
325 const T vref =
sn.visits[c](refsf - 1, rr - 1);
327 for (std::size_t i = 0; i < M; ++i) {
329 if (std::isinf(njobs[rr - 1]) && i + 1 == refst)
continue;
330 const std::size_t isf =
sn.stateful_of_station(i + 1);
332 T(CNclass[rr - 1] + T(T(
sn.visits[c](isf - 1, rr - 1) * RN(i, rr - 1)) / vref));
344 std::vector<std::size_t> refclass_nz;
345 for (std::size_t c = 0; c < C; ++c)
346 if (
sn.refclass[c] != 0) refclass_nz.push_back(c + 1);
347 for (std::size_t c = 0; c < C; ++c) {
351 std::vector<std::size_t> completing;
352 for (std::size_t k = 0; k < K; ++k)
353 if (
sn.chains[c][k] &&
sn.classes[k].completes) completing.push_back(k + 1);
355 std::vector<std::size_t> ks;
356 for (std::size_t j = 0; j < refclass_nz.size(); ++j)
357 for (std::size_t q = 0; q <
sn.inchain[c].size(); ++q)
358 if (refclass_nz[j] ==
sn.inchain[c][q]) ks.push_back(refclass_nz[j]);
359 if (
sn.refclass[c] == 0) ks =
sn.inchain[c];
361 for (std::size_t i = 0; i < M; ++i) {
362 for (std::size_t j = 0; j < ks.size(); ++j) {
363 const std::size_t k = ks[j];
364 const std::size_t refst =
sn.classes[k - 1].refstat;
365 if (refst == 0)
continue;
366 const std::size_t refsf =
sn.stateful_of_station(refst);
368 for (std::size_t q = 0; q < completing.size(); ++q)
369 denom = T(denom +
sn.visits[c](refsf - 1, completing[q] - 1));
371 const std::size_t isf =
sn.stateful_of_station(i + 1);
372 alpha(i, k - 1) = T(alpha(i, k - 1) + T(
sn.visits[c](isf - 1, k - 1) / denom));
378 for (std::size_t i = 0; i < M; ++i)
379 for (std::size_t k = 0; k < K; ++k)
383 out.
CN.assign(C, zero);
384 out.
XN.assign(C, zero);
385 const std::size_t nsf =
sn.nof_stateful();
387 for (std::size_t c = 0; c < C; ++c) {
388 const std::vector<std::size_t>& inchain =
sn.inchain[c];
389 if (inchain.empty())
continue;
390 std::vector<std::size_t> completing;
391 for (std::size_t k = 0; k < K; ++k)
392 if (
sn.chains[c][k] &&
sn.classes[k].completes) completing.push_back(k + 1);
398 const std::size_t ref =
sn.classes[inchain[0] - 1].refstat;
399 if (ref != 0 && r.
TN.rows() == M) {
400 const std::size_t refsf =
sn.stateful_of_station(ref);
401 std::vector<std::size_t> ss;
402 for (std::size_t j = 0; j < refclass_nz.size(); ++j)
403 for (std::size_t q = 0; q < inchain.size(); ++q)
404 if (refclass_nz[j] == inchain[q]) ss.push_back(refclass_nz[j]);
405 if (ss.empty()) ss = inchain;
406 for (std::size_t i = 0; i < M; ++i) {
407 const std::size_t isf =
sn.stateful_of_station(i + 1);
408 if (isf == 0 || isf > nsf)
continue;
409 for (std::size_t q = 0; q < completing.size(); ++q) {
410 const std::size_t rr = completing[q];
412 if (std::isnan(tn))
continue;
413 for (std::size_t j = 0; j < ss.size(); ++j) {
414 const std::size_t s = ss[j];
415 out.
XN[c] = T(out.
XN[c] + T(
sn.rt((isf - 1) * K + (rr - 1),
416 (refsf - 1) * K + (s - 1)) *
425 double nJobsChain = 0.0;
426 for (std::size_t k = 0; k < K; ++k)
427 if (
sn.chains[c][k]) nJobsChain += njobs[k];
429 if (std::isinf(nJobsChain)) {
430 if (inchain.size() != completing.size())
432 "getAvgSys: edge-based chain definition is not supported for open queueing "
433 "networks -- the chain holds a non-completing class, so there is no single "
434 "flow whose reciprocal is the cycle time");
435 const std::size_t refst =
sn.classes[inchain[0] - 1].refstat;
437 for (std::size_t j = 0; j < inchain.size(); ++j) {
438 const T v = T(alpha(refst - 1, inchain[j] - 1) * CNclass[inchain[j] - 1]);
448 }
else if (nJobsChain > 0.0) {
453 "getAvgSys: the chain holds jobs but completes none, so its cycle time is "
454 "infinite, which exact arithmetic cannot represent; rerun in double");
473 const std::size_t M =
sn.nstations, C =
sn.nchains;
484 for (std::size_t c = 0; c < C; ++c) {
485 for (std::size_t j = 0; j <
sn.inchain[c].size(); ++j) {
486 const std::size_t k =
sn.inchain[c][j] - 1;
487 for (std::size_t i = 0; i < M; ++i) {
488 out.
QN(i, c) = T(out.
QN(i, c) + r.
QN(i, k));
489 out.
UN(i, c) = T(out.
UN(i, c) + r.
UN(i, k));
490 out.
WN(i, c) = T(out.
WN(i, c) + r.
WN(i, k));
491 out.
AN(i, c) = T(out.
AN(i, c) + r.
AN(i, k));
492 out.
TN(i, c) = T(out.
TN(i, c) + r.
TN(i, k));
493 out.
RN(i, c) = T(out.
RN(i, c) + T(r.
RN(i, k) * dem.
alpha(i, k)));
514 const std::size_t I =
sn.nodes.size(), C =
sn.nchains;
527 std::vector<std::size_t> node_to_station(I, 0);
528 for (std::size_t ist = 0; ist <
sn.nstations; ++ist) {
529 const std::size_t ind =
sn.station_to_node[ist];
530 if (ind) node_to_station[ind - 1] = ist + 1;
532 for (std::size_t c = 0; c < C; ++c) {
533 for (std::size_t j = 0; j <
sn.inchain[c].size(); ++j) {
534 const std::size_t k =
sn.inchain[c][j] - 1;
535 for (std::size_t i = 0; i < I; ++i) {
536 out.
QN(i, c) = T(out.
QN(i, c) + QNn(i, k));
537 out.
UN(i, c) = T(out.
UN(i, c) + UNn(i, k));
538 out.
AN(i, c) = T(out.
AN(i, c) + ANn(i, k));
539 out.
TN(i, c) = T(out.
TN(i, c) + TNn(i, k));
540 const std::size_t ist = node_to_station[i];
542 out.
RN(i, c) = T(out.
RN(i, c) + T(RNn(i, k) * dem.
alpha(ist - 1, k)));
545 out.
WN(i, c) = T(out.
WN(i, c) + WNn(i, k));
554 std::vector<std::string> out;
555 for (std::size_t c = 0; c < nchains; ++c) out.push_back(
"Chain" + std::to_string(c + 1));
562 std::vector<std::string> out;
563 for (std::size_t c = 0; c <
sn.nchains; ++c) {
565 for (std::size_t j = 0; j <
sn.inchain[c].size(); ++j) {
567 s +=
sn.classes[
sn.inchain[c][j] - 1].name;
569 out.push_back(s +
")");
NumericError(const std::string &what)
UnsupportedError(const std::string &what)
A network plus its refreshed NetworkStruct.
The exception types the port throws.
Dense matrix and non-owning view.
bool in(const std::string &s, std::initializer_list< const char * > set)
ChainDemands< T > sn_get_demands_chain(const qn::NetworkStruct< T > &L)
Port of sn_get_demands_chain.
std::vector< std::string > chain_class_labels(const qn::NetworkStruct< T > &sn)
(ClassA ClassB), the JobClasses column: which classes a chain holds.
SysResult< T > solver_get_avg_sys(const qn::NetworkStruct< T > &sn, const mva::AvgResult< T > &r)
Port of @@NetworkSolver/getAvgSys.m.
ChainResult< T > solver_get_avg_node_chain(const qn::NetworkStruct< T > &sn, const Matrix< T > &QNn, const Matrix< T > &UNn, const Matrix< T > &RNn, const Matrix< T > &WNn, const Matrix< T > &ANn, const Matrix< T > &TNn)
Port of @@NetworkSolver/getAvgNodeChain.m: the NODE table aggregated by chain.
ChainResult< T > solver_get_avg_chain(const qn::NetworkStruct< T > &sn, const mva::AvgResult< T > &r)
Port of @@NetworkSolver/getAvgChain.m: the station table aggregated by chain.
std::vector< std::string > chain_names(std::size_t nchains)
Chain1, Chain2, ... – the reference's own chain labels.
Conservation laws of a layered queueing network, enumerated from its structure.
A queueing network and its refreshed NetworkStruct.
Number-type abstraction for the templated API port.
Chain aggregation and de-aggregation.
The SolverMVA class surface: @@SolverMVA/runAnalyzer.m and the gates around it.
The <kind> part of a LINE:<category>:<kind> identifier (see _kb/17-error-vocabulary....
The metrics getAvg returns, after filtering.
Matrix< T > RN
response time, per visit
Matrix< T > UN
utilization
Matrix< T > WN
residence time, per job
Matrix< T > QN
queue length
Matrix< T > AN
arrival rate
The chain-level view of a layer, as sn_get_demands_chain returns it.
Matrix< T > alpha
(M x K) class share of its chain's visits at a station
The station- or node-level table aggregated by chain.
Matrix< T > TN
(rows x nchains), rows = stations or nodes
@@NetworkSolver/getAvgSys: one response time and one throughput per chain.
std::vector< T > XN
(nchains) system throughput at the reference station
std::vector< T > CN
(nchains) system response time, i.e. the cycle time