5#ifndef LINE_SOLVERS_MVA_FJ_MMT_H
6#define LINE_SOLVERS_MVA_FJ_MMT_H
98using lang::GlobalConstants;
228 std::size_t nbranches) {
229 if (joinNode == 0)
return nbranches;
230 typename std::map<std::size_t, typename qn::NetworkStruct<T>::JoinDecl>::const_iterator it =
231 sn.joindecl.find(joinNode);
232 if (it ==
sn.joindecl.end())
return nbranches;
234 const double q = it->second.quorum;
236 const std::size_t k =
static_cast<std::size_t
>(q + 0.5);
237 if (k > 0 && k < nbranches)
return k;
258 if (rate < lo) rate = lo;
259 if (rate > hi) rate = hi;
286 const typename std::map<std::size_t, Matrix<T>>::const_iterator it = V.
csmatrix.find(i);
289 if (r > C.
rows() || s > C.
cols())
return zero;
290 if (!(C(r - 1, s - 1) > zero))
return zero;
291 return T(C(r - 1, s - 1) * V.
get_route(s, s, i, j));
296void fj_clear_row(qn::NetworkStruct<T>& V, std::size_t r, std::size_t s, std::size_t nd) {
297 auto it = V.P.find(std::make_pair(r, s));
298 if (it == V.P.end() || it->second.rows() < nd)
return;
299 const T zero = num_traits<T>::from_int(0);
300 for (std::size_t j = 0; j < it->second.cols(); ++j) it->second(nd - 1, j) = zero;
321void fj_nested_forks(
const qn::NetworkStruct<T>& V,
const std::vector<bool>& isFork,
322 std::size_t startNode, std::size_t endNode, std::size_t cls,
323 std::vector<bool>& flags, std::vector<bool>& seen) {
324 if (startNode == endNode)
return;
325 if (seen[startNode - 1])
return;
326 seen[startNode - 1] =
true;
327 const T zero = num_traits<T>::from_int(0);
328 for (std::size_t j = 1; j <= V.nodes.size(); ++j) {
329 if (!(V.get_route(cls, cls, startNode, j) > zero))
continue;
334 if (isFork[j - 1]) flags[j - 1] =
false;
335 fj_nested_forks(V, isFork, j, endNode, cls, flags, seen);
359 const std::size_t nf =
tr.forks.size();
360 std::vector<bool> isFork(V.
nodes.size(),
false);
361 for (std::size_t a = 0; a < nf; ++a) {
362 tr.forks[a].outer.assign(
tr.norig + 1,
true);
363 tr.forks[a].parent = a;
364 isFork[
tr.forks[a].node - 1] =
true;
366 for (std::size_t a = 0; a < nf; ++a) {
372 for (std::size_t s :
tr.auxclasses) {
373 if (
tr.fjforkmap[s] != a)
continue;
374 const std::size_t r =
tr.fjclassmap[s];
375 std::vector<bool> flags(V.
nodes.size(),
false);
376 for (std::size_t b = 0; b < nf; ++b) flags[
tr.forks[b].node - 1] =
true;
377 std::vector<bool> seen(V.
nodes.size(),
false);
378 detail::fj_nested_forks(V, isFork, F.
node, F.
joinNode, r, flags, seen);
379 for (std::size_t b = 0; b < nf; ++b) {
380 if (flags[
tr.forks[b].node - 1])
continue;
381 tr.forks[b].outer[r] =
false;
411 const std::size_t Kaug = V.
classes.size();
412 for (
typename std::map<std::size_t,
Matrix<T>>::iterator it = V.
csmatrix.begin();
414 if (it->second.rows() == Kaug && it->second.cols() == Kaug)
continue;
417 for (std::size_t x = 0; x < old.
rows() && x < Kaug; ++x)
418 for (std::size_t y = 0; y < old.
cols() && y < Kaug; ++y) C(x, y) = old(x, y);
419 for (std::size_t a = old.
rows(); a < Kaug; ++a) C(a, a) = one;
420 for (std::size_t ai = 0; ai <
tr.auxclasses.size(); ++ai)
421 for (std::size_t bi = 0; bi <
tr.auxclasses.size(); ++bi) {
422 const std::size_t a =
tr.auxclasses[ai], b =
tr.auxclasses[bi];
423 if (a > Kaug || b > Kaug || a >=
tr.fjclassmap.size() || b >=
tr.fjclassmap.size())
425 const std::size_t x =
tr.fjclassmap[a], y =
tr.fjclassmap[b];
426 if (x == 0 || y == 0 || x > old.
rows() || y > old.
cols())
continue;
428 C(a - 1, b - 1) = old(x - 1, y - 1);
450 std::vector<std::size_t> forkNodes;
451 for (std::size_t i = 0; i < L.
nodes.size(); ++i)
452 if (L.
nodes[i].nodetype == NodeType::Fork) forkNodes.push_back(i + 1);
453 if (forkNodes.empty())
return tr;
456 for (std::size_t node : forkNodes) {
464 for (
const std::pair<std::size_t, std::size_t>& p : L.
fj) {
465 if (p.first != node)
continue;
468 std::to_string(node) +
469 " with more than one join station; the reference supports "
470 "one join per fork");
478 for (std::size_t r = 1; r <= L.
nclasses; ++r) {
479 std::vector<bool> dest(L.
nodes.size(),
false);
480 for (std::size_t s = 1; s <= L.
nclasses; ++s)
481 for (std::size_t j = 1; j <= L.
nodes.size(); ++j)
482 if (L.
get_route(r, s, node, j) > zero) dest[j - 1] =
true;
486 tr.forks.push_back(F);
497 for (std::size_t r = 1; r <= L.
nclasses; ++r) {
500 for (std::size_t s = 1; s <= L.
nclasses; ++s)
501 for (std::size_t j = 1; j <= L.
nodes.size(); ++j) {
506 V.
nodes[F.
node - 1].nodetype = NodeType::Router;
514 for (std::size_t j = 1; j <= L.
nodes.size(); ++j) {
515 if (L.
nodes[j - 1].nodetype != NodeType::Join)
continue;
516 const std::size_t st = V.
nodes[j - 1].station;
518 throw InputError(
"fj_mmt: the join node '" + V.
nodes[j - 1].name +
"' of layer '" +
519 L.
name +
"' is not a station");
520 V.
stations[st - 1].nodetype = NodeType::Delay;
521 V.
stations[st - 1].sched = SchedStrategy::INF;
522 V.
stations[st - 1].nservers = std::numeric_limits<double>::infinity();
523 V.
nodes[j - 1].nodetype = NodeType::Delay;
524 for (std::size_t k = 1; k <= V.
classes.size(); ++k)
526 tr.joinStations.push_back(st);
538 std::size_t existing_src = 0, existing_sink = 0;
539 for (std::size_t i = 1; i <= V.
stations.size(); ++i)
540 if (V.
stations[i - 1].nodetype == NodeType::Source) existing_src = i;
541 for (std::size_t j = 1; j <= V.
nodes.size(); ++j)
542 if (V.
nodes[j - 1].nodetype == NodeType::Sink) existing_sink = j;
543 if (existing_src != 0 && existing_sink != 0) {
544 tr.sourceStation = existing_src;
546 tr.sinkNode = existing_sink;
551 src.
sched = SchedStrategy::EXT;
555 tr.sinkNode = V.
add_node(
"Sink", NodeType::Sink,
false);
562 const std::size_t nnodes = V.
nodes.size();
563 for (std::size_t fa = 0; fa <
tr.forks.size(); ++fa) {
565 std::vector<bool> forked(L.
nclasses + 1,
false);
566 for (std::size_t c = 0; c < L.
nchains; ++c)
567 for (std::size_t r = 1; r <= L.
nclasses; ++r)
570 for (std::size_t c = 0; c < L.
nchains; ++c) {
572 for (std::size_t r : L.
inchain[c])
573 if (forked[r]) any =
true;
575 const std::vector<std::size_t>& ic = L.
inchain[c];
578 const std::size_t refstat = L.
classes[ic[0] - 1].refstat;
580 std::vector<std::vector<bool>> reach(L.
nodes.size(),
581 std::vector<bool>(L.
nclasses + 1,
false));
582 std::vector<std::pair<std::size_t, std::size_t>> q;
583 for (std::size_t r : ic) {
584 reach[refnode - 1][r] =
true;
585 q.emplace_back(refnode, r);
587 for (std::size_t h = 0; h < q.size(); ++h) {
588 const std::size_t nd = q[h].first, cl = q[h].second;
589 for (std::size_t j = 1; j <= L.
nodes.size(); ++j)
590 for (std::size_t s : ic)
591 if (!reach[j - 1][s] && detail::fj_route_cs(L, cl, s, nd, j) > zero) {
592 reach[j - 1][s] =
true;
593 q.emplace_back(j, s);
596 std::vector<std::size_t> aux(ic.size(), 0);
597 for (std::size_t a = 0; a < ic.size(); ++a) {
598 const std::size_t r = ic[a];
601 xc.
type = JobClassType::OPEN;
602 xc.
population = std::numeric_limits<double>::infinity();
611 for (std::size_t i = 1; i <= V.
stations.size(); ++i) {
612 if (i ==
tr.sourceStation)
continue;
616 if (
tr.is_join_station(i)) {
622 const bool disable = (F.
origfanout[r] == 0) || !reach[F.
node - 1][r];
628 tr.fjclassmap.resize(V.
classes.size() + 1, 0);
629 tr.fjforkmap.resize(V.
classes.size() + 1, 0);
630 tr.fanout.resize(V.
classes.size() + 1, 0.0);
631 tr.auxdisabled.resize(V.
classes.size() + 1,
false);
632 tr.fjclassmap[aux[a]] = r;
633 tr.fjforkmap[aux[a]] = fa;
635 tr.auxdisabled[aux[a]] = disable;
636 tr.auxclasses.push_back(aux[a]);
640 for (std::size_t x = 0; x < ic.size(); ++x)
641 for (std::size_t y = 0; y < ic.size(); ++y)
642 for (std::size_t i = 1; i <= nnodes; ++i)
643 for (std::size_t j = 1; j <= nnodes; ++j) {
644 const T p = V.
get_route(ic[x], ic[y], i, j);
645 if (p > zero) V.
set_route(aux[x], aux[y], i, j, p);
651 for (std::size_t x = 0; x < ic.size(); ++x) {
652 for (std::size_t y = 0; y < ic.size(); ++y) {
653 detail::fj_clear_row(V, aux[x], aux[y],
tr.sourceNode);
664 std::vector<std::vector<bool>> vis(nnodes, std::vector<bool>(L.
nclasses + 1,
false));
665 std::vector<std::pair<std::size_t, std::size_t>> bq;
666 for (std::size_t x = 0; x < ic.size(); ++x) {
667 const std::size_t r = ic[x];
669 for (std::size_t y = 0; y < ic.size() && !out; ++y)
670 for (std::size_t j = 1; j <= nnodes; ++j)
675 if (out && !vis[F.
node - 1][r]) {
676 vis[F.
node - 1][r] =
true;
677 bq.emplace_back(F.
node, r);
680 for (std::size_t h = 0; h < bq.size(); ++h) {
681 const std::size_t cn = bq[h].first, cc = bq[h].second;
682 for (std::size_t y = 0; y < ic.size(); ++y) {
683 const std::size_t s = ic[y];
684 for (std::size_t nd = 1; nd <= nnodes; ++nd)
685 if (!vis[nd - 1][s] && detail::fj_route_cs(V, cc, s, cn, nd) > zero) {
686 vis[nd - 1][s] =
true;
687 if (nd != F.
joinNode) bq.emplace_back(nd, s);
697 std::vector<bool> infra(nnodes,
false);
698 infra[F.
node - 1] =
true;
700 infra[
tr.sourceNode - 1] =
true;
701 infra[
tr.sinkNode - 1] =
true;
703 for (std::size_t nd = 1; nd <= nnodes; ++nd) {
704 if (infra[nd - 1])
continue;
705 for (std::size_t x = 0; x < ic.size(); ++x) {
706 if (vis[nd - 1][ic[x]])
continue;
707 for (std::size_t y = 0; y < ic.size(); ++y)
708 detail::fj_clear_row(V, aux[x], aux[y], nd);
712 for (std::size_t x = 0; x < ic.size(); ++x) {
714 for (std::size_t nd = 1; nd <= nnodes && !anyvis; ++nd)
715 if (vis[nd - 1][ic[x]]) anyvis =
true;
716 if (!anyvis) V.
set_route(aux[x], aux[x],
tr.sourceNode,
tr.sinkNode, one);
721 tr.fjclassmap.resize(V.
classes.size() + 1, 0);
722 tr.fjforkmap.resize(V.
classes.size() + 1, 0);
723 tr.fanout.resize(V.
classes.size() + 1, 0.0);
724 tr.auxdisabled.resize(V.
classes.size() + 1,
false);
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
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)
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::size_t add_node(const std::string &nm, NodeType ty, bool stateful)
Add a non-station node (a Fork, a Router).
std::vector< JobClass > classes
std::size_t sinkNode
1-based NODE index of the Sink, 0 = none (it is not a station)
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::map< std::size_t, Matrix< T > > csmatrix
The class-switch matrix of a ClassSwitch node, by 1-based NODE index.
void refresh_rt()
sn.rt and sn.rtnodes: the class-expanded routing matrices.
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 add_station(const Station< T > &st)
Add a station, which is also a node, and grow the service table.
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...
The exception types the port throws.
Mean of the k-th smallest of n independent EXPONENTIAL branch completion times, i....
T fj_ordstat_exp(const std::vector< T > &ri, std::size_t k)
Mean of the k-th smallest of n independent EXPONENTIAL branch completion times, i....
SchedStrategy
Scheduling disciplines, with the values of MATLAB SchedStrategy.
JobClassType
Job class kinds, with the values of MATLAB JobClassType.
NodeType
Node kinds, with the values of MATLAB NodeType.
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,...
void fj_widen_csmatrix(FjMmt< T > &tr)
Widen every explicit ClassSwitch matrix to the auxiliary-expanded class set.
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.
T fj_expected_max(const std::vector< T > &means)
The AND-join case, i.e.
FjMmt< T > fj_mmt(const qn::NetworkStruct< T > &L)
Build the transformed layer.
void fj_sort_forks(FjMmt< T > &tr)
Port of ModelAdapter.sortForks: fill in outer and parent on every fork record.
A queueing network and its refreshed NetworkStruct.
static Distrib exp_rate(const T &r)
static Distrib disabled_dist()
static Distrib immediate()
The Immediate singleton.
One record per Fork of the base layer, ascending in node index.
std::size_t parent
parent_forks(f) as an index into forks: the fork whose node visits measure how often this one fires.
std::size_t joinStation
0 when the fork has no join
std::size_t node
1-based node index, shared with the base layer
std::size_t joinNode
0 when the fork has no join
std::vector< bool > outer
(nclasses+1) outer_forks(f, r): this fork is the outermost one on class r's path.
double fanOut
sn.nodeparam{f}.fanOut, which MATLAB sets to the fork's tasksPerLink and NOT to its number of output ...
std::vector< std::size_t > origfanout
(nclasses+1) the fork's number of output links for each class.
The transformed layer and the bookkeeping the fixed point needs to drive it and to merge its results ...
std::map< std::size_t, std::size_t > auxDelayNode
H-T only: the join node -> the auxiliary Delay NODE that carries the time the ORIGINAL class spends o...
std::vector< ForkRec > forks
std::vector< std::size_t > fjclassmap
(nclasses+1) auxiliary -> original, 0 if not auxiliary
std::vector< std::size_t > auxclasses
auxiliary class indices, ascending
std::size_t norig
class count of the base layer
qn::NetworkStruct< T > V
the transformed ("nonfj") layer
std::vector< std::size_t > auxbranch
H-T only: the 1-based branch a given auxiliary class walks; 0 elsewhere.
bool heidelberger_trivedi
The transform is Heidelberger-Trivedi (options.config.fork_join='ht') rather than MMT.
bool is_join_station(std::size_t st) const
std::size_t sourceStation
std::vector< bool > auxdisabled
(nclasses+1)
std::vector< std::size_t > joinStations
Every station that was a Join and is now a zero-service Delay, whether or not a fork claims it.
std::vector< double > fanout
(nclasses+1)
std::map< std::size_t, std::size_t > auxDelayStation
H-T only: the same delay as a STATION index.
std::vector< std::size_t > fjforkmap
(nclasses+1) auxiliary -> index into forks
static constexpr double Immediate
Rate of an Immediate distribution; its mean is 1/Immediate = 1e-8.
static constexpr double FineTol
static constexpr double Zero
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.
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 is_ref_class
marks the chain's reference class
One station of the network.
double nservers
may be infinite (a Delay, or an inf-scheduled task)