225 static constexpr std::size_t
npos =
static_cast<std::size_t
>(-1);
229 const std::string&
name()
const {
return name_; }
233 if (activity_map_.find(
name) != activity_map_.end())
234 throw InputError(
"Workflow: activity '" +
name +
"' is already declared");
236 const std::size_t idx = activities_.size() - 1;
237 activity_map_[
name] = idx;
248 precedences_.push_back(prec);
253 const std::vector<WorkflowActivity<T>>&
activities()
const {
return activities_; }
254 const std::vector<Precedence<T>>&
precedences()
const {
return precedences_; }
257 auto it = activity_map_.find(
name);
258 return it == activity_map_.end() ?
npos : it->second;
264 return activities_[i];
269 if (i >= activities_.size())
throw InputError(
"Workflow: activity index out of range");
270 return activities_[i];
279 if (activities_.empty())
280 throw InputError(
"Workflow must have at least one activity.");
283 for (
const std::string& nm : prec.pre_acts)
286 "' referenced in precedence not found in workflow.");
287 for (
const std::string& nm : prec.post_acts)
290 "' referenced in precedence not found in workflow.");
294 if (prec.post_type == PrecedenceType::POST_OR) {
295 if (prec.post_params.empty())
296 throw InputError(
"OR-fork must have probabilities specified.");
298 for (
const T& p : prec.post_params) total += p;
302 throw InputError(
"OR-fork probabilities must sum to 1.");
304 if (prec.post_type == PrecedenceType::POST_LOOP) {
305 if (prec.post_params.size() != 1)
306 throw InputError(
"Loop count must be a single positive number.");
308 throw InputError(
"Loop count must be a positive number.");
310 if (prec.pre_type == PrecedenceType::PRE_AND && !prec.pre_params.empty()) {
312 const double nb =
static_cast<double>(prec.pre_acts.size());
313 if (quorum > 0.0 && quorum < nb)
315 "AND-join with quorum " + std::to_string(
static_cast<long>(quorum)) +
316 " of " + std::to_string(
static_cast<long>(nb)) +
317 " is not supported by Workflow: a partial join is not the maximum of the "
318 "branches. Use a full join, or SolverLN with method='default', which "
319 "routes the join explicitly.");
332 if (cached_valid_)
return cached_ph_;
336 if (!compose_series_parallel(law)) {
342 cached_valid_ =
true;
364 activities_[i].set_host_demand(host_demand);
376 throw InputError(
"The activity mean must be a positive finite scalar.");
380 const Distrib<T>& d = activities_[i].host_demand();
381 const T old_mean = d.
mean;
382 if (d.
type == ProcessType::IMMEDIATE ||
389 const T factor = T(old_mean / mean_value);
397 cached_valid_ =
false;
398 sp_tree_.nodes.clear();
399 sp_tree_.root =
npos;
400 sp_tree_.leaf_of.clear();
401 sp_tree_.execs.clear();
408 cached_valid_ =
false;
409 if (!sp_built_)
return;
410 if (act_idx >= sp_tree_.leaf_of.size() || sp_tree_.leaf_of[act_idx] ==
npos) {
415 invalidate_branch(sp_tree_, sp_tree_.leaf_of[act_idx]);
425 cached_valid_ =
false;
426 if (!sp_built_)
return;
427 if (act_idx >= sp_tree_.leaf_of.size() || sp_tree_.leaf_of[act_idx] ==
npos) {
431 const std::size_t k = sp_tree_.leaf_of[act_idx];
432 invalidate_branch(sp_tree_, k);
434 if (node.
law.S.rows() > 0) {
435 for (std::size_t i = 0; i < node.
law.S.rows(); ++i)
436 for (std::size_t j = 0; j < node.
law.S.cols(); ++j)
437 node.
law.S(i, j) = T(node.
law.S(i, j) * factor);
447 if (!sp_built_ && !sp_failed_) build_sp_tree();
448 return sp_built_ ? &sp_tree_ :
nullptr;
465 const std::size_t n1 = a.
S.rows(), n2 = b.
S.rows();
469 for (std::size_t i = 0; i < n1; ++i)
470 for (std::size_t j = 0; j < n1; ++j) out.
S(i, j) = a.
S(i, j);
471 for (std::size_t i = 0; i < n2; ++i)
472 for (std::size_t j = 0; j < n2; ++j) out.
S(n1 + i, n1 + j) = b.
S(i, j);
474 for (std::size_t i = 0; i < n1; ++i) {
476 for (std::size_t j = 0; j < n1; ++j) rate += a.
S(i, j);
478 for (std::size_t j = 0; j < n2; ++j) out.
S(i, n1 + j) = T(rate * b.
alpha[j]);
482 for (
const T& v : a.
alpha) defect -= v;
483 out.
alpha.assign(n1 + n2, zero);
484 for (std::size_t i = 0; i < n1; ++i) out.
alpha[i] = a.
alpha[i];
485 for (std::size_t j = 0; j < n2; ++j) out.
alpha[n1 + j] = T(defect * b.
alpha[j]);
498 const std::size_t n1 = a.
S.rows(), n2 = b.
S.rows();
499 const std::size_t nboth = n1 * n2;
500 const std::size_t ntot = nboth + n1 + n2;
502 std::vector<T> abs1(n1, zero), abs2(n2, zero);
503 for (std::size_t i = 0; i < n1; ++i) {
505 for (std::size_t j = 0; j < n1; ++j) r += a.
S(i, j);
508 for (std::size_t i = 0; i < n2; ++i) {
510 for (std::size_t j = 0; j < n2; ++j) r += b.
S(i, j);
518 for (std::size_t i = 0; i < n1; ++i)
519 for (std::size_t j = 0; j < n2; ++j) {
520 const std::size_t r = i * n2 + j;
521 for (std::size_t ii = 0; ii < n1; ++ii)
522 out.
S(r, ii * n2 + j) += a.
S(i, ii);
523 for (std::size_t jj = 0; jj < n2; ++jj)
524 out.
S(r, i * n2 + jj) += b.
S(j, jj);
526 out.
S(r, nboth + i) += abs2[j];
527 out.
S(r, nboth + n1 + j) += abs1[i];
530 for (std::size_t i = 0; i < n1; ++i)
531 for (std::size_t j = 0; j < n1; ++j) out.
S(nboth + i, nboth + j) = a.
S(i, j);
532 for (std::size_t i = 0; i < n2; ++i)
533 for (std::size_t j = 0; j < n2; ++j)
534 out.
S(nboth + n1 + i, nboth + n1 + j) = b.
S(i, j);
536 out.
alpha.assign(ntot, zero);
537 for (std::size_t i = 0; i < n1; ++i)
538 for (std::size_t j = 0; j < n2; ++j)
549 const std::vector<T>& probs) {
551 std::size_t total = 0;
552 for (
const PhLaw<T>& l : laws) total += l.S.rows();
556 out.
alpha.assign(total, zero);
559 for (std::size_t b = 0; b < laws.size(); ++b) {
560 const std::size_t nb = laws[b].
S.rows();
561 for (std::size_t i = 0; i < nb; ++i) {
562 for (std::size_t j = 0; j < nb; ++j) out.
S(off + i, off + j) = laws[b].
S(i, j);
563 out.
alpha[off + i] = T(probs[b] * laws[b].alpha[i]);
588 const std::size_t n = body.
S.rows();
593 out.
alpha.assign(1, one);
603 out.
alpha.assign(n + 1, zero);
604 for (std::size_t i = 0; i < n; ++i) out.
alpha[i] = T(count * body.
alpha[i]);
605 out.
alpha[n] = T(one - count);
607 for (std::size_t i = 0; i < n; ++i)
608 for (std::size_t j = 0; j < n; ++j) out.
S(i, j) = body.
S(i, j);
613 const T p = T(one - one / count);
615 for (
const T& v : body.
alpha) defect -= v;
616 const T denom = T(one - p * defect);
618 out.
alpha.assign(n, zero);
619 for (std::size_t i = 0; i < n; ++i) out.
alpha[i] = T(body.
alpha[i] / denom);
622 for (std::size_t i = 0; i < n; ++i) {
624 for (std::size_t j = 0; j < n; ++j) rate += body.
S(i, j);
625 rate = T(-rate * p / denom);
626 for (std::size_t j = 0; j < n; ++j) out.
S(i, j) += T(rate * body.
alpha[j]);
658 const std::size_t n = S.
rows();
659 std::vector<std::vector<bool>> A(n, std::vector<bool>(n,
false));
660 std::vector<std::size_t> in_deg(n, 0);
661 for (std::size_t i = 0; i < n; ++i)
662 for (std::size_t j = 0; j < n; ++j) {
663 if (i == j)
continue;
670 std::vector<std::size_t> queue;
671 for (std::size_t i = 0; i < n; ++i)
672 if (in_deg[i] == 0) queue.push_back(i);
673 std::size_t visited = 0, head = 0;
674 while (head < queue.size()) {
675 const std::size_t cur = queue[head++];
677 for (std::size_t j = 0; j < n; ++j) {
678 if (!A[cur][j])
continue;
679 if (--in_deg[j] == 0) queue.push_back(j);
693 p.
pre_type = PrecedenceType::PRE_SEQ;
698 static std::vector<Precedence<T>>
SerialSequence(
const std::vector<std::string>& acts) {
699 std::vector<Precedence<T>> out;
700 for (std::size_t i = 0; i + 1 < acts.size(); ++i)
701 out.push_back(
Serial(acts[i], acts[i + 1]));
706 const std::vector<std::string>& posts) {
710 p.
pre_type = PrecedenceType::PRE_SEQ;
717 const std::string& post,
718 const std::vector<T>& quorum = std::vector<T>()) {
722 p.
pre_type = PrecedenceType::PRE_AND;
729 const std::vector<T>& probs) {
733 p.
pre_type = PrecedenceType::PRE_SEQ;
740 const std::string& post) {
744 p.
pre_type = PrecedenceType::PRE_OR;
758 p.
pre_type = PrecedenceType::PRE_SEQ;
770 enum class ParseStatus { END, STOP, JOIN, FAIL };
774 std::vector<std::size_t> out_p, in_p;
775 std::vector<bool> consumed;
778 bool compose_series_parallel(PhLaw<T>& out) {
780 if (sp_failed_)
return false;
782 if (!sp_built_)
return false;
784 out = compose_node(sp_tree_.root);
794 bool build_sp_tree() {
795 sp_tree_.nodes.clear();
796 sp_tree_.root =
npos;
797 sp_tree_.leaf_of.clear();
798 sp_tree_.execs.clear();
802 const std::size_t n = activities_.size();
803 if (n == 0)
return false;
806 S.out_p.assign(n,
npos);
807 S.in_p.assign(n,
npos);
808 S.consumed.assign(n,
false);
812 for (std::size_t p = 0; p < precedences_.size(); ++p) {
813 for (
const std::string& nm : precedences_[p].pre_acts) {
815 if (i ==
npos || S.out_p[i] !=
npos)
return false;
818 for (
const std::string& nm : precedences_[p].post_acts) {
820 if (j ==
npos || S.in_p[j] !=
npos)
return false;
825 std::vector<std::size_t> starts;
826 for (std::size_t i = 0; i < n; ++i)
827 if (S.in_p[i] ==
npos) starts.push_back(i);
828 if (starts.size() != 1)
return false;
830 std::vector<std::size_t> kids;
831 std::size_t stop_at =
npos;
832 const ParseStatus st = sp_parse_seq(S, starts[0], std::vector<std::size_t>(), kids, stop_at);
833 if (st != ParseStatus::END)
return false;
834 for (std::size_t i = 0; i < n; ++i)
835 if (!S.consumed[i])
return false;
837 const std::size_t root = sp_serial_node(kids);
838 if (root ==
npos)
return false;
840 sp_tree_.root = root;
841 sp_tree_.leaf_of.assign(n,
npos);
842 for (std::size_t k = 0; k < sp_tree_.nodes.size(); ++k)
844 sp_tree_.leaf_of[sp_tree_.nodes[k].act] = k;
845 sp_tree_.execs = sp_execution_counts(sp_tree_);
853 ParseStatus sp_parse_seq(ParseState& S, std::size_t cur,
854 const std::vector<std::size_t>& stop_set,
855 std::vector<std::size_t>& kids, std::size_t& stop_at) {
858 if (cur ==
npos)
return ParseStatus::END;
859 if (std::find(stop_set.begin(), stop_set.end(), cur) != stop_set.end()) {
861 return ParseStatus::STOP;
863 if (S.consumed[cur])
return ParseStatus::FAIL;
864 S.consumed[cur] =
true;
865 kids.push_back(sp_add_node(
SPNodeType::LEAF, cur, std::vector<std::size_t>(),
866 std::vector<T>(), num_traits<T>::from_int(0)));
868 const std::size_t p = S.out_p[cur];
869 if (p ==
npos)
return ParseStatus::END;
870 const Precedence<T>& prec = precedences_[p];
871 if (prec.pre_acts.size() > 1) {
874 return ParseStatus::JOIN;
877 std::vector<std::size_t> post_inds = sp_indices_of(prec.post_acts);
878 for (std::size_t i : post_inds)
879 if (i ==
npos)
return ParseStatus::FAIL;
881 std::size_t knode =
npos, next_act =
npos;
882 switch (prec.post_type) {
883 case PrecedenceType::POST_AND:
884 if (!sp_parse_fork(S, post_inds, std::vector<T>(), stop_set,
true, knode,
886 return ParseStatus::FAIL;
887 kids.push_back(knode);
890 case PrecedenceType::POST_OR:
891 if (prec.post_params.size() != post_inds.size())
return ParseStatus::FAIL;
892 if (!sp_parse_fork(S, post_inds, prec.post_params, stop_set,
false, knode,
894 return ParseStatus::FAIL;
895 kids.push_back(knode);
898 case PrecedenceType::POST_LOOP:
899 if (!sp_parse_loop(S, post_inds, prec.post_params, stop_set, knode, next_act))
900 return ParseStatus::FAIL;
901 kids.push_back(knode);
904 case PrecedenceType::POST_SEQ:
905 if (post_inds.size() != 1)
return ParseStatus::FAIL;
911 return ParseStatus::FAIL;
917 bool sp_parse_fork(ParseState& S,
const std::vector<std::size_t>& branch_heads,
918 const std::vector<T>& probs,
const std::vector<std::size_t>& stop_set,
919 bool is_and, std::size_t& knode, std::size_t& next_act) {
920 const std::size_t nb = branch_heads.size();
921 std::vector<std::size_t> branch_nodes(nb,
npos), bstop(nb,
npos);
922 std::vector<ParseStatus> bstatus(nb, ParseStatus::FAIL);
924 for (std::size_t b = 0; b < nb; ++b) {
925 std::vector<std::size_t> bkids;
926 std::size_t sa =
npos;
927 const ParseStatus st = sp_parse_seq(S, branch_heads[b], stop_set, bkids, sa);
928 if (st == ParseStatus::FAIL)
return false;
929 const std::size_t bn = sp_serial_node(bkids);
930 if (bn ==
npos)
return false;
931 branch_nodes[b] = bn;
936 const bool all_join =
937 std::all_of(bstatus.begin(), bstatus.end(),
938 [](ParseStatus s) { return s == ParseStatus::JOIN; });
939 const bool all_end = std::all_of(bstatus.begin(), bstatus.end(),
940 [](ParseStatus s) { return s == ParseStatus::END; });
941 const bool all_stop = std::all_of(bstatus.begin(), bstatus.end(),
942 [](ParseStatus s) { return s == ParseStatus::STOP; });
943 const bool same_stop =
944 std::all_of(bstop.begin(), bstop.end(),
945 [&bstop](std::size_t x) { return x == bstop[0]; });
948 if (!same_stop)
return false;
949 const Precedence<T>& join_prec = precedences_[bstop[0]];
950 if (join_prec.pre_acts.size() != nb)
return false;
952 if (join_prec.pre_type != PrecedenceType::PRE_AND)
return false;
954 if (join_prec.pre_type != PrecedenceType::PRE_OR)
return false;
956 const std::vector<std::size_t> post_inds = sp_indices_of(join_prec.post_acts);
957 if (post_inds.size() != 1 || post_inds[0] ==
npos)
return false;
958 next_act = post_inds[0];
959 }
else if (all_end) {
963 }
else if (!is_and && all_stop && same_stop) {
970 num_traits<T>::from_int(0));
978 bool sp_parse_loop(ParseState& S,
const std::vector<std::size_t>& post_inds,
979 const std::vector<T>& counts,
const std::vector<std::size_t>& stop_set,
980 std::size_t& knode, std::size_t& next_act) {
981 if (counts.size() != 1)
return false;
982 const T count = counts[0];
984 std::vector<std::size_t> body_acts;
985 std::size_t end_act =
npos;
986 if (post_inds.size() >= 2) {
987 body_acts.assign(post_inds.begin(), post_inds.end() - 1);
988 end_act = post_inds.back();
990 body_acts.push_back(post_inds[0]);
993 std::vector<std::size_t> loop_stop = stop_set;
994 loop_stop.insert(loop_stop.end(), body_acts.begin(), body_acts.end());
995 if (end_act !=
npos) loop_stop.push_back(end_act);
997 std::vector<std::size_t> body_kids;
999 while (j < body_acts.size()) {
1000 const std::size_t a = body_acts[j];
1001 if (S.consumed[a]) {
1005 std::vector<std::size_t> this_stop;
1006 for (std::size_t x : loop_stop)
1007 if (x != a) this_stop.push_back(x);
1009 std::vector<std::size_t> kk;
1010 std::size_t sa =
npos;
1011 const ParseStatus st = sp_parse_seq(S, a, this_stop, kk, sa);
1012 if (st == ParseStatus::FAIL)
return false;
1013 body_kids.insert(body_kids.end(), kk.begin(), kk.end());
1015 if (st == ParseStatus::END) {
1017 }
else if (st == ParseStatus::STOP) {
1018 const auto it = std::find(body_acts.begin(), body_acts.end(), sa);
1019 if (it != body_acts.end()) {
1020 j =
static_cast<std::size_t
>(it - body_acts.begin());
1021 }
else if (end_act !=
npos && sa == end_act) {
1022 j = body_acts.size();
1033 const std::size_t body_node = sp_serial_node(body_kids);
1034 if (body_node ==
npos)
return false;
1037 std::vector<T>(), count);
1043 std::size_t sp_serial_node(
const std::vector<std::size_t>& kids) {
1044 if (kids.empty())
return npos;
1045 if (kids.size() == 1)
return kids[0];
1047 num_traits<T>::from_int(0));
1050 std::size_t sp_add_node(
SPNodeType type, std::size_t act,
1051 const std::vector<std::size_t>& kids,
const std::vector<T>& probs,
1059 sp_tree_.nodes.push_back(node);
1060 const std::size_t k = sp_tree_.nodes.size() - 1;
1061 for (std::size_t c : kids) sp_tree_.nodes[c].parent = k;
1065 std::vector<std::size_t> sp_indices_of(
const std::vector<std::string>& names)
const {
1066 std::vector<std::size_t> out(names.size(),
npos);
1067 for (std::size_t i = 0; i < names.size(); ++i) out[i] =
activity_index(names[i]);
1075 PhLaw<T> compose_node(std::size_t k) {
1076 SPNode<T>& node = sp_tree_.nodes[k];
1077 if (node.valid)
return node.law;
1080 switch (node.type) {
1082 law = activities_[node.act].ph_representation();
1085 const std::vector<std::size_t> kids = node.kids;
1086 law = compose_node(kids[0]);
1087 for (std::size_t i = 1; i < kids.size(); ++i)
1092 const std::vector<std::size_t> kids = node.kids;
1093 law = compose_node(kids[0]);
1094 for (std::size_t i = 1; i < kids.size(); ++i)
1099 const std::vector<std::size_t> kids = node.kids;
1100 std::vector<PhLaw<T>> laws;
1101 for (std::size_t c : kids) laws.push_back(compose_node(c));
1106 const std::size_t child = node.kids[0];
1112 sp_tree_.nodes[k].law = law;
1113 sp_tree_.nodes[k].valid =
true;
1117 static void invalidate_branch(SPTree<T>& tree, std::size_t k) {
1119 tree.nodes[k].valid =
false;
1120 k = tree.nodes[k].parent;
1130 static std::vector<T> sp_execution_counts(
const SPTree<T>& tree) {
1131 std::vector<T> execs(tree.nodes.size(), num_traits<T>::from_int(0));
1132 if (tree.root ==
npos)
return execs;
1133 execs[tree.root] = num_traits<T>::from_int(1);
1134 std::vector<std::size_t> stack(1, tree.root);
1135 while (!stack.empty()) {
1136 const std::size_t k = stack.back();
1138 const SPNode<T>& node = tree.nodes[k];
1139 for (std::size_t i = 0; i < node.kids.size(); ++i) {
1140 const std::size_t c = node.kids[i];
1142 execs[c] = T(execs[k] * node.probs[i]);
1144 execs[c] = T(execs[k] * node.count);
1146 execs[c] = execs[k];
1160 std::size_t pre_act =
npos;
1161 std::vector<std::size_t> post_acts;
1162 std::vector<T> probs;
1167 std::vector<std::size_t> pre_acts;
1168 std::size_t post_act =
npos;
1172 std::size_t pre_act =
npos;
1173 std::vector<std::size_t> body_acts;
1174 std::size_t end_act =
npos;
1175 T count = num_traits<T>::from_int(1);
1179 std::vector<std::vector<std::size_t>> adj;
1180 std::vector<std::size_t> in_deg, out_deg;
1181 std::vector<ForkInfo> forks;
1182 std::vector<JoinInfo> joins;
1183 std::vector<LoopInfo> loops;
1186 Structure analyze_structure()
const {
1187 const std::size_t n = activities_.size();
1189 st.adj.assign(n, std::vector<std::size_t>());
1190 st.in_deg.assign(n, 0);
1191 st.out_deg.assign(n, 0);
1193 for (
const Precedence<T>& prec : precedences_) {
1194 const std::vector<std::size_t> pre = sp_indices_of(prec.pre_acts);
1195 const std::vector<std::size_t> post = sp_indices_of(prec.post_acts);
1196 for (std::size_t i : pre)
1197 for (std::size_t j : post) {
1198 st.adj[i].push_back(j);
1203 if (prec.post_type == PrecedenceType::POST_AND) {
1208 st.forks.push_back(f);
1209 }
else if (prec.post_type == PrecedenceType::POST_OR) {
1214 f.probs = prec.post_params;
1215 st.forks.push_back(f);
1216 }
else if (prec.post_type == PrecedenceType::POST_LOOP) {
1219 if (post.size() > 1) {
1220 l.body_acts.assign(post.begin(), post.end() - 1);
1221 l.end_act = post.back();
1225 l.count = prec.post_params.empty() ? num_traits<T>::from_int(1)
1226 : prec.post_params[0];
1227 st.loops.push_back(l);
1230 if (prec.pre_type == PrecedenceType::PRE_AND ||
1231 prec.pre_type == PrecedenceType::PRE_OR) {
1233 j.is_and = prec.pre_type == PrecedenceType::PRE_AND;
1235 j.post_act = post[0];
1236 st.joins.push_back(j);
1242 std::vector<std::size_t> topological_sort(
1243 const std::vector<std::vector<std::size_t>>& adj)
const {
1244 const std::size_t n = activities_.size();
1245 std::vector<std::size_t> in_deg(n, 0);
1246 for (
const std::vector<std::size_t>& nbrs : adj)
1247 for (std::size_t j : nbrs) ++in_deg[j];
1249 std::vector<std::size_t> queue, order;
1250 for (std::size_t i = 0; i < n; ++i)
1251 if (in_deg[i] == 0) queue.push_back(i);
1252 std::size_t head = 0;
1253 while (head < queue.size()) {
1254 const std::size_t cur = queue[head++];
1255 order.push_back(cur);
1256 for (std::size_t nx : adj[cur])
1257 if (--in_deg[nx] == 0) queue.push_back(nx);
1259 std::vector<bool> seen(n,
false);
1260 for (std::size_t i : order) seen[i] =
true;
1261 for (std::size_t i = 0; i < n; ++i)
1262 if (!seen[i]) order.push_back(i);
1274 PhLaw<T> build_ctmc() {
1275 const std::size_t n = activities_.size();
1276 if (n == 1)
return activities_[0].ph_representation();
1278 const Structure st = analyze_structure();
1280 std::vector<PhLaw<T>> block(n);
1281 std::vector<bool> absorbed(n,
false);
1282 for (std::size_t i = 0; i < n; ++i) block[i] = activities_[i].ph_representation();
1284 for (
const LoopInfo& loop : st.loops) {
1285 PhLaw<T> body = activities_[loop.body_acts[0]].ph_representation();
1286 for (std::size_t j = 1; j < loop.body_acts.size(); ++j)
1287 body =
compose_serial(body, activities_[loop.body_acts[j]].ph_representation());
1291 if (loop.end_act !=
npos) {
1292 res =
compose_serial(res, activities_[loop.end_act].ph_representation());
1293 absorbed[loop.end_act] =
true;
1295 block[loop.pre_act] = res;
1296 for (std::size_t idx : loop.body_acts) absorbed[idx] =
true;
1299 for (
const ForkInfo& fork : st.forks) {
1300 const JoinInfo* join = find_matching_join(fork.post_acts, st.joins, fork.is_and);
1301 if (fork.is_and && join ==
nullptr)
continue;
1305 inner = block[fork.post_acts[0]];
1306 for (std::size_t i = 1; i < fork.post_acts.size(); ++i)
1309 std::vector<PhLaw<T>> laws;
1310 for (std::size_t idx : fork.post_acts) laws.push_back(block[idx]);
1315 if (join !=
nullptr && !absorbed[join->post_act]) {
1317 absorbed[join->post_act] =
true;
1319 block[fork.pre_act] = res;
1320 for (std::size_t idx : fork.post_acts) absorbed[idx] =
true;
1323 const std::vector<std::size_t> order = topological_sort(st.adj);
1324 bool started =
false;
1326 for (std::size_t idx : order) {
1327 if (absorbed[idx])
continue;
1335 if (!started) out = activities_[0].ph_representation();
1339 static const JoinInfo* find_matching_join(
const std::vector<std::size_t>& post_acts,
1340 const std::vector<JoinInfo>& joins,
bool is_and) {
1341 std::vector<std::size_t> want = post_acts;
1342 std::sort(want.begin(), want.end());
1343 for (
const JoinInfo& j : joins) {
1344 if (j.is_and != is_and)
continue;
1345 std::vector<std::size_t> have = j.pre_acts;
1346 std::sort(have.begin(), have.end());
1347 if (have == want)
return &j;
1353 std::vector<WorkflowActivity<T>> activities_;
1354 std::map<std::string, std::size_t> activity_map_;
1355 std::vector<Precedence<T>> precedences_;
1357 Distrib<T> cached_ph_;
1358 bool cached_valid_ =
false;
1360 bool sp_built_ =
false;
1361 bool sp_failed_ =
false;