5#ifndef LINE_SOLVERS_SOLVER_CHAIN_TABLES_H
6#define LINE_SOLVERS_SOLVER_CHAIN_TABLES_H
62namespace chain_detail {
87T exp_max_mean(
const std::vector<T>& branch_times) {
88 const std::size_t n = branch_times.size();
89 const T zero = num_traits<T>::from_int(0), one = num_traits<T>::from_int(1);
90 if (n == 0)
return zero;
93 "getAvgSys: the fork-join section has " + std::to_string(n) +
94 " parallel paths, and the reference's synchronization delay is an "
95 "inclusion-exclusion sum of 2^n alternating terms; past 20 paths that sum has lost "
96 "every significant digit to cancellation, so it is refused rather than reported");
98 std::vector<T> lambda(n);
99 for (std::size_t i = 0; i < n; ++i) {
100 const double ri = num_traits<T>::to_double(branch_times[i]);
103 "getAvgSys: a path from the fork to the join has non-positive total response "
104 "time, so its exponential rate is not defined; the branch carries no station "
105 "with a finite response time");
106 lambda[i] = T(one / branch_times[i]);
110 const unsigned long long total = 1ull << n;
111 for (
unsigned long long mask = 1; mask < total; ++mask) {
113 std::size_t bits = 0;
114 for (std::size_t i = 0; i < n; ++i)
115 if (mask & (1ull << i)) {
116 rate = T(rate + lambda[i]);
119 const T term = T(one / rate);
131 std::vector<T> times;
132 std::vector<std::size_t> stations;
161PathsResult<T> paths_cs(
const qn::NetworkStruct<T>& sn,
const Matrix<T>& P, std::size_t cur,
162 std::size_t stop, std::size_t cls, Matrix<T>& RN,
const T& elapsed,
163 const std::vector<std::size_t>& acc_stations,
164 std::vector<std::pair<std::size_t, std::size_t>>& on_path) {
165 const std::size_t K = sn.nclasses, N = sn.nodes.size();
166 const T zero = num_traits<T>::from_int(0);
170 out.times.push_back(elapsed);
171 out.stations = acc_stations;
174 for (std::size_t i = 0; i < on_path.size(); ++i)
175 if (on_path[i].first == cur && on_path[i].second == cls)
177 "getAvgSys: the fork-join section of node '" + sn.nodes[cur - 1].name +
178 "' contains a routing cycle, so the set of paths from the fork to the join is "
179 "infinite and the synchronization delay has no inclusion-exclusion form");
180 on_path.push_back(std::make_pair(cur, cls));
183 std::vector<std::size_t> stations = acc_stations;
184 const std::size_t ist = sn.nodes[cur - 1].station;
186 here = RN(ist - 1, cls - 1);
187 stations.push_back(ist);
190 const std::size_t row = (cur - 1) * K + (cls - 1);
191 for (std::size_t nxt = 1; nxt <= N; ++nxt) {
192 for (std::size_t s = 1; s <= K; ++s) {
193 if (row >= P.rows())
break;
194 const std::size_t col = (nxt - 1) * K + (s - 1);
195 if (col >= P.cols())
continue;
196 if (num_traits<T>::to_double(P(row, col)) == 0.0)
continue;
198 std::size_t hop = nxt;
199 T entry = T(elapsed + here);
201 std::size_t inner_join = 0;
202 for (std::size_t k = 0; k < sn.fj.size(); ++k)
203 if (sn.fj[k].first == nxt) inner_join = sn.fj[k].second;
204 if (inner_join != 0) {
205 const std::size_t jst = sn.nodes[inner_join - 1].station;
206 if (jst != 0 && num_traits<T>::to_double(RN(jst - 1, s - 1)) == 0.0) {
207 std::vector<std::pair<std::size_t, std::size_t>> inner_path;
208 const PathsResult<T> in =
209 paths_cs(sn, P, nxt, inner_join, s, RN, zero,
210 std::vector<std::size_t>(), inner_path);
211 RN(jst - 1, s - 1) = exp_max_mean(in.times);
212 for (std::size_t q = 0; q < in.stations.size(); ++q)
213 RN(in.stations[q] - 1, s - 1) = zero;
218 const PathsResult<T> sub = paths_cs(sn, P, hop, stop, s, RN, entry, stations, on_path);
219 out.times.insert(out.times.end(), sub.times.begin(), sub.times.end());
220 out.stations.insert(out.stations.end(), sub.stations.begin(), sub.stations.end());
257 const std::size_t M =
sn.nstations, K =
sn.nclasses, C =
sn.nchains;
259 const std::vector<double> njobs =
sn.njobs();
261 bool has_join =
false, has_fork =
false;
262 for (std::size_t i = 0; i <
sn.nodes.size(); ++i) {
272 for (std::size_t i = 0; i < M; ++i)
274 for (std::size_t c = 0; c < K; ++c) RN(i, c) = zero;
285 if (has_fork && has_join) {
286 for (std::size_t f = 0; f <
sn.fj.size(); ++f) {
287 const std::size_t fork =
sn.fj[f].first, join =
sn.fj[f].second;
288 if (fork == 0 || join == 0)
continue;
289 const std::size_t jst =
sn.nodes[join - 1].station;
290 if (jst == 0)
continue;
291 for (std::size_t c = 0; c < C; ++c) {
292 double nJobsChain = 0.0;
293 for (std::size_t k = 0; k < K; ++k)
294 if (
sn.chains[c][k]) nJobsChain += njobs[k];
295 if (!std::isinf(nJobsChain))
continue;
296 for (std::size_t q = 0; q <
sn.inchain[c].size(); ++q) {
297 const std::size_t rr =
sn.inchain[c][q];
299 std::vector<std::pair<std::size_t, std::size_t>> on_path;
300 const chain_detail::PathsResult<T> paths =
301 chain_detail::paths_cs(
sn,
sn.rtnodes, fork, join, rr, RN, zero,
302 std::vector<std::size_t>(), on_path);
303 if (paths.times.empty())
continue;
306 RN(jst - 1, rr - 1) = chain_detail::exp_max_mean(paths.times);
307 for (std::size_t j = 0; j < paths.stations.size(); ++j)
308 RN(paths.stations[j] - 1, rr - 1) = zero;
317 std::vector<T> CNclass(K, zero);
318 for (std::size_t c = 0; c < C; ++c) {
319 for (std::size_t j = 0; j <
sn.inchain[c].size(); ++j) {
320 const std::size_t rr =
sn.inchain[c][j];
321 const std::size_t refst =
sn.classes[rr - 1].refstat;
322 if (refst == 0)
continue;
323 const std::size_t refsf =
sn.stateful_of_station(refst);
324 const T vref =
sn.visits[c](refsf - 1, rr - 1);
326 for (std::size_t i = 0; i < M; ++i) {
328 if (std::isinf(njobs[rr - 1]) && i + 1 == refst)
continue;
329 const std::size_t isf =
sn.stateful_of_station(i + 1);
331 T(CNclass[rr - 1] + T(T(
sn.visits[c](isf - 1, rr - 1) * RN(i, rr - 1)) / vref));
343 std::vector<std::size_t> refclass_nz;
344 for (std::size_t c = 0; c < C; ++c)
345 if (
sn.refclass[c] != 0) refclass_nz.push_back(c + 1);
346 for (std::size_t c = 0; c < C; ++c) {
350 std::vector<std::size_t> completing;
351 for (std::size_t k = 0; k < K; ++k)
352 if (
sn.chains[c][k] &&
sn.classes[k].completes) completing.push_back(k + 1);
354 std::vector<std::size_t> ks;
355 for (std::size_t j = 0; j < refclass_nz.size(); ++j)
356 for (std::size_t q = 0; q <
sn.inchain[c].size(); ++q)
357 if (refclass_nz[j] ==
sn.inchain[c][q]) ks.push_back(refclass_nz[j]);
358 if (
sn.refclass[c] == 0) ks =
sn.inchain[c];
360 for (std::size_t i = 0; i < M; ++i) {
361 for (std::size_t j = 0; j < ks.size(); ++j) {
362 const std::size_t k = ks[j];
363 const std::size_t refst =
sn.classes[k - 1].refstat;
364 if (refst == 0)
continue;
365 const std::size_t refsf =
sn.stateful_of_station(refst);
367 for (std::size_t q = 0; q < completing.size(); ++q)
368 denom = T(denom +
sn.visits[c](refsf - 1, completing[q] - 1));
370 const std::size_t isf =
sn.stateful_of_station(i + 1);
371 alpha(i, k - 1) = T(alpha(i, k - 1) + T(
sn.visits[c](isf - 1, k - 1) / denom));
377 for (std::size_t i = 0; i < M; ++i)
378 for (std::size_t k = 0; k < K; ++k)
382 out.
CN.assign(C, zero);
383 out.
XN.assign(C, zero);
384 const std::size_t nsf =
sn.nof_stateful();
386 for (std::size_t c = 0; c < C; ++c) {
387 const std::vector<std::size_t>& inchain =
sn.inchain[c];
388 if (inchain.empty())
continue;
389 std::vector<std::size_t> completing;
390 for (std::size_t k = 0; k < K; ++k)
391 if (
sn.chains[c][k] &&
sn.classes[k].completes) completing.push_back(k + 1);
397 const std::size_t ref =
sn.classes[inchain[0] - 1].refstat;
398 if (ref != 0 && r.
TN.rows() == M) {
399 const std::size_t refsf =
sn.stateful_of_station(ref);
400 std::vector<std::size_t> ss;
401 for (std::size_t j = 0; j < refclass_nz.size(); ++j)
402 for (std::size_t q = 0; q < inchain.size(); ++q)
403 if (refclass_nz[j] == inchain[q]) ss.push_back(refclass_nz[j]);
404 if (ss.empty()) ss = inchain;
405 for (std::size_t i = 0; i < M; ++i) {
406 const std::size_t isf =
sn.stateful_of_station(i + 1);
407 if (isf == 0 || isf > nsf)
continue;
408 for (std::size_t q = 0; q < completing.size(); ++q) {
409 const std::size_t rr = completing[q];
411 if (std::isnan(tn))
continue;
412 for (std::size_t j = 0; j < ss.size(); ++j) {
413 const std::size_t s = ss[j];
414 out.
XN[c] = T(out.
XN[c] + T(
sn.rt((isf - 1) * K + (rr - 1),
415 (refsf - 1) * K + (s - 1)) *
424 double nJobsChain = 0.0;
425 for (std::size_t k = 0; k < K; ++k)
426 if (
sn.chains[c][k]) nJobsChain += njobs[k];
428 if (std::isinf(nJobsChain)) {
429 if (inchain.size() != completing.size())
431 "getAvgSys: edge-based chain definition is not supported for open queueing "
432 "networks -- the chain holds a non-completing class, so there is no single "
433 "flow whose reciprocal is the cycle time");
434 const std::size_t refst =
sn.classes[inchain[0] - 1].refstat;
436 for (std::size_t j = 0; j < inchain.size(); ++j) {
437 const T v = T(alpha(refst - 1, inchain[j] - 1) * CNclass[inchain[j] - 1]);
445 out.
CN[c] = x == 0.0 ? zero
462 const std::size_t M =
sn.nstations, C =
sn.nchains;
473 for (std::size_t c = 0; c < C; ++c) {
474 for (std::size_t j = 0; j <
sn.inchain[c].size(); ++j) {
475 const std::size_t k =
sn.inchain[c][j] - 1;
476 for (std::size_t i = 0; i < M; ++i) {
477 out.
QN(i, c) = T(out.
QN(i, c) + r.
QN(i, k));
478 out.
UN(i, c) = T(out.
UN(i, c) + r.
UN(i, k));
479 out.
WN(i, c) = T(out.
WN(i, c) + r.
WN(i, k));
480 out.
AN(i, c) = T(out.
AN(i, c) + r.
AN(i, k));
481 out.
TN(i, c) = T(out.
TN(i, c) + r.
TN(i, k));
482 out.
RN(i, c) = T(out.
RN(i, c) + T(r.
RN(i, k) * dem.
alpha(i, k)));
503 const std::size_t I =
sn.nodes.size(), C =
sn.nchains;
516 std::vector<std::size_t> node_to_station(I, 0);
517 for (std::size_t ist = 0; ist <
sn.nstations; ++ist) {
518 const std::size_t ind =
sn.station_to_node[ist];
519 if (ind) node_to_station[ind - 1] = ist + 1;
521 for (std::size_t c = 0; c < C; ++c) {
522 for (std::size_t j = 0; j <
sn.inchain[c].size(); ++j) {
523 const std::size_t k =
sn.inchain[c][j] - 1;
524 for (std::size_t i = 0; i < I; ++i) {
525 out.
QN(i, c) = T(out.
QN(i, c) + QNn(i, k));
526 out.
UN(i, c) = T(out.
UN(i, c) + UNn(i, k));
527 out.
AN(i, c) = T(out.
AN(i, c) + ANn(i, k));
528 out.
TN(i, c) = T(out.
TN(i, c) + TNn(i, k));
529 const std::size_t ist = node_to_station[i];
531 out.
RN(i, c) = T(out.
RN(i, c) + T(RNn(i, k) * dem.
alpha(ist - 1, k)));
532 out.
WN(i, c) = T(out.
WN(i, c) + T(WNn(i, k) * dem.
alpha(ist - 1, k)));
541 std::vector<std::string> out;
542 for (std::size_t c = 0; c < nchains; ++c) out.push_back(
"Chain" + std::to_string(c + 1));
549 std::vector<std::string> out;
550 for (std::size_t c = 0; c <
sn.nchains; ++c) {
552 for (std::size_t j = 0; j <
sn.inchain[c].size(); ++j) {
554 s +=
sn.classes[
sn.inchain[c][j] - 1].name;
556 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.
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.
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 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