5#ifndef LINE_SOLVERS_MVA_FJ_DRIVER_H
6#define LINE_SOLVERS_MVA_FJ_DRIVER_H
57 const std::vector<std::size_t>& merge,
const Matrix<T>& QN,
60 const std::size_t st = V.
nodes[nd - 1].station;
61 if (st == 0)
return zero;
63 for (std::size_t k : merge) {
64 q += QN(st - 1, k - 1);
65 t += TN(st - 1, k - 1);
67 if (!(t > zero))
return zero;
97 const T& t0, std::vector<T>& out,
98 std::vector<std::pair<std::size_t, std::size_t>> visited) {
101 if (curNode == endNode) {
102 out.push_back(T(t0 -
fj_node_time(V, curNode, merge, QN, TN)));
105 const std::pair<std::size_t, std::size_t> here(curNode, curClass);
106 if (std::find(visited.begin(), visited.end(), here) != visited.end())
return;
107 visited.push_back(here);
109 for (std::size_t s = 1; s <= V.
classes.size(); ++s)
110 for (std::size_t nd = 1; nd <= V.
nodes.size(); ++nd) {
114 if (!(detail::fj_route_cs(V, curClass, s, curNode, nd) > zero))
continue;
115 std::vector<std::size_t> m2 = merge;
120 std::size_t inner =
tr.forks.size();
121 for (std::size_t b = 0; b <
tr.forks.size(); ++b)
122 if (
tr.forks[b].node == nd &&
tr.forks[b].joinNode != 0) inner = b;
124 if (inner <
tr.forks.size()) {
125 const std::size_t ijoin =
tr.forks[inner].joinNode;
126 const std::size_t istat =
tr.forks[inner].joinStation;
128 std::size_t iaux = 0;
129 for (std::size_t x :
tr.auxclasses)
130 if (
tr.fjforkmap[x] == inner &&
tr.fjclassmap[x] == s) iaux = x;
131 std::vector<std::size_t> m3 = m2;
132 if (iaux != 0) m3.push_back(iaux);
134 std::vector<T> paths;
137 if (!paths.empty()) {
139 for (
const T& x : paths) mean += x;
152 for (std::size_t cls : m3) V.
set_service(istat, cls, d);
154 fj_find_paths(
tr, ijoin, endNode, s, m2, QN, TN, T(t0 + d0), out, visited);
166 for (std::size_t i = 0; i < A.
rows(); ++i)
167 for (std::size_t j = 0; j < n && j < A.
cols(); ++j) out(i, j) = A(i, j);
175 for (std::size_t i = 0; i < A.
rows(); ++i)
176 for (std::size_t j = 0; j < A.
cols(); ++j) {
214 std::vector<T> tnfork(L.
nclasses, zero);
215 const std::size_t pnode =
tr.forks[
tr.forks[fa].parent].node;
216 for (std::size_t c = 0; c < L.
nchains; ++c) {
217 const std::vector<std::size_t>& ic = L.
inchain[c];
218 for (std::size_t r : ic) {
219 const std::size_t rs = L.
classes[r - 1].refstat;
221 T den = zero, tsum = zero;
222 for (std::size_t k : ic) {
223 den += L.
visits[c](isf - 1, k - 1);
224 tsum += TN(rs - 1, k - 1);
226 if (!(den > zero))
continue;
227 tnfork[r - 1] = T(T(L.
nodevisits[c](pnode - 1, r - 1) / den) * tsum);
272 for (std::size_t fa = 0; fa <
tr.forks.size(); ++fa) {
273 const std::size_t fnode =
tr.forks[fa].node;
274 const std::size_t jnode =
tr.forks[fa].joinNode;
275 if (jnode == 0)
continue;
276 const std::size_t jstat =
tr.forks[fa].joinStation;
277 const std::size_t adstat =
tr.auxDelayStation[jnode];
278 for (std::size_t c = 0; c < L.
nchains; ++c) {
279 const std::vector<std::size_t>& ic = L.
inchain[c];
280 for (std::size_t xi = 0; xi < ic.size(); ++xi) {
281 const std::size_t r = ic[xi];
299 std::vector<std::size_t> br, foreign;
301 for (std::size_t s :
tr.auxclasses) {
302 if (
tr.fjclassmap[s] != r)
continue;
305 foreign.push_back(s);
309 for (std::size_t i = 0; i < out.
R.rows(); ++i)
313 acc = T(acc - out.
R(adstat - 1, s - 1));
315 ri.push_back(T(acc - out.
R(jstat - 1, s - 1)));
317 if (br.empty())
continue;
321 for (std::size_t i = 0; i < out.
R.rows(); ++i) {
323 for (std::size_t yi = 0; yi < ic.size(); ++yi) row += out.
R(i, ic[yi] - 1);
326 r0 = T(r0 - out.
R(jstat - 1, r - 1));
330 tr.forks[fa].fanOut))));
332 for (std::size_t b = 0; b < br.size(); ++b) {
333 T di = T(d0 - ri[b]);
334 if (di < zero) di = zero;
353 for (std::size_t s : foreign) {
382 std::vector<bool> orig(
tr.V.classes.size() + 1,
false);
383 for (std::size_t s :
tr.auxclasses) orig[
tr.fjclassmap[s]] =
true;
385 std::vector<std::vector<T>> tnJoin(
tr.joinStations.size(),
386 std::vector<T>(L.
nclasses + 1, zero));
387 for (std::size_t a = 0; a <
tr.joinStations.size(); ++a) {
388 const std::size_t js =
tr.joinStations[a];
389 for (std::size_t r = 1; r <= L.
nclasses; ++r) {
390 if (!orig[r])
continue;
391 tnJoin[a][r] = out.
Tp(js - 1, r - 1);
392 out.
Q(js - 1, r - 1) = zero;
393 out.
R(js - 1, r - 1) = zero;
394 out.
Tp(js - 1, r - 1) = zero;
395 out.
U(js - 1, r - 1) = zero;
398 const std::size_t nrows = out.
Tp.rows();
399 for (std::size_t s :
tr.auxclasses) {
400 const std::size_t r =
tr.fjclassmap[s];
401 for (std::size_t i = 0; i < nrows; ++i) {
402 out.
Q(i, r - 1) = T(out.
Q(i, r - 1) + out.
Q(i, s - 1));
403 out.
U(i, r - 1) = T(out.
U(i, r - 1) + out.
U(i, s - 1));
404 out.
Tp(i, r - 1) = T(out.
Tp(i, r - 1) + out.
Tp(i, s - 1));
406 out.
Tp(i, r - 1) != zero ? T(out.
Q(i, r - 1) / out.
Tp(i, r - 1)) : zero;
409 for (std::size_t a = 0; a <
tr.joinStations.size(); ++a) {
410 const std::size_t js =
tr.joinStations[a];
411 for (std::size_t r = 1; r <= L.
nclasses; ++r)
412 if (orig[r]) out.
Tp(js - 1, r - 1) = tnJoin[a][r];
434template <
class T,
class InnerSolve>
448 if (!
tr.heidelberger_trivedi) {
455 for (std::size_t i = 1; i <= L.
stations.size(); ++i)
456 for (std::size_t k = 1; k <= L.
classes.size(); ++k)
458 for (std::size_t s :
tr.auxclasses) {
459 const std::size_t r =
tr.fjclassmap[s];
460 for (std::size_t i = 1; i <= L.
stations.size(); ++i) {
461 if (
tr.is_join_station(i))
continue;
465 for (std::size_t js :
tr.joinStations)
466 for (std::size_t k = 1; k <= V.
classes.size(); ++k)
468 for (std::size_t s :
tr.auxclasses)
481 bool forkLoop =
true;
483 while (forkLoop && forkIter <
opt.iter_max) {
488 if (forkIter > 1 && !
tr.heidelberger_trivedi) {
491 for (std::size_t s :
tr.auxclasses) {
492 if (
tr.auxdisabled[s] || !(
tr.fanout[s] > 0.0))
continue;
502 if (QN_1.
rows() == QN.rows() && QN_1.
cols() == QN.cols()) {
504 for (std::size_t i = 0; i < QN.rows(); ++i)
505 for (std::size_t j = 0; j < QN.cols(); ++j)
509 "fork-join iteration %d: queue lengths moved by at most %.3e", forkIter, moved);
512 "fork-join iteration %d: transformed model rebuilt", forkIter);
523 if (
tr.heidelberger_trivedi) {
533 for (std::size_t fa = 0; fa <
tr.forks.size(); ++fa) {
534 const std::size_t fnode =
tr.forks[fa].node;
535 const std::size_t jstat =
tr.forks[fa].joinStation;
544 for (std::size_t s :
tr.auxclasses) {
545 if (
tr.fjforkmap[s] != fa)
continue;
546 lam[s] = T((lam[s] + tnfork[
tr.fjclassmap[s] - 1]) / two);
551 for (std::size_t s :
tr.auxclasses) {
552 if (
tr.fjforkmap[s] != fa)
continue;
553 const std::size_t r =
tr.fjclassmap[s];
555 for (std::size_t s2 :
tr.auxclasses)
556 if (
tr.fjclassmap[s2] == r) acc += out.
Tp(jstat - 1, s2 - 1);
557 out.
Tp(jstat - 1, r - 1) =
558 T(out.
Tp(jstat - 1, r - 1) + acc - out.
Tp(jstat - 1, s - 1));
559 lam[s] = T((lam[s] + out.
Tp(jstat - 1, r - 1)) / two);
564 if (!
tr.forks[fa].outer[r])
continue;
567 std::vector<std::size_t> merge{r, s};
579 double wd =
tr.forks[fa].fanOut;
580 std::size_t w = (wd >= 1.0) ?
static_cast<std::size_t
>(wd + 0.5) : 1;
582 const std::vector<T> base = ri;
583 for (std::size_t k = 1; k < w; ++k)
584 ri.insert(ri.end(), base.begin(), base.end());
587 for (
const T& x : ri) mean += x;
595 const T raw = T(d0 - mean);
604 sync = raw > zero ? raw : zero;
617 const std::size_t nrows = out.
Tp.rows();
618 std::vector<std::vector<T>> tnJoin(
tr.joinStations.size(),
620 std::vector<T> tnSource(L.
nclasses, zero);
621 for (std::size_t a = 0; a <
tr.joinStations.size(); ++a)
622 for (std::size_t k = 0; k < L.
nclasses; ++k)
623 tnJoin[a][k] = out.
Tp(
tr.joinStations[a] - 1, k);
624 for (std::size_t k = 0; k < L.
nclasses; ++k)
625 tnSource[k] = out.
Tp(
tr.sourceStation - 1, k);
626 for (std::size_t s :
tr.auxclasses) {
627 const std::size_t r =
tr.fjclassmap[s];
628 for (std::size_t i = 0; i < nrows; ++i) {
629 out.
Q(i, r - 1) = T(out.
Q(i, r - 1) + out.
Q(i, s - 1));
630 out.
U(i, r - 1) = T(out.
U(i, r - 1) + out.
U(i, s - 1));
631 out.
Tp(i, r - 1) = T(out.
Tp(i, r - 1) + out.
Tp(i, s - 1));
632 out.
R(i, r - 1) = out.
Tp(i, r - 1) != zero
633 ? T(out.
Q(i, r - 1) / out.
Tp(i, r - 1))
637 for (std::size_t a = 0; a <
tr.joinStations.size(); ++a)
638 for (std::size_t k = 0; k < L.
nclasses; ++k)
639 out.
Tp(
tr.joinStations[a] - 1, k) = tnJoin[a][k];
640 for (std::size_t k = 0; k < L.
nclasses; ++k)
641 out.
Tp(
tr.sourceStation - 1, k) = tnSource[k];
655 for (std::size_t i = 0; i < out.
Q.rows(); ++i)
656 for (std::size_t k = 0; k < out.
Q.cols(); ++k)
662 "SolverMVA: the fork-join fixed point of model '" + L.
name +
663 "' returned a non-finite metric; the auxiliary open classes the transform "
664 "adds have saturated one of the branch stations");
NumericError(const std::string &what)
A network plus its refreshed NetworkStruct.
std::vector< Matrix< T > > nodevisits
(nchains) each (nnodes x nclasses)
std::size_t stateful_of_station(std::size_t st) const
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< JobClass > classes
std::vector< Station< T > > stations
stations[k-1] is the k-th station
void set_service(std::size_t station, std::size_t cls, const Distrib< T > &d)
std::vector< std::vector< std::size_t > > inchain
1-based class indices per chain
std::vector< NodeDef > nodes
every node, in creation order
std::vector< Matrix< T > > visits
(nchains) each (nstateful x nclasses)
static bool owns_log()
True only inside the OUTERMOST open run; gates EMISSION.
static void step(const char *fmt,...)
Write one progress line.
The exception types the port throws.
The fork-join transform SolverMVA applies before solving a layer that contains a Fork.
Running progress log of a LINE solver run (the "solver console").
void fj_find_paths(FjMmt< T > &tr, std::size_t curNode, std::size_t endNode, std::size_t curClass, const std::vector< std::size_t > &merge, const Matrix< T > &QN, const Matrix< T > &TN, const T &t0, std::vector< T > &out, std::vector< std::pair< std::size_t, std::size_t > > visited)
Port of ModelAdapter.findPathsCS: the response time along every path from a fork to ITS join,...
void fj_ht_sync_delays(const qn::NetworkStruct< T > &L, FjMmt< T > &tr, const MvaSolution< T > &out)
Port of the heidelberger-trivedi arm of fjFixedPoint.m:212-255: the synchronisation delays of one pas...
Matrix< T > fj_leading_cols(const Matrix< T > &A, std::size_t n)
The leading n columns of a class-indexed metric.
Distrib< T > fj_exp_fit_mean(const T &mean)
Exp.fitMean(m), including its clamp.
T fj_expected_ordstat(const std::vector< T > &means, std::size_t k)
The instant the join fires: E[X_(k)] of independent exponentials with the given means,...
bool fj_converged(const Matrix< T > &A, const Matrix< T > &B, double iter_tol)
The mixed absolute/relative stopping test of the fork-join loop.
std::size_t fj_join_quorum(const qn::NetworkStruct< T > &sn, std::size_t joinNode, std::size_t nbranches)
The number of siblings the Join node fires on, out of nbranches forked.
void fj_ht_merge(const qn::NetworkStruct< T > &L, const FjMmt< T > &tr, MvaSolution< T > &out)
Port of the heidelberger-trivedi arm of fjFixedPoint.m:263-291: fold the auxiliary columns back into ...
std::vector< T > fj_fork_tput(const qn::NetworkStruct< T > &L, const FjMmt< T > &tr, const Matrix< T > &TN, std::size_t fa)
Port of fjFixedPoint.m:130-136: the firing throughput of fork fa, per class of the BASE model.
T fj_finite_or_zero(const T &x)
A metric read out of a solve, with a non-finite entry read as zero.
T fj_node_time(const qn::NetworkStruct< T > &V, std::size_t nd, const std::vector< std::size_t > &merge, const Matrix< T > &QN, const Matrix< T > &TN)
Port of ModelAdapter.findPathsCS' per-node charge: the queue length of the merge set at this station ...
MvaSolution< T > fj_fixed_point(const qn::NetworkStruct< T > &L, FjMmt< T > &tr, std::vector< T > &lam, const MvaOptions &opt, InnerSolve inner)
Drive the fork-join fixed point of a transformed model to convergence.
Conservation laws of a layered queueing network, enumerated from its structure.
A queueing network and its refreshed NetworkStruct.
SolverMVA over a SolverLN layer.
The MATLAB GlobalConstants, as reported by lineStart at its defaults.
static Distrib exp_rate(const T &r)
static Distrib disabled_dist()
static Distrib immediate()
The Immediate singleton.
The transformed layer and the bookkeeping the fixed point needs to drive it and to merge its results ...
static constexpr double Immediate
Rate of an Immediate distribution; its mean is 1/Immediate = 1e-8.
static constexpr double FineTol
static constexpr double Zero
The options SolverMVA reads.
Class-level results, the [Q,U,R,T,C,X] of the MATLAB analyzers.