239 static constexpr std::size_t
npos =
static_cast<std::size_t
>(-1);
243 const std::string&
name()
const {
return name_; }
247 if (activity_map_.find(
name) != activity_map_.end())
248 throw InputError(
"Workflow: activity '" +
name +
"' is already declared");
250 const std::size_t idx = activities_.size() - 1;
251 activity_map_[
name] = idx;
262 precedences_.push_back(prec);
267 const std::vector<WorkflowActivity<T>>&
activities()
const {
return activities_; }
268 const std::vector<Precedence<T>>&
precedences()
const {
return precedences_; }
271 auto it = activity_map_.find(
name);
272 return it == activity_map_.end() ?
npos : it->second;
278 return activities_[i];
283 if (i >= activities_.size())
throw InputError(
"Workflow: activity index out of range");
284 return activities_[i];
293 if (activities_.empty())
294 throw InputError(
"Workflow must have at least one activity.");
297 for (
const std::string& nm : prec.pre_acts)
300 "' referenced in precedence not found in workflow.");
301 for (
const std::string& nm : prec.post_acts)
304 "' referenced in precedence not found in workflow.");
308 if (prec.post_type == PrecedenceType::POST_OR) {
309 if (prec.post_params.empty())
310 throw InputError(
"OR-fork must have probabilities specified.");
312 for (
const T& p : prec.post_params) total += p;
316 throw InputError(
"OR-fork probabilities must sum to 1.");
318 if (prec.post_type == PrecedenceType::POST_LOOP) {
319 if (prec.post_params.size() != 1)
320 throw InputError(
"Loop count must be a single positive number.");
322 throw InputError(
"Loop count must be a positive number.");
324 if (prec.pre_type == PrecedenceType::PRE_AND && !prec.pre_params.empty()) {
326 const double nb =
static_cast<double>(prec.pre_acts.size());
327 if (quorum > 0.0 && quorum < nb)
329 "AND-join with quorum " + std::to_string(
static_cast<long>(quorum)) +
330 " of " + std::to_string(
static_cast<long>(nb)) +
331 " is not supported by Workflow: a partial join is not the maximum of the "
332 "branches. Use a full join, or SolverLN with method='default', which "
333 "routes the join explicitly.");
346 if (cached_valid_)
return cached_ph_;
350 if (!compose_series_parallel(law)) {
356 cached_valid_ =
true;
397 activities_[i].set_host_demand(host_demand);
409 throw InputError(
"The activity mean must be a positive finite scalar.");
413 const Distrib<T>& d = activities_[i].host_demand();
414 const T old_mean = d.
mean;
415 if (d.
type == ProcessType::IMMEDIATE ||
422 const T factor = T(old_mean / mean_value);
430 cached_valid_ =
false;
431 sp_tree_.nodes.clear();
432 sp_tree_.root =
npos;
433 sp_tree_.leaf_of.clear();
434 sp_tree_.execs.clear();
441 cached_valid_ =
false;
442 if (!sp_built_)
return;
443 if (act_idx >= sp_tree_.leaf_of.size() || sp_tree_.leaf_of[act_idx] ==
npos) {
448 invalidate_branch(sp_tree_, sp_tree_.leaf_of[act_idx]);
458 cached_valid_ =
false;
459 if (!sp_built_)
return;
460 if (act_idx >= sp_tree_.leaf_of.size() || sp_tree_.leaf_of[act_idx] ==
npos) {
464 const std::size_t k = sp_tree_.leaf_of[act_idx];
465 invalidate_branch(sp_tree_, k);
467 if (node.
law.S.rows() > 0) {
468 for (std::size_t i = 0; i < node.
law.S.rows(); ++i)
469 for (std::size_t j = 0; j < node.
law.S.cols(); ++j)
470 node.
law.S(i, j) = T(node.
law.S(i, j) * factor);
480 if (!sp_built_ && !sp_failed_) build_sp_tree();
481 return sp_built_ ? &sp_tree_ :
nullptr;
498 const std::size_t n1 = a.
S.rows(), n2 = b.
S.rows();
502 for (std::size_t i = 0; i < n1; ++i)
503 for (std::size_t j = 0; j < n1; ++j) out.
S(i, j) = a.
S(i, j);
504 for (std::size_t i = 0; i < n2; ++i)
505 for (std::size_t j = 0; j < n2; ++j) out.
S(n1 + i, n1 + j) = b.
S(i, j);
507 for (std::size_t i = 0; i < n1; ++i) {
509 for (std::size_t j = 0; j < n1; ++j) rate += a.
S(i, j);
511 for (std::size_t j = 0; j < n2; ++j) out.
S(i, n1 + j) = T(rate * b.
alpha[j]);
515 for (
const T& v : a.
alpha) defect -= v;
516 out.
alpha.assign(n1 + n2, zero);
517 for (std::size_t i = 0; i < n1; ++i) out.
alpha[i] = a.
alpha[i];
518 for (std::size_t j = 0; j < n2; ++j) out.
alpha[n1 + j] = T(defect * b.
alpha[j]);
531 const std::size_t n1 = a.
S.rows(), n2 = b.
S.rows();
532 const std::size_t nboth = n1 * n2;
533 const std::size_t ntot = nboth + n1 + n2;
535 std::vector<T> abs1(n1, zero), abs2(n2, zero);
536 for (std::size_t i = 0; i < n1; ++i) {
538 for (std::size_t j = 0; j < n1; ++j) r += a.
S(i, j);
541 for (std::size_t i = 0; i < n2; ++i) {
543 for (std::size_t j = 0; j < n2; ++j) r += b.
S(i, j);
551 for (std::size_t i = 0; i < n1; ++i)
552 for (std::size_t j = 0; j < n2; ++j) {
553 const std::size_t r = i * n2 + j;
554 for (std::size_t ii = 0; ii < n1; ++ii)
555 out.
S(r, ii * n2 + j) += a.
S(i, ii);
556 for (std::size_t jj = 0; jj < n2; ++jj)
557 out.
S(r, i * n2 + jj) += b.
S(j, jj);
559 out.
S(r, nboth + i) += abs2[j];
560 out.
S(r, nboth + n1 + j) += abs1[i];
563 for (std::size_t i = 0; i < n1; ++i)
564 for (std::size_t j = 0; j < n1; ++j) out.
S(nboth + i, nboth + j) = a.
S(i, j);
565 for (std::size_t i = 0; i < n2; ++i)
566 for (std::size_t j = 0; j < n2; ++j)
567 out.
S(nboth + n1 + i, nboth + n1 + j) = b.
S(i, j);
569 out.
alpha.assign(ntot, zero);
570 for (std::size_t i = 0; i < n1; ++i)
571 for (std::size_t j = 0; j < n2; ++j)
582 const std::vector<T>& probs) {
584 std::size_t total = 0;
585 for (
const PhLaw<T>& l : laws) total += l.S.rows();
589 out.
alpha.assign(total, zero);
592 for (std::size_t b = 0; b < laws.size(); ++b) {
593 const std::size_t nb = laws[b].
S.rows();
594 for (std::size_t i = 0; i < nb; ++i) {
595 for (std::size_t j = 0; j < nb; ++j) out.
S(off + i, off + j) = laws[b].
S(i, j);
596 out.
alpha[off + i] = T(probs[b] * laws[b].alpha[i]);
621 const std::size_t n = body.
S.rows();
626 out.
alpha.assign(1, one);
636 out.
alpha.assign(n + 1, zero);
637 for (std::size_t i = 0; i < n; ++i) out.
alpha[i] = T(count * body.
alpha[i]);
638 out.
alpha[n] = T(one - count);
640 for (std::size_t i = 0; i < n; ++i)
641 for (std::size_t j = 0; j < n; ++j) out.
S(i, j) = body.
S(i, j);
646 const T p = T(one - one / count);
648 for (
const T& v : body.
alpha) defect -= v;
649 const T denom = T(one - p * defect);
651 out.
alpha.assign(n, zero);
652 for (std::size_t i = 0; i < n; ++i) out.
alpha[i] = T(body.
alpha[i] / denom);
655 for (std::size_t i = 0; i < n; ++i) {
657 for (std::size_t j = 0; j < n; ++j) rate += body.
S(i, j);
658 rate = T(-rate * p / denom);
659 for (std::size_t j = 0; j < n; ++j) out.
S(i, j) += T(rate * body.
alpha[j]);
691 const std::size_t n = S.
rows();
692 std::vector<std::vector<bool>> A(n, std::vector<bool>(n,
false));
693 std::vector<std::size_t> in_deg(n, 0);
694 for (std::size_t i = 0; i < n; ++i)
695 for (std::size_t j = 0; j < n; ++j) {
696 if (i == j)
continue;
703 std::vector<std::size_t> queue;
704 for (std::size_t i = 0; i < n; ++i)
705 if (in_deg[i] == 0) queue.push_back(i);
706 std::size_t visited = 0, head = 0;
707 while (head < queue.size()) {
708 const std::size_t cur = queue[head++];
710 for (std::size_t j = 0; j < n; ++j) {
711 if (!A[cur][j])
continue;
712 if (--in_deg[j] == 0) queue.push_back(j);
726 p.
pre_type = PrecedenceType::PRE_SEQ;
731 static std::vector<Precedence<T>>
SerialSequence(
const std::vector<std::string>& acts) {
732 std::vector<Precedence<T>> out;
733 for (std::size_t i = 0; i + 1 < acts.size(); ++i)
734 out.push_back(
Serial(acts[i], acts[i + 1]));
739 const std::vector<std::string>& posts) {
743 p.
pre_type = PrecedenceType::PRE_SEQ;
750 const std::string& post,
751 const std::vector<T>& quorum = std::vector<T>()) {
755 p.
pre_type = PrecedenceType::PRE_AND;
762 const std::vector<T>& probs) {
766 p.
pre_type = PrecedenceType::PRE_SEQ;
773 const std::string& post) {
777 p.
pre_type = PrecedenceType::PRE_OR;
791 p.
pre_type = PrecedenceType::PRE_SEQ;
803 enum class ParseStatus { END, STOP, JOIN, FAIL };
807 std::vector<std::size_t> out_p, in_p;
808 std::vector<bool> consumed;
811 bool compose_series_parallel(PhLaw<T>& out) {
813 if (sp_failed_)
return false;
815 if (!sp_built_)
return false;
817 out = compose_node(sp_tree_.root);
827 bool build_sp_tree() {
828 sp_tree_.nodes.clear();
829 sp_tree_.root =
npos;
830 sp_tree_.leaf_of.clear();
831 sp_tree_.execs.clear();
835 const std::size_t n = activities_.size();
836 if (n == 0)
return false;
839 S.out_p.assign(n,
npos);
840 S.in_p.assign(n,
npos);
841 S.consumed.assign(n,
false);
845 for (std::size_t p = 0; p < precedences_.size(); ++p) {
846 for (
const std::string& nm : precedences_[p].pre_acts) {
848 if (i ==
npos || S.out_p[i] !=
npos)
return false;
851 for (
const std::string& nm : precedences_[p].post_acts) {
853 if (j ==
npos || S.in_p[j] !=
npos)
return false;
858 std::vector<std::size_t> starts;
859 for (std::size_t i = 0; i < n; ++i)
860 if (S.in_p[i] ==
npos) starts.push_back(i);
861 if (starts.size() != 1)
return false;
863 std::vector<std::size_t> kids;
864 std::size_t stop_at =
npos;
865 const ParseStatus st = sp_parse_seq(S, starts[0], std::vector<std::size_t>(), kids, stop_at);
866 if (st != ParseStatus::END)
return false;
867 for (std::size_t i = 0; i < n; ++i)
868 if (!S.consumed[i])
return false;
870 const std::size_t root = sp_serial_node(kids);
871 if (root ==
npos)
return false;
873 sp_tree_.root = root;
874 sp_tree_.leaf_of.assign(n,
npos);
875 for (std::size_t k = 0; k < sp_tree_.nodes.size(); ++k)
877 sp_tree_.leaf_of[sp_tree_.nodes[k].act] = k;
878 sp_tree_.execs = sp_execution_counts(sp_tree_);
886 ParseStatus sp_parse_seq(ParseState& S, std::size_t cur,
887 const std::vector<std::size_t>& stop_set,
888 std::vector<std::size_t>& kids, std::size_t& stop_at) {
891 if (cur ==
npos)
return ParseStatus::END;
892 if (std::find(stop_set.begin(), stop_set.end(), cur) != stop_set.end()) {
894 return ParseStatus::STOP;
896 if (S.consumed[cur])
return ParseStatus::FAIL;
897 S.consumed[cur] =
true;
898 kids.push_back(sp_add_node(
SPNodeType::LEAF, cur, std::vector<std::size_t>(),
899 std::vector<T>(), num_traits<T>::from_int(0)));
901 const std::size_t p = S.out_p[cur];
902 if (p ==
npos)
return ParseStatus::END;
903 const Precedence<T>& prec = precedences_[p];
904 if (prec.pre_acts.size() > 1) {
907 return ParseStatus::JOIN;
910 std::vector<std::size_t> post_inds = sp_indices_of(prec.post_acts);
911 for (std::size_t i : post_inds)
912 if (i ==
npos)
return ParseStatus::FAIL;
914 std::size_t knode =
npos, next_act =
npos;
915 switch (prec.post_type) {
916 case PrecedenceType::POST_AND:
917 if (!sp_parse_fork(S, post_inds, std::vector<T>(), stop_set,
true, knode,
919 return ParseStatus::FAIL;
920 kids.push_back(knode);
923 case PrecedenceType::POST_OR:
924 if (prec.post_params.size() != post_inds.size())
return ParseStatus::FAIL;
925 if (!sp_parse_fork(S, post_inds, prec.post_params, stop_set,
false, knode,
927 return ParseStatus::FAIL;
928 kids.push_back(knode);
931 case PrecedenceType::POST_LOOP:
932 if (!sp_parse_loop(S, post_inds, prec.post_params, stop_set, knode, next_act))
933 return ParseStatus::FAIL;
934 kids.push_back(knode);
937 case PrecedenceType::POST_SEQ:
938 if (post_inds.size() != 1)
return ParseStatus::FAIL;
944 return ParseStatus::FAIL;
950 bool sp_parse_fork(ParseState& S,
const std::vector<std::size_t>& branch_heads,
951 const std::vector<T>& probs,
const std::vector<std::size_t>& stop_set,
952 bool is_and, std::size_t& knode, std::size_t& next_act) {
953 const std::size_t nb = branch_heads.size();
954 std::vector<std::size_t> branch_nodes(nb,
npos), bstop(nb,
npos);
955 std::vector<ParseStatus> bstatus(nb, ParseStatus::FAIL);
957 for (std::size_t b = 0; b < nb; ++b) {
958 std::vector<std::size_t> bkids;
959 std::size_t sa =
npos;
960 const ParseStatus st = sp_parse_seq(S, branch_heads[b], stop_set, bkids, sa);
961 if (st == ParseStatus::FAIL)
return false;
962 const std::size_t bn = sp_serial_node(bkids);
963 if (bn ==
npos)
return false;
964 branch_nodes[b] = bn;
969 const bool all_join =
970 std::all_of(bstatus.begin(), bstatus.end(),
971 [](ParseStatus s) { return s == ParseStatus::JOIN; });
972 const bool all_end = std::all_of(bstatus.begin(), bstatus.end(),
973 [](ParseStatus s) { return s == ParseStatus::END; });
974 const bool all_stop = std::all_of(bstatus.begin(), bstatus.end(),
975 [](ParseStatus s) { return s == ParseStatus::STOP; });
976 const bool same_stop =
977 std::all_of(bstop.begin(), bstop.end(),
978 [&bstop](std::size_t x) { return x == bstop[0]; });
981 if (!same_stop)
return false;
982 const Precedence<T>& join_prec = precedences_[bstop[0]];
983 if (join_prec.pre_acts.size() != nb)
return false;
985 if (join_prec.pre_type != PrecedenceType::PRE_AND)
return false;
987 if (join_prec.pre_type != PrecedenceType::PRE_OR)
return false;
989 const std::vector<std::size_t> post_inds = sp_indices_of(join_prec.post_acts);
990 if (post_inds.size() != 1 || post_inds[0] ==
npos)
return false;
991 next_act = post_inds[0];
992 }
else if (all_end) {
996 }
else if (!is_and && all_stop && same_stop) {
1003 num_traits<T>::from_int(0));
1011 bool sp_parse_loop(ParseState& S,
const std::vector<std::size_t>& post_inds,
1012 const std::vector<T>& counts,
const std::vector<std::size_t>& stop_set,
1013 std::size_t& knode, std::size_t& next_act) {
1014 if (counts.size() != 1)
return false;
1015 const T count = counts[0];
1017 std::vector<std::size_t> body_acts;
1018 std::size_t end_act =
npos;
1019 if (post_inds.size() >= 2) {
1020 body_acts.assign(post_inds.begin(), post_inds.end() - 1);
1021 end_act = post_inds.back();
1023 body_acts.push_back(post_inds[0]);
1026 std::vector<std::size_t> loop_stop = stop_set;
1027 loop_stop.insert(loop_stop.end(), body_acts.begin(), body_acts.end());
1028 if (end_act !=
npos) loop_stop.push_back(end_act);
1030 std::vector<std::size_t> body_kids;
1032 while (j < body_acts.size()) {
1033 const std::size_t a = body_acts[j];
1034 if (S.consumed[a]) {
1038 std::vector<std::size_t> this_stop;
1039 for (std::size_t x : loop_stop)
1040 if (x != a) this_stop.push_back(x);
1042 std::vector<std::size_t> kk;
1043 std::size_t sa =
npos;
1044 const ParseStatus st = sp_parse_seq(S, a, this_stop, kk, sa);
1045 if (st == ParseStatus::FAIL)
return false;
1046 body_kids.insert(body_kids.end(), kk.begin(), kk.end());
1048 if (st == ParseStatus::END) {
1050 }
else if (st == ParseStatus::STOP) {
1051 const auto it = std::find(body_acts.begin(), body_acts.end(), sa);
1052 if (it != body_acts.end()) {
1053 j =
static_cast<std::size_t
>(it - body_acts.begin());
1054 }
else if (end_act !=
npos && sa == end_act) {
1055 j = body_acts.size();
1066 const std::size_t body_node = sp_serial_node(body_kids);
1067 if (body_node ==
npos)
return false;
1070 std::vector<T>(), count);
1076 std::size_t sp_serial_node(
const std::vector<std::size_t>& kids) {
1077 if (kids.empty())
return npos;
1078 if (kids.size() == 1)
return kids[0];
1080 num_traits<T>::from_int(0));
1083 std::size_t sp_add_node(
SPNodeType type, std::size_t act,
1084 const std::vector<std::size_t>& kids,
const std::vector<T>& probs,
1092 sp_tree_.nodes.push_back(node);
1093 const std::size_t k = sp_tree_.nodes.size() - 1;
1094 for (std::size_t c : kids) sp_tree_.nodes[c].parent = k;
1098 std::vector<std::size_t> sp_indices_of(
const std::vector<std::string>& names)
const {
1099 std::vector<std::size_t> out(names.size(),
npos);
1100 for (std::size_t i = 0; i < names.size(); ++i) out[i] =
activity_index(names[i]);
1108 PhLaw<T> compose_node(std::size_t k) {
1109 SPNode<T>& node = sp_tree_.nodes[k];
1110 if (node.valid)
return node.law;
1113 switch (node.type) {
1115 law = activities_[node.act].ph_representation();
1118 const std::vector<std::size_t> kids = node.kids;
1119 law = compose_node(kids[0]);
1120 for (std::size_t i = 1; i < kids.size(); ++i)
1125 const std::vector<std::size_t> kids = node.kids;
1126 law = compose_node(kids[0]);
1127 for (std::size_t i = 1; i < kids.size(); ++i)
1132 const std::vector<std::size_t> kids = node.kids;
1133 std::vector<PhLaw<T>> laws;
1134 for (std::size_t c : kids) laws.push_back(compose_node(c));
1139 const std::size_t
child = node.kids[0];
1145 sp_tree_.nodes[k].law = law;
1146 sp_tree_.nodes[k].valid =
true;
1150 static void invalidate_branch(SPTree<T>& tree, std::size_t k) {
1152 tree.nodes[k].valid =
false;
1153 k = tree.nodes[k].parent;
1163 static std::vector<T> sp_execution_counts(
const SPTree<T>& tree) {
1164 std::vector<T> execs(tree.nodes.size(), num_traits<T>::from_int(0));
1165 if (tree.root ==
npos)
return execs;
1166 execs[tree.root] = num_traits<T>::from_int(1);
1167 std::vector<std::size_t> stack(1, tree.root);
1168 while (!stack.empty()) {
1169 const std::size_t k = stack.back();
1171 const SPNode<T>& node = tree.nodes[k];
1172 for (std::size_t i = 0; i < node.kids.size(); ++i) {
1173 const std::size_t c = node.kids[i];
1175 execs[c] = T(execs[k] * node.probs[i]);
1177 execs[c] = T(execs[k] * node.count);
1179 execs[c] = execs[k];
1193 std::size_t pre_act =
npos;
1194 std::vector<std::size_t> post_acts;
1195 std::vector<T> probs;
1200 std::vector<std::size_t> pre_acts;
1201 std::size_t post_act =
npos;
1205 std::size_t pre_act =
npos;
1206 std::vector<std::size_t> body_acts;
1207 std::size_t end_act =
npos;
1208 T count = num_traits<T>::from_int(1);
1212 std::vector<std::vector<std::size_t>> adj;
1213 std::vector<std::size_t> in_deg, out_deg;
1214 std::vector<ForkInfo> forks;
1215 std::vector<JoinInfo> joins;
1216 std::vector<LoopInfo> loops;
1219 Structure analyze_structure()
const {
1220 const std::size_t n = activities_.size();
1222 st.adj.assign(n, std::vector<std::size_t>());
1223 st.in_deg.assign(n, 0);
1224 st.out_deg.assign(n, 0);
1226 for (
const Precedence<T>& prec : precedences_) {
1227 const std::vector<std::size_t> pre = sp_indices_of(prec.pre_acts);
1228 const std::vector<std::size_t> post = sp_indices_of(prec.post_acts);
1229 for (std::size_t i : pre)
1230 for (std::size_t j : post) {
1231 st.adj[i].push_back(j);
1236 if (prec.post_type == PrecedenceType::POST_AND) {
1241 st.forks.push_back(f);
1242 }
else if (prec.post_type == PrecedenceType::POST_OR) {
1247 f.probs = prec.post_params;
1248 st.forks.push_back(f);
1249 }
else if (prec.post_type == PrecedenceType::POST_LOOP) {
1252 if (post.size() > 1) {
1253 l.body_acts.assign(post.begin(), post.end() - 1);
1254 l.end_act = post.back();
1258 l.count = prec.post_params.empty() ? num_traits<T>::from_int(1)
1259 : prec.post_params[0];
1260 st.loops.push_back(l);
1263 if (prec.pre_type == PrecedenceType::PRE_AND ||
1264 prec.pre_type == PrecedenceType::PRE_OR) {
1266 j.is_and = prec.pre_type == PrecedenceType::PRE_AND;
1268 j.post_act = post[0];
1269 st.joins.push_back(j);
1275 std::vector<std::size_t> topological_sort(
1276 const std::vector<std::vector<std::size_t>>& adj)
const {
1277 const std::size_t n = activities_.size();
1278 std::vector<std::size_t> in_deg(n, 0);
1279 for (
const std::vector<std::size_t>& nbrs : adj)
1280 for (std::size_t j : nbrs) ++in_deg[j];
1282 std::vector<std::size_t> queue, order;
1283 for (std::size_t i = 0; i < n; ++i)
1284 if (in_deg[i] == 0) queue.push_back(i);
1285 std::size_t head = 0;
1286 while (head < queue.size()) {
1287 const std::size_t cur = queue[head++];
1288 order.push_back(cur);
1289 for (std::size_t nx : adj[cur])
1290 if (--in_deg[nx] == 0) queue.push_back(nx);
1292 std::vector<bool> seen(n,
false);
1293 for (std::size_t i : order) seen[i] =
true;
1294 for (std::size_t i = 0; i < n; ++i)
1295 if (!seen[i]) order.push_back(i);
1307 PhLaw<T> build_ctmc() {
1308 const std::size_t n = activities_.size();
1309 if (n == 1)
return activities_[0].ph_representation();
1311 const Structure st = analyze_structure();
1313 std::vector<PhLaw<T>> block(n);
1314 std::vector<bool> absorbed(n,
false);
1315 for (std::size_t i = 0; i < n; ++i) block[i] = activities_[i].ph_representation();
1317 for (
const LoopInfo& loop : st.loops) {
1318 PhLaw<T> body = activities_[loop.body_acts[0]].ph_representation();
1319 for (std::size_t j = 1; j < loop.body_acts.size(); ++j)
1320 body =
compose_serial(body, activities_[loop.body_acts[j]].ph_representation());
1324 if (loop.end_act !=
npos) {
1325 res =
compose_serial(res, activities_[loop.end_act].ph_representation());
1326 absorbed[loop.end_act] =
true;
1328 block[loop.pre_act] = res;
1329 for (std::size_t idx : loop.body_acts) absorbed[idx] =
true;
1332 for (
const ForkInfo& fork : st.forks) {
1333 const JoinInfo* join = find_matching_join(fork.post_acts, st.joins, fork.is_and);
1334 if (fork.is_and && join ==
nullptr)
continue;
1338 inner = block[fork.post_acts[0]];
1339 for (std::size_t i = 1; i < fork.post_acts.size(); ++i)
1342 std::vector<PhLaw<T>> laws;
1343 for (std::size_t idx : fork.post_acts) laws.push_back(block[idx]);
1348 if (join !=
nullptr && !absorbed[join->post_act]) {
1350 absorbed[join->post_act] =
true;
1352 block[fork.pre_act] = res;
1353 for (std::size_t idx : fork.post_acts) absorbed[idx] =
true;
1356 const std::vector<std::size_t> order = topological_sort(st.adj);
1357 bool started =
false;
1359 for (std::size_t idx : order) {
1360 if (absorbed[idx])
continue;
1368 if (!started) out = activities_[0].ph_representation();
1372 static const JoinInfo* find_matching_join(
const std::vector<std::size_t>& post_acts,
1373 const std::vector<JoinInfo>& joins,
bool is_and) {
1374 std::vector<std::size_t> want = post_acts;
1375 std::sort(want.begin(), want.end());
1376 for (
const JoinInfo& j : joins) {
1377 if (j.is_and != is_and)
continue;
1378 std::vector<std::size_t> have = j.pre_acts;
1379 std::sort(have.begin(), have.end());
1380 if (have == want)
return &j;
1386 std::vector<WorkflowActivity<T>> activities_;
1387 std::map<std::string, std::size_t> activity_map_;
1388 std::vector<Precedence<T>> precedences_;
1390 Distrib<T> cached_ph_;
1391 bool cached_valid_ =
false;
1393 bool sp_built_ =
false;
1394 bool sp_failed_ =
false;