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];
288 std::vector<std::size_t> br;
289 for (std::size_t s :
tr.auxclasses)
290 if (
tr.fjforkmap[s] == fa &&
tr.fjclassmap[s] == r) br.push_back(s);
291 if (br.empty())
continue;
293 std::vector<T> ri(br.size(), zero);
294 for (std::size_t b = 0; b < br.size(); ++b) {
296 for (std::size_t i = 0; i < out.
R.rows(); ++i)
300 acc = T(acc - out.
R(adstat - 1, br[b] - 1));
301 ri[b] = T(acc - out.
R(jstat - 1, br[b] - 1));
306 for (std::size_t i = 0; i < out.
R.rows(); ++i) {
308 for (std::size_t yi = 0; yi < ic.size(); ++yi) row += out.
R(i, ic[yi] - 1);
311 r0 = T(r0 - out.
R(jstat - 1, r - 1));
315 tr.forks[fa].fanOut))));
317 for (std::size_t b = 0; b < br.size(); ++b) {
318 T di = T(d0 - ri[b]);
319 if (di < zero) di = zero;
348 std::vector<bool> orig(
tr.V.classes.size() + 1,
false);
349 for (std::size_t s :
tr.auxclasses) orig[
tr.fjclassmap[s]] =
true;
351 std::vector<std::vector<T>> tnJoin(
tr.joinStations.size(),
352 std::vector<T>(L.
nclasses + 1, zero));
353 for (std::size_t a = 0; a <
tr.joinStations.size(); ++a) {
354 const std::size_t js =
tr.joinStations[a];
355 for (std::size_t r = 1; r <= L.
nclasses; ++r) {
356 if (!orig[r])
continue;
357 tnJoin[a][r] = out.
Tp(js - 1, r - 1);
358 out.
Q(js - 1, r - 1) = zero;
359 out.
R(js - 1, r - 1) = zero;
360 out.
Tp(js - 1, r - 1) = zero;
361 out.
U(js - 1, r - 1) = zero;
364 const std::size_t nrows = out.
Tp.rows();
365 for (std::size_t s :
tr.auxclasses) {
366 const std::size_t r =
tr.fjclassmap[s];
367 for (std::size_t i = 0; i < nrows; ++i) {
368 out.
Q(i, r - 1) = T(out.
Q(i, r - 1) + out.
Q(i, s - 1));
369 out.
U(i, r - 1) = T(out.
U(i, r - 1) + out.
U(i, s - 1));
370 out.
Tp(i, r - 1) = T(out.
Tp(i, r - 1) + out.
Tp(i, s - 1));
372 out.
Tp(i, r - 1) != zero ? T(out.
Q(i, r - 1) / out.
Tp(i, r - 1)) : zero;
375 for (std::size_t a = 0; a <
tr.joinStations.size(); ++a) {
376 const std::size_t js =
tr.joinStations[a];
377 for (std::size_t r = 1; r <= L.
nclasses; ++r)
378 if (orig[r]) out.
Tp(js - 1, r - 1) = tnJoin[a][r];
400template <
class T,
class InnerSolve>
414 if (!
tr.heidelberger_trivedi) {
421 for (std::size_t i = 1; i <= L.
stations.size(); ++i)
422 for (std::size_t k = 1; k <= L.
classes.size(); ++k)
424 for (std::size_t s :
tr.auxclasses) {
425 const std::size_t r =
tr.fjclassmap[s];
426 for (std::size_t i = 1; i <= L.
stations.size(); ++i) {
427 if (
tr.is_join_station(i))
continue;
431 for (std::size_t js :
tr.joinStations)
432 for (std::size_t k = 1; k <= V.
classes.size(); ++k)
434 for (std::size_t s :
tr.auxclasses)
447 bool forkLoop =
true;
449 while (forkLoop && forkIter <
opt.iter_max) {
454 if (forkIter > 1 && !
tr.heidelberger_trivedi) {
457 for (std::size_t s :
tr.auxclasses) {
458 if (
tr.auxdisabled[s] || !(
tr.fanout[s] > 0.0))
continue;
468 if (QN_1.
rows() == QN.rows() && QN_1.
cols() == QN.cols()) {
470 for (std::size_t i = 0; i < QN.rows(); ++i)
471 for (std::size_t j = 0; j < QN.cols(); ++j)
475 "fork-join iteration %d: queue lengths moved by at most %.3e", forkIter, moved);
478 "fork-join iteration %d: transformed model rebuilt", forkIter);
489 if (
tr.heidelberger_trivedi) {
499 for (std::size_t fa = 0; fa <
tr.forks.size(); ++fa) {
500 const std::size_t fnode =
tr.forks[fa].node;
501 const std::size_t jstat =
tr.forks[fa].joinStation;
510 for (std::size_t s :
tr.auxclasses) {
511 if (
tr.fjforkmap[s] != fa)
continue;
512 lam[s] = T((lam[s] + tnfork[
tr.fjclassmap[s] - 1]) / two);
517 for (std::size_t s :
tr.auxclasses) {
518 if (
tr.fjforkmap[s] != fa)
continue;
519 const std::size_t r =
tr.fjclassmap[s];
521 for (std::size_t s2 :
tr.auxclasses)
522 if (
tr.fjclassmap[s2] == r) acc += out.
Tp(jstat - 1, s2 - 1);
523 out.
Tp(jstat - 1, r - 1) =
524 T(out.
Tp(jstat - 1, r - 1) + acc - out.
Tp(jstat - 1, s - 1));
525 lam[s] = T((lam[s] + out.
Tp(jstat - 1, r - 1)) / two);
530 if (!
tr.forks[fa].outer[r])
continue;
533 std::vector<std::size_t> merge{r, s};
545 double wd =
tr.forks[fa].fanOut;
546 std::size_t w = (wd >= 1.0) ?
static_cast<std::size_t
>(wd + 0.5) : 1;
548 const std::vector<T> base = ri;
549 for (std::size_t k = 1; k < w; ++k)
550 ri.insert(ri.end(), base.begin(), base.end());
553 for (
const T& x : ri) mean += x;
561 const T raw = T(d0 - mean);
570 sync = raw > zero ? raw : zero;
583 const std::size_t nrows = out.
Tp.rows();
584 std::vector<std::vector<T>> tnJoin(
tr.joinStations.size(),
586 std::vector<T> tnSource(L.
nclasses, zero);
587 for (std::size_t a = 0; a <
tr.joinStations.size(); ++a)
588 for (std::size_t k = 0; k < L.
nclasses; ++k)
589 tnJoin[a][k] = out.
Tp(
tr.joinStations[a] - 1, k);
590 for (std::size_t k = 0; k < L.
nclasses; ++k)
591 tnSource[k] = out.
Tp(
tr.sourceStation - 1, k);
592 for (std::size_t s :
tr.auxclasses) {
593 const std::size_t r =
tr.fjclassmap[s];
594 for (std::size_t i = 0; i < nrows; ++i) {
595 out.
Q(i, r - 1) = T(out.
Q(i, r - 1) + out.
Q(i, s - 1));
596 out.
U(i, r - 1) = T(out.
U(i, r - 1) + out.
U(i, s - 1));
597 out.
Tp(i, r - 1) = T(out.
Tp(i, r - 1) + out.
Tp(i, s - 1));
598 out.
R(i, r - 1) = out.
Tp(i, r - 1) != zero
599 ? T(out.
Q(i, r - 1) / out.
Tp(i, r - 1))
603 for (std::size_t a = 0; a <
tr.joinStations.size(); ++a)
604 for (std::size_t k = 0; k < L.
nclasses; ++k)
605 out.
Tp(
tr.joinStations[a] - 1, k) = tnJoin[a][k];
606 for (std::size_t k = 0; k < L.
nclasses; ++k)
607 out.
Tp(
tr.sourceStation - 1, k) = tnSource[k];
621 for (std::size_t i = 0; i < out.
Q.rows(); ++i)
622 for (std::size_t k = 0; k < out.
Q.cols(); ++k)
628 "SolverMVA: the fork-join fixed point of model '" + L.
name +
629 "' returned a non-finite metric; the auxiliary open classes the transform "
630 "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.
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.