238 static constexpr std::size_t
npos =
static_cast<std::size_t
>(-1);
242 const std::string&
name()
const {
return name_; }
246 if (activity_map_.find(
name) != activity_map_.end())
247 throw InputError(
"Workflow: activity '" +
name +
"' is already declared");
249 const std::size_t idx = activities_.size() - 1;
250 activity_map_[
name] = idx;
261 precedences_.push_back(prec);
266 const std::vector<WorkflowActivity<T>>&
activities()
const {
return activities_; }
267 const std::vector<Precedence<T>>&
precedences()
const {
return precedences_; }
270 auto it = activity_map_.find(
name);
271 return it == activity_map_.end() ?
npos : it->second;
277 return activities_[i];
282 if (i >= activities_.size())
throw InputError(
"Workflow: activity index out of range");
283 return activities_[i];
292 if (activities_.empty())
293 throw InputError(
"Workflow must have at least one activity.");
296 for (
const std::string& nm : prec.pre_acts)
299 "' referenced in precedence not found in workflow.");
300 for (
const std::string& nm : prec.post_acts)
303 "' referenced in precedence not found in workflow.");
307 if (prec.post_type == PrecedenceType::POST_OR) {
308 if (prec.post_params.empty())
309 throw InputError(
"OR-fork must have probabilities specified.");
311 for (
const T& p : prec.post_params) total += p;
315 throw InputError(
"OR-fork probabilities must sum to 1.");
317 if (prec.post_type == PrecedenceType::POST_LOOP) {
318 if (prec.post_params.size() != 1)
319 throw InputError(
"Loop count must be a single positive number.");
321 throw InputError(
"Loop count must be a positive number.");
323 if (prec.pre_type == PrecedenceType::PRE_AND && !prec.pre_params.empty()) {
325 const double nb =
static_cast<double>(prec.pre_acts.size());
326 if (quorum > 0.0 && quorum < nb)
328 "AND-join with quorum " + std::to_string(
static_cast<long>(quorum)) +
329 " of " + std::to_string(
static_cast<long>(nb)) +
330 " is not supported by Workflow: a partial join is not the maximum of the "
331 "branches. Use a full join, or SolverLN with method='default', which "
332 "routes the join explicitly.");
345 if (cached_valid_)
return cached_ph_;
349 if (!compose_series_parallel(law)) {
355 cached_valid_ =
true;
377 activities_[i].set_host_demand(host_demand);
389 throw InputError(
"The activity mean must be a positive finite scalar.");
393 const Distrib<T>& d = activities_[i].host_demand();
394 const T old_mean = d.
mean;
395 if (d.
type == ProcessType::IMMEDIATE ||
402 const T factor = T(old_mean / mean_value);
410 cached_valid_ =
false;
411 sp_tree_.nodes.clear();
412 sp_tree_.root =
npos;
413 sp_tree_.leaf_of.clear();
414 sp_tree_.execs.clear();
421 cached_valid_ =
false;
422 if (!sp_built_)
return;
423 if (act_idx >= sp_tree_.leaf_of.size() || sp_tree_.leaf_of[act_idx] ==
npos) {
428 invalidate_branch(sp_tree_, sp_tree_.leaf_of[act_idx]);
438 cached_valid_ =
false;
439 if (!sp_built_)
return;
440 if (act_idx >= sp_tree_.leaf_of.size() || sp_tree_.leaf_of[act_idx] ==
npos) {
444 const std::size_t k = sp_tree_.leaf_of[act_idx];
445 invalidate_branch(sp_tree_, k);
447 if (node.
law.S.rows() > 0) {
448 for (std::size_t i = 0; i < node.
law.S.rows(); ++i)
449 for (std::size_t j = 0; j < node.
law.S.cols(); ++j)
450 node.
law.S(i, j) = T(node.
law.S(i, j) * factor);
460 if (!sp_built_ && !sp_failed_) build_sp_tree();
461 return sp_built_ ? &sp_tree_ :
nullptr;
478 const std::size_t n1 = a.
S.rows(), n2 = b.
S.rows();
482 for (std::size_t i = 0; i < n1; ++i)
483 for (std::size_t j = 0; j < n1; ++j) out.
S(i, j) = a.
S(i, j);
484 for (std::size_t i = 0; i < n2; ++i)
485 for (std::size_t j = 0; j < n2; ++j) out.
S(n1 + i, n1 + j) = b.
S(i, j);
487 for (std::size_t i = 0; i < n1; ++i) {
489 for (std::size_t j = 0; j < n1; ++j) rate += a.
S(i, j);
491 for (std::size_t j = 0; j < n2; ++j) out.
S(i, n1 + j) = T(rate * b.
alpha[j]);
495 for (
const T& v : a.
alpha) defect -= v;
496 out.
alpha.assign(n1 + n2, zero);
497 for (std::size_t i = 0; i < n1; ++i) out.
alpha[i] = a.
alpha[i];
498 for (std::size_t j = 0; j < n2; ++j) out.
alpha[n1 + j] = T(defect * b.
alpha[j]);
511 const std::size_t n1 = a.
S.rows(), n2 = b.
S.rows();
512 const std::size_t nboth = n1 * n2;
513 const std::size_t ntot = nboth + n1 + n2;
515 std::vector<T> abs1(n1, zero), abs2(n2, zero);
516 for (std::size_t i = 0; i < n1; ++i) {
518 for (std::size_t j = 0; j < n1; ++j) r += a.
S(i, j);
521 for (std::size_t i = 0; i < n2; ++i) {
523 for (std::size_t j = 0; j < n2; ++j) r += b.
S(i, j);
531 for (std::size_t i = 0; i < n1; ++i)
532 for (std::size_t j = 0; j < n2; ++j) {
533 const std::size_t r = i * n2 + j;
534 for (std::size_t ii = 0; ii < n1; ++ii)
535 out.
S(r, ii * n2 + j) += a.
S(i, ii);
536 for (std::size_t jj = 0; jj < n2; ++jj)
537 out.
S(r, i * n2 + jj) += b.
S(j, jj);
539 out.
S(r, nboth + i) += abs2[j];
540 out.
S(r, nboth + n1 + j) += abs1[i];
543 for (std::size_t i = 0; i < n1; ++i)
544 for (std::size_t j = 0; j < n1; ++j) out.
S(nboth + i, nboth + j) = a.
S(i, j);
545 for (std::size_t i = 0; i < n2; ++i)
546 for (std::size_t j = 0; j < n2; ++j)
547 out.
S(nboth + n1 + i, nboth + n1 + j) = b.
S(i, j);
549 out.
alpha.assign(ntot, zero);
550 for (std::size_t i = 0; i < n1; ++i)
551 for (std::size_t j = 0; j < n2; ++j)
562 const std::vector<T>& probs) {
564 std::size_t total = 0;
565 for (
const PhLaw<T>& l : laws) total += l.S.rows();
569 out.
alpha.assign(total, zero);
572 for (std::size_t b = 0; b < laws.size(); ++b) {
573 const std::size_t nb = laws[b].
S.rows();
574 for (std::size_t i = 0; i < nb; ++i) {
575 for (std::size_t j = 0; j < nb; ++j) out.
S(off + i, off + j) = laws[b].
S(i, j);
576 out.
alpha[off + i] = T(probs[b] * laws[b].alpha[i]);
601 const std::size_t n = body.
S.rows();
606 out.
alpha.assign(1, one);
616 out.
alpha.assign(n + 1, zero);
617 for (std::size_t i = 0; i < n; ++i) out.
alpha[i] = T(count * body.
alpha[i]);
618 out.
alpha[n] = T(one - count);
620 for (std::size_t i = 0; i < n; ++i)
621 for (std::size_t j = 0; j < n; ++j) out.
S(i, j) = body.
S(i, j);
626 const T p = T(one - one / count);
628 for (
const T& v : body.
alpha) defect -= v;
629 const T denom = T(one - p * defect);
631 out.
alpha.assign(n, zero);
632 for (std::size_t i = 0; i < n; ++i) out.
alpha[i] = T(body.
alpha[i] / denom);
635 for (std::size_t i = 0; i < n; ++i) {
637 for (std::size_t j = 0; j < n; ++j) rate += body.
S(i, j);
638 rate = T(-rate * p / denom);
639 for (std::size_t j = 0; j < n; ++j) out.
S(i, j) += T(rate * body.
alpha[j]);
671 const std::size_t n = S.
rows();
672 std::vector<std::vector<bool>> A(n, std::vector<bool>(n,
false));
673 std::vector<std::size_t> in_deg(n, 0);
674 for (std::size_t i = 0; i < n; ++i)
675 for (std::size_t j = 0; j < n; ++j) {
676 if (i == j)
continue;
683 std::vector<std::size_t> queue;
684 for (std::size_t i = 0; i < n; ++i)
685 if (in_deg[i] == 0) queue.push_back(i);
686 std::size_t visited = 0, head = 0;
687 while (head < queue.size()) {
688 const std::size_t cur = queue[head++];
690 for (std::size_t j = 0; j < n; ++j) {
691 if (!A[cur][j])
continue;
692 if (--in_deg[j] == 0) queue.push_back(j);
706 p.
pre_type = PrecedenceType::PRE_SEQ;
711 static std::vector<Precedence<T>>
SerialSequence(
const std::vector<std::string>& acts) {
712 std::vector<Precedence<T>> out;
713 for (std::size_t i = 0; i + 1 < acts.size(); ++i)
714 out.push_back(
Serial(acts[i], acts[i + 1]));
719 const std::vector<std::string>& posts) {
723 p.
pre_type = PrecedenceType::PRE_SEQ;
730 const std::string& post,
731 const std::vector<T>& quorum = std::vector<T>()) {
735 p.
pre_type = PrecedenceType::PRE_AND;
742 const std::vector<T>& probs) {
746 p.
pre_type = PrecedenceType::PRE_SEQ;
753 const std::string& post) {
757 p.
pre_type = PrecedenceType::PRE_OR;
771 p.
pre_type = PrecedenceType::PRE_SEQ;
783 enum class ParseStatus { END, STOP, JOIN, FAIL };
787 std::vector<std::size_t> out_p, in_p;
788 std::vector<bool> consumed;
791 bool compose_series_parallel(PhLaw<T>& out) {
793 if (sp_failed_)
return false;
795 if (!sp_built_)
return false;
797 out = compose_node(sp_tree_.root);
807 bool build_sp_tree() {
808 sp_tree_.nodes.clear();
809 sp_tree_.root =
npos;
810 sp_tree_.leaf_of.clear();
811 sp_tree_.execs.clear();
815 const std::size_t n = activities_.size();
816 if (n == 0)
return false;
819 S.out_p.assign(n,
npos);
820 S.in_p.assign(n,
npos);
821 S.consumed.assign(n,
false);
825 for (std::size_t p = 0; p < precedences_.size(); ++p) {
826 for (
const std::string& nm : precedences_[p].pre_acts) {
828 if (i ==
npos || S.out_p[i] !=
npos)
return false;
831 for (
const std::string& nm : precedences_[p].post_acts) {
833 if (j ==
npos || S.in_p[j] !=
npos)
return false;
838 std::vector<std::size_t> starts;
839 for (std::size_t i = 0; i < n; ++i)
840 if (S.in_p[i] ==
npos) starts.push_back(i);
841 if (starts.size() != 1)
return false;
843 std::vector<std::size_t> kids;
844 std::size_t stop_at =
npos;
845 const ParseStatus st = sp_parse_seq(S, starts[0], std::vector<std::size_t>(), kids, stop_at);
846 if (st != ParseStatus::END)
return false;
847 for (std::size_t i = 0; i < n; ++i)
848 if (!S.consumed[i])
return false;
850 const std::size_t root = sp_serial_node(kids);
851 if (root ==
npos)
return false;
853 sp_tree_.root = root;
854 sp_tree_.leaf_of.assign(n,
npos);
855 for (std::size_t k = 0; k < sp_tree_.nodes.size(); ++k)
857 sp_tree_.leaf_of[sp_tree_.nodes[k].act] = k;
858 sp_tree_.execs = sp_execution_counts(sp_tree_);
866 ParseStatus sp_parse_seq(ParseState& S, std::size_t cur,
867 const std::vector<std::size_t>& stop_set,
868 std::vector<std::size_t>& kids, std::size_t& stop_at) {
871 if (cur ==
npos)
return ParseStatus::END;
872 if (std::find(stop_set.begin(), stop_set.end(), cur) != stop_set.end()) {
874 return ParseStatus::STOP;
876 if (S.consumed[cur])
return ParseStatus::FAIL;
877 S.consumed[cur] =
true;
878 kids.push_back(sp_add_node(
SPNodeType::LEAF, cur, std::vector<std::size_t>(),
879 std::vector<T>(), num_traits<T>::from_int(0)));
881 const std::size_t p = S.out_p[cur];
882 if (p ==
npos)
return ParseStatus::END;
883 const Precedence<T>& prec = precedences_[p];
884 if (prec.pre_acts.size() > 1) {
887 return ParseStatus::JOIN;
890 std::vector<std::size_t> post_inds = sp_indices_of(prec.post_acts);
891 for (std::size_t i : post_inds)
892 if (i ==
npos)
return ParseStatus::FAIL;
894 std::size_t knode =
npos, next_act =
npos;
895 switch (prec.post_type) {
896 case PrecedenceType::POST_AND:
897 if (!sp_parse_fork(S, post_inds, std::vector<T>(), stop_set,
true, knode,
899 return ParseStatus::FAIL;
900 kids.push_back(knode);
903 case PrecedenceType::POST_OR:
904 if (prec.post_params.size() != post_inds.size())
return ParseStatus::FAIL;
905 if (!sp_parse_fork(S, post_inds, prec.post_params, stop_set,
false, knode,
907 return ParseStatus::FAIL;
908 kids.push_back(knode);
911 case PrecedenceType::POST_LOOP:
912 if (!sp_parse_loop(S, post_inds, prec.post_params, stop_set, knode, next_act))
913 return ParseStatus::FAIL;
914 kids.push_back(knode);
917 case PrecedenceType::POST_SEQ:
918 if (post_inds.size() != 1)
return ParseStatus::FAIL;
924 return ParseStatus::FAIL;
930 bool sp_parse_fork(ParseState& S,
const std::vector<std::size_t>& branch_heads,
931 const std::vector<T>& probs,
const std::vector<std::size_t>& stop_set,
932 bool is_and, std::size_t& knode, std::size_t& next_act) {
933 const std::size_t nb = branch_heads.size();
934 std::vector<std::size_t> branch_nodes(nb,
npos), bstop(nb,
npos);
935 std::vector<ParseStatus> bstatus(nb, ParseStatus::FAIL);
937 for (std::size_t b = 0; b < nb; ++b) {
938 std::vector<std::size_t> bkids;
939 std::size_t sa =
npos;
940 const ParseStatus st = sp_parse_seq(S, branch_heads[b], stop_set, bkids, sa);
941 if (st == ParseStatus::FAIL)
return false;
942 const std::size_t bn = sp_serial_node(bkids);
943 if (bn ==
npos)
return false;
944 branch_nodes[b] = bn;
949 const bool all_join =
950 std::all_of(bstatus.begin(), bstatus.end(),
951 [](ParseStatus s) { return s == ParseStatus::JOIN; });
952 const bool all_end = std::all_of(bstatus.begin(), bstatus.end(),
953 [](ParseStatus s) { return s == ParseStatus::END; });
954 const bool all_stop = std::all_of(bstatus.begin(), bstatus.end(),
955 [](ParseStatus s) { return s == ParseStatus::STOP; });
956 const bool same_stop =
957 std::all_of(bstop.begin(), bstop.end(),
958 [&bstop](std::size_t x) { return x == bstop[0]; });
961 if (!same_stop)
return false;
962 const Precedence<T>& join_prec = precedences_[bstop[0]];
963 if (join_prec.pre_acts.size() != nb)
return false;
965 if (join_prec.pre_type != PrecedenceType::PRE_AND)
return false;
967 if (join_prec.pre_type != PrecedenceType::PRE_OR)
return false;
969 const std::vector<std::size_t> post_inds = sp_indices_of(join_prec.post_acts);
970 if (post_inds.size() != 1 || post_inds[0] ==
npos)
return false;
971 next_act = post_inds[0];
972 }
else if (all_end) {
976 }
else if (!is_and && all_stop && same_stop) {
983 num_traits<T>::from_int(0));
991 bool sp_parse_loop(ParseState& S,
const std::vector<std::size_t>& post_inds,
992 const std::vector<T>& counts,
const std::vector<std::size_t>& stop_set,
993 std::size_t& knode, std::size_t& next_act) {
994 if (counts.size() != 1)
return false;
995 const T count = counts[0];
997 std::vector<std::size_t> body_acts;
998 std::size_t end_act =
npos;
999 if (post_inds.size() >= 2) {
1000 body_acts.assign(post_inds.begin(), post_inds.end() - 1);
1001 end_act = post_inds.back();
1003 body_acts.push_back(post_inds[0]);
1006 std::vector<std::size_t> loop_stop = stop_set;
1007 loop_stop.insert(loop_stop.end(), body_acts.begin(), body_acts.end());
1008 if (end_act !=
npos) loop_stop.push_back(end_act);
1010 std::vector<std::size_t> body_kids;
1012 while (j < body_acts.size()) {
1013 const std::size_t a = body_acts[j];
1014 if (S.consumed[a]) {
1018 std::vector<std::size_t> this_stop;
1019 for (std::size_t x : loop_stop)
1020 if (x != a) this_stop.push_back(x);
1022 std::vector<std::size_t> kk;
1023 std::size_t sa =
npos;
1024 const ParseStatus st = sp_parse_seq(S, a, this_stop, kk, sa);
1025 if (st == ParseStatus::FAIL)
return false;
1026 body_kids.insert(body_kids.end(), kk.begin(), kk.end());
1028 if (st == ParseStatus::END) {
1030 }
else if (st == ParseStatus::STOP) {
1031 const auto it = std::find(body_acts.begin(), body_acts.end(), sa);
1032 if (it != body_acts.end()) {
1033 j =
static_cast<std::size_t
>(it - body_acts.begin());
1034 }
else if (end_act !=
npos && sa == end_act) {
1035 j = body_acts.size();
1046 const std::size_t body_node = sp_serial_node(body_kids);
1047 if (body_node ==
npos)
return false;
1050 std::vector<T>(), count);
1056 std::size_t sp_serial_node(
const std::vector<std::size_t>& kids) {
1057 if (kids.empty())
return npos;
1058 if (kids.size() == 1)
return kids[0];
1060 num_traits<T>::from_int(0));
1063 std::size_t sp_add_node(
SPNodeType type, std::size_t act,
1064 const std::vector<std::size_t>& kids,
const std::vector<T>& probs,
1072 sp_tree_.nodes.push_back(node);
1073 const std::size_t k = sp_tree_.nodes.size() - 1;
1074 for (std::size_t c : kids) sp_tree_.nodes[c].parent = k;
1078 std::vector<std::size_t> sp_indices_of(
const std::vector<std::string>& names)
const {
1079 std::vector<std::size_t> out(names.size(),
npos);
1080 for (std::size_t i = 0; i < names.size(); ++i) out[i] =
activity_index(names[i]);
1088 PhLaw<T> compose_node(std::size_t k) {
1089 SPNode<T>& node = sp_tree_.nodes[k];
1090 if (node.valid)
return node.law;
1093 switch (node.type) {
1095 law = activities_[node.act].ph_representation();
1098 const std::vector<std::size_t> kids = node.kids;
1099 law = compose_node(kids[0]);
1100 for (std::size_t i = 1; i < kids.size(); ++i)
1105 const std::vector<std::size_t> kids = node.kids;
1106 law = compose_node(kids[0]);
1107 for (std::size_t i = 1; i < kids.size(); ++i)
1112 const std::vector<std::size_t> kids = node.kids;
1113 std::vector<PhLaw<T>> laws;
1114 for (std::size_t c : kids) laws.push_back(compose_node(c));
1119 const std::size_t
child = node.kids[0];
1125 sp_tree_.nodes[k].law = law;
1126 sp_tree_.nodes[k].valid =
true;
1130 static void invalidate_branch(SPTree<T>& tree, std::size_t k) {
1132 tree.nodes[k].valid =
false;
1133 k = tree.nodes[k].parent;
1143 static std::vector<T> sp_execution_counts(
const SPTree<T>& tree) {
1144 std::vector<T> execs(tree.nodes.size(), num_traits<T>::from_int(0));
1145 if (tree.root ==
npos)
return execs;
1146 execs[tree.root] = num_traits<T>::from_int(1);
1147 std::vector<std::size_t> stack(1, tree.root);
1148 while (!stack.empty()) {
1149 const std::size_t k = stack.back();
1151 const SPNode<T>& node = tree.nodes[k];
1152 for (std::size_t i = 0; i < node.kids.size(); ++i) {
1153 const std::size_t c = node.kids[i];
1155 execs[c] = T(execs[k] * node.probs[i]);
1157 execs[c] = T(execs[k] * node.count);
1159 execs[c] = execs[k];
1173 std::size_t pre_act =
npos;
1174 std::vector<std::size_t> post_acts;
1175 std::vector<T> probs;
1180 std::vector<std::size_t> pre_acts;
1181 std::size_t post_act =
npos;
1185 std::size_t pre_act =
npos;
1186 std::vector<std::size_t> body_acts;
1187 std::size_t end_act =
npos;
1188 T count = num_traits<T>::from_int(1);
1192 std::vector<std::vector<std::size_t>> adj;
1193 std::vector<std::size_t> in_deg, out_deg;
1194 std::vector<ForkInfo> forks;
1195 std::vector<JoinInfo> joins;
1196 std::vector<LoopInfo> loops;
1199 Structure analyze_structure()
const {
1200 const std::size_t n = activities_.size();
1202 st.adj.assign(n, std::vector<std::size_t>());
1203 st.in_deg.assign(n, 0);
1204 st.out_deg.assign(n, 0);
1206 for (
const Precedence<T>& prec : precedences_) {
1207 const std::vector<std::size_t> pre = sp_indices_of(prec.pre_acts);
1208 const std::vector<std::size_t> post = sp_indices_of(prec.post_acts);
1209 for (std::size_t i : pre)
1210 for (std::size_t j : post) {
1211 st.adj[i].push_back(j);
1216 if (prec.post_type == PrecedenceType::POST_AND) {
1221 st.forks.push_back(f);
1222 }
else if (prec.post_type == PrecedenceType::POST_OR) {
1227 f.probs = prec.post_params;
1228 st.forks.push_back(f);
1229 }
else if (prec.post_type == PrecedenceType::POST_LOOP) {
1232 if (post.size() > 1) {
1233 l.body_acts.assign(post.begin(), post.end() - 1);
1234 l.end_act = post.back();
1238 l.count = prec.post_params.empty() ? num_traits<T>::from_int(1)
1239 : prec.post_params[0];
1240 st.loops.push_back(l);
1243 if (prec.pre_type == PrecedenceType::PRE_AND ||
1244 prec.pre_type == PrecedenceType::PRE_OR) {
1246 j.is_and = prec.pre_type == PrecedenceType::PRE_AND;
1248 j.post_act = post[0];
1249 st.joins.push_back(j);
1255 std::vector<std::size_t> topological_sort(
1256 const std::vector<std::vector<std::size_t>>& adj)
const {
1257 const std::size_t n = activities_.size();
1258 std::vector<std::size_t> in_deg(n, 0);
1259 for (
const std::vector<std::size_t>& nbrs : adj)
1260 for (std::size_t j : nbrs) ++in_deg[j];
1262 std::vector<std::size_t> queue, order;
1263 for (std::size_t i = 0; i < n; ++i)
1264 if (in_deg[i] == 0) queue.push_back(i);
1265 std::size_t head = 0;
1266 while (head < queue.size()) {
1267 const std::size_t cur = queue[head++];
1268 order.push_back(cur);
1269 for (std::size_t nx : adj[cur])
1270 if (--in_deg[nx] == 0) queue.push_back(nx);
1272 std::vector<bool> seen(n,
false);
1273 for (std::size_t i : order) seen[i] =
true;
1274 for (std::size_t i = 0; i < n; ++i)
1275 if (!seen[i]) order.push_back(i);
1287 PhLaw<T> build_ctmc() {
1288 const std::size_t n = activities_.size();
1289 if (n == 1)
return activities_[0].ph_representation();
1291 const Structure st = analyze_structure();
1293 std::vector<PhLaw<T>> block(n);
1294 std::vector<bool> absorbed(n,
false);
1295 for (std::size_t i = 0; i < n; ++i) block[i] = activities_[i].ph_representation();
1297 for (
const LoopInfo& loop : st.loops) {
1298 PhLaw<T> body = activities_[loop.body_acts[0]].ph_representation();
1299 for (std::size_t j = 1; j < loop.body_acts.size(); ++j)
1300 body =
compose_serial(body, activities_[loop.body_acts[j]].ph_representation());
1304 if (loop.end_act !=
npos) {
1305 res =
compose_serial(res, activities_[loop.end_act].ph_representation());
1306 absorbed[loop.end_act] =
true;
1308 block[loop.pre_act] = res;
1309 for (std::size_t idx : loop.body_acts) absorbed[idx] =
true;
1312 for (
const ForkInfo& fork : st.forks) {
1313 const JoinInfo* join = find_matching_join(fork.post_acts, st.joins, fork.is_and);
1314 if (fork.is_and && join ==
nullptr)
continue;
1318 inner = block[fork.post_acts[0]];
1319 for (std::size_t i = 1; i < fork.post_acts.size(); ++i)
1322 std::vector<PhLaw<T>> laws;
1323 for (std::size_t idx : fork.post_acts) laws.push_back(block[idx]);
1328 if (join !=
nullptr && !absorbed[join->post_act]) {
1330 absorbed[join->post_act] =
true;
1332 block[fork.pre_act] = res;
1333 for (std::size_t idx : fork.post_acts) absorbed[idx] =
true;
1336 const std::vector<std::size_t> order = topological_sort(st.adj);
1337 bool started =
false;
1339 for (std::size_t idx : order) {
1340 if (absorbed[idx])
continue;
1348 if (!started) out = activities_[0].ph_representation();
1352 static const JoinInfo* find_matching_join(
const std::vector<std::size_t>& post_acts,
1353 const std::vector<JoinInfo>& joins,
bool is_and) {
1354 std::vector<std::size_t> want = post_acts;
1355 std::sort(want.begin(), want.end());
1356 for (
const JoinInfo& j : joins) {
1357 if (j.is_and != is_and)
continue;
1358 std::vector<std::size_t> have = j.pre_acts;
1359 std::sort(have.begin(), have.end());
1360 if (have == want)
return &j;
1366 std::vector<WorkflowActivity<T>> activities_;
1367 std::map<std::string, std::size_t> activity_map_;
1368 std::vector<Precedence<T>> precedences_;
1370 Distrib<T> cached_ph_;
1371 bool cached_valid_ =
false;
1373 bool sp_built_ =
false;
1374 bool sp_failed_ =
false;