305 const std::size_t K =
sn.nclasses;
306 const std::size_t I =
sn.nodes.size();
307 const std::vector<std::vector<double>> Vn = fj_detail::node_visit_sum(
sn);
309 const double inf = std::numeric_limits<double>::infinity();
321 for (std::size_t i = 0; i < I; ++i)
322 if (V.
nodes[i].nodetype == NodeType::Fork) V.
nodes[i].stateful =
true;
324 for (std::size_t i = 0; i < I; ++i)
330 std::size_t f = 0, j = 0, r = 0, w = 1;
332 std::vector<std::size_t> wlink;
333 std::vector<std::size_t> branchheads;
334 std::vector<std::vector<std::size_t>> branchsets;
335 std::vector<std::vector<std::size_t>> auxmatrix;
337 std::vector<ForkInfo> info;
339 for (std::size_t f = 1; f <= I; ++f) {
340 if (
sn.nodes[f - 1].nodetype != NodeType::Fork)
continue;
342 for (std::size_t p = 0; p <
sn.fj.size(); ++p)
343 if (
sn.fj[p].first == f) j =
sn.fj[p].second;
344 const std::size_t w =
static_cast<std::size_t
>(
sn.nodes[f - 1].tasks_per_link);
346 for (std::size_t r = 1; r <= K; ++r) {
347 if (!(Vn[f - 1][r - 1] > 0))
continue;
352 fi.w = w == 0 ? 1 : w;
355 for (std::size_t nd = 1; nd <= I; ++nd)
357 (nd - 1) * K + (r - 1))) > 0)
358 fi.branchheads.push_back(nd);
359 const std::size_t B = fi.branchheads.size();
362 "fj_tag: the Fork node '" +
sn.nodes[f - 1].name +
363 "' has a single output link for class '" +
sn.classes[r - 1].name +
364 "'. A degenerate fork is not a fork -- remove it, or give it a second branch");
373 std::vector<std::size_t> wv(B, 0);
375 for (std::size_t b = 0; b < B; ++b) {
378 wv[b] =
static_cast<std::size_t
>(lv + 0.5);
379 if (wv[b] == 0) wv[b] = 1;
380 if (wv[b] != wv[0]) uniform =
false;
392 fi.branchsets.assign(B, std::vector<std::size_t>());
393 for (std::size_t b = 0; b < B; ++b) {
394 std::vector<std::size_t> visitset(1, fi.branchheads[b]);
395 std::vector<std::size_t> frontier(1, fi.branchheads[b]);
396 while (!frontier.empty()) {
397 const std::size_t cn = frontier.front();
398 frontier.erase(frontier.begin());
399 if (
sn.nodes[cn - 1].nodetype == NodeType::Fork)
401 "fj_tag: nested fork-join (the Fork node '" +
sn.nodes[cn - 1].name +
402 "' sits on a branch of '" +
sn.nodes[f - 1].name +
403 "') is not supported: a sibling would need a tag from each enclosing "
404 "fork and this transform mints one tag dimension");
405 if (
sn.nodes[cn - 1].nodetype == NodeType::Join && cn != j)
407 "fj_tag: overlapping fork-join pairs (the Join node '" +
408 sn.nodes[cn - 1].name +
"' sits on a branch of '" +
409 sn.nodes[f - 1].name +
"', which closes at '" +
sn.nodes[j - 1].name +
410 "') are not supported");
411 for (std::size_t nd = 1; nd <= I; ++nd)
412 for (std::size_t s = 1; s <= K; ++s) {
414 sn.rtnodes((cn - 1) * K + (r - 1), (nd - 1) * K + (s - 1))) >
419 "fj_tag: class switching between the Fork node '" +
420 sn.nodes[f - 1].name +
"' and its Join is not supported: the "
421 "sibling classes are minted per ORIGINAL class, so a sibling "
422 "that switches has no auxiliary twin to switch into");
423 if (nd == j)
continue;
424 if (std::find(visitset.begin(), visitset.end(), nd) == visitset.end()) {
425 visitset.push_back(nd);
426 frontier.push_back(nd);
434 std::vector<std::size_t> canreach(1, j);
438 for (std::size_t a = 0; a < visitset.size(); ++a) {
439 const std::size_t cn = visitset[a];
440 if (std::find(canreach.begin(), canreach.end(), cn) != canreach.end())
442 for (std::size_t b2 = 0; b2 < canreach.size(); ++b2)
444 sn.rtnodes((cn - 1) * K + (r - 1),
445 (canreach[b2] - 1) * K + (r - 1))) > 0) {
446 canreach.push_back(cn);
452 for (std::size_t a = 0; a < visitset.size(); ++a)
453 if (std::find(canreach.begin(), canreach.end(), visitset[a]) == canreach.end())
455 "fj_tag: the Join node '" +
sn.nodes[j - 1].name +
456 "' is unreachable from the branch node '" +
457 sn.nodes[visitset[a] - 1].name +
458 "'; a sibling trapped there never joins and its parent never completes");
459 fi.branchsets[b] = visitset;
464 for (std::size_t cc = 0; cc <
sn.chains.size(); ++cc)
465 if (
sn.chains[cc][r - 1]) { c = cc + 1;
break; }
468 tot =
sn.njobs()[r - 1];
470 for (std::size_t s = 0; s < K; ++s)
471 if (
sn.chains[c - 1][s]) tot +=
sn.njobs()[s];
473 const std::size_t Tt =
static_cast<std::size_t
>(tot + 0.5);
475 throw InputError(
"fj_tag: the chain of class '" +
sn.classes[r - 1].name +
476 "' routed through '" +
sn.nodes[f - 1].name +
477 "' carries no jobs, so no fork firing can ever occur");
479 fi.auxmatrix.assign(B, std::vector<std::size_t>(Tt, 0));
480 for (std::size_t t = 1; t <= Tt; ++t)
481 for (std::size_t b = 0; b < B; ++b) {
483 ac.
name =
sn.classes[r - 1].name +
"_f" + std::to_string(f) +
"_b" +
484 std::to_string(b + 1) +
"_t" + std::to_string(t);
485 ac.
type = JobClassType::CLOSED;
490 fi.auxmatrix[b][t - 1] = a;
494 for (std::size_t x = 0; x < fi.branchsets[b].size(); ++x) {
495 const std::size_t cn = fi.branchsets[b][x];
496 const std::size_t ist =
sn.nodes[cn - 1].station;
497 if (ist == 0 ||
sn.nodes[cn - 1].nodetype == NodeType::Join)
continue;
504 for (std::size_t x = 0; x < fi.branchsets[b].size(); ++x) {
505 const std::size_t cn = fi.branchsets[b][x];
506 for (std::size_t nd = 1; nd <= I; ++nd) {
507 const T p =
sn.get_route(r, r, cn, nd);
516 const std::size_t Kaug = V.
classes.size();
522 for (std::size_t row = 0; row < info.size(); ++row) {
523 const ForkInfo& fi = info[row];
524 for (std::size_t b = 0; b < fi.auxmatrix.size(); ++b)
525 for (std::size_t t = 0; t < fi.auxmatrix[b].size(); ++t) {
526 const std::size_t a = fi.auxmatrix[b][t];
539 for (std::size_t i = 0; i < V.
stations.size(); ++i) {
550 for (std::size_t row = 0; row < info.size(); ++row) {
551 const ForkInfo& fi = info[row];
552 for (std::size_t b = 0; b < fi.auxmatrix.size(); ++b)
553 for (std::size_t t = 0; t < fi.auxmatrix[b].size(); ++t) {
554 const std::size_t a = fi.auxmatrix[b][t];
555 for (std::size_t x = 0; x < fi.branchsets[b].size(); ++x) {
556 const std::size_t cn = fi.branchsets[b][x];
557 if (
sn.nodes[cn - 1].station == 0)
continue;
564 for (std::size_t i = 0; i < I; ++i) {
566 if (nd.
routing.empty())
continue;
567 if (nd.
routing.size() < Kaug) nd.
routing.resize(Kaug, RoutingStrategy::PROB);
568 for (std::size_t row = 0; row < info.size(); ++row) {
569 const ForkInfo& fi = info[row];
570 for (std::size_t b = 0; b < fi.auxmatrix.size(); ++b)
571 for (std::size_t t = 0; t < fi.auxmatrix[b].size(); ++t)
572 nd.
routing[fi.auxmatrix[b][t] - 1] =
573 sn.nodes[i].routing.size() >= fi.r ?
sn.nodes[i].routing[fi.r - 1]
574 : RoutingStrategy::PROB;
581 for (
typename std::map<std::size_t,
Matrix<T>>::iterator it = V.
csmatrix.begin();
585 for (std::size_t x = 0; x < old.
rows() && x < Kaug; ++x)
586 for (std::size_t y = 0; y < old.
cols() && y < Kaug; ++y) C(x, y) = old(x, y);
606 for (std::size_t r = 1; r <= K; ++r) {
607 std::size_t corig = 0, cnew = 0;
608 for (std::size_t cc = 0; cc <
sn.chains.size(); ++cc)
609 if (
sn.chains[cc][r - 1]) { corig = cc + 1;
break; }
610 for (std::size_t cc = 0; cc < V.
chains.size(); ++cc)
611 if (V.
chains[cc][r - 1]) { cnew = cc + 1;
break; }
612 if (corig == 0 || cnew == 0)
continue;
613 for (std::size_t isf = 1; isf <= V.
stateful_nodes.size(); ++isf) {
615 const std::size_t isf_old =
sn.stateful_index(ind);
617 V.
visits[cnew - 1](isf - 1, r - 1) =
sn.visits[corig - 1](isf_old - 1, r - 1);
621 V.
visits[cnew - 1](isf - 1, r - 1) =
625 for (std::size_t i = 0; i < I; ++i)
626 V.
nodevisits[cnew - 1](i, r - 1) =
sn.nodevisits[corig - 1](i, r - 1);
628 for (std::size_t row = 0; row < info.size(); ++row) {
629 const ForkInfo& fi = info[row];
630 std::size_t cnew = 0;
631 for (std::size_t cc = 0; cc < V.
chains.size(); ++cc)
632 if (V.
chains[cc][fi.r - 1]) { cnew = cc + 1;
break; }
633 if (cnew == 0)
continue;
634 for (std::size_t b = 0; b < fi.auxmatrix.size(); ++b) {
635 std::vector<std::size_t> support = fi.branchsets[b];
636 support.push_back(fi.j);
641 const std::size_t wb = fi.wlink.empty() ? fi.w : fi.wlink[b];
642 for (std::size_t t = 0; t < fi.auxmatrix[b].size(); ++t) {
643 const std::size_t a = fi.auxmatrix[b][t];
644 for (std::size_t c2 = 0; c2 < V.
nchains; ++c2) {
645 for (std::size_t x = 0; x < V.
visits[c2].rows(); ++x)
646 V.
visits[c2](x, a - 1) = zero;
647 for (std::size_t x = 0; x < V.
nodevisits[c2].rows(); ++x)
650 for (std::size_t i = 0; i < V.
stations.size(); ++i)
652 for (std::size_t x = 0; x < support.size(); ++x) {
653 const std::size_t cn = support[x];
660 if (V.
nodes[cn - 1].station != 0)
662 static_cast<double>(wb);
676 std::vector<std::size_t> reqb(fi.auxmatrix.size(), fi.w);
677 if (!fi.wlink.empty())
678 for (std::size_t b = 0; b < reqb.size() && b < fi.wlink.size(); ++b)
679 reqb[b] = fi.wlink[b];
680 typename std::map<std::size_t, typename NetworkStruct<T>::JoinDecl>::const_iterator jdq =
681 sn.joindecl.find(fi.j);
683 jdq->second.quorum > 0.0) {
684 const std::size_t q =
static_cast<std::size_t
>(jdq->second.quorum + 0.5);
685 for (std::size_t b = 0; b < reqb.size(); ++b)
686 if (q < reqb[b]) reqb[b] = q;
692 for (std::size_t row = 0; row < info.size(); ++row) {
693 const ForkInfo& fi = info[row];
694 const std::size_t Tt = fi.auxmatrix.empty() ? 0 : fi.auxmatrix[0].size();
695 for (std::size_t t = 1; t <= Tt; ++t) {
702 for (std::size_t b = 0; b < fi.auxmatrix.size(); ++b)
703 e.
auxclasses.push_back(fi.auxmatrix[b][t - 1]);