5#ifndef LINE_API_WF_WF_PARALLEL_DETECTOR_H
6#define LINE_API_WF_WF_PARALLEL_DETECTOR_H
61inline bool wf_valid_fork_join_pair(
int fork,
int join,
const std::map<
int, std::vector<int>>& adj,
62 const std::set<int>& forkSet,
const std::set<int>& joinSet) {
63 std::deque<int> queue;
64 std::set<int> visited;
65 std::map<int, long> pathCount;
66 queue.push_back(fork);
69 while (!queue.empty()) {
70 const int current = queue.front();
72 if (!visited.insert(current).second)
continue;
73 std::map<int, std::vector<int>>::const_iterator it = adj.find(current);
74 if (it == adj.end())
continue;
75 for (std::size_t k = 0; k < it->second.size(); ++k) {
76 const int nb = it->second[k];
78 pathCount[join] += pathCount[current];
79 }
else if (!visited.count(nb) && !forkSet.count(nb) && !joinSet.count(nb)) {
81 pathCount[nb] += pathCount[current];
85 std::map<int, long>::const_iterator jt = pathCount.find(join);
86 return jt != pathCount.end() && jt->second > 1;
91std::set<int> wf_reachable(
const Matrix<T>& linkMatrix,
int startNode,
int endNode) {
92 std::set<int> reachable;
93 std::set<int> visited;
94 std::deque<int> queue;
95 queue.push_back(startNode);
96 while (!queue.empty()) {
97 const int current = queue.front();
99 if (visited.count(current) || current == endNode)
continue;
100 visited.insert(current);
101 for (std::size_t i = 0; i < linkMatrix.
rows(); ++i) {
102 const int s = wf_id(linkMatrix, i, 0);
103 const int e = wf_id(linkMatrix, i, 1);
104 if (s == current && e != endNode) {
115std::set<int> wf_can_reach(
const Matrix<T>& linkMatrix,
int targetNode,
int startNode) {
116 const std::map<int, std::vector<int>> radj = wf_reverse_adjacency(linkMatrix);
117 std::set<int> canReach;
118 std::set<int> visited;
119 std::deque<int> queue;
120 queue.push_back(targetNode);
121 while (!queue.empty()) {
122 const int current = queue.front();
124 if (visited.count(current) || current == startNode)
continue;
125 visited.insert(current);
126 std::map<int, std::vector<int>>::const_iterator it = radj.find(current);
127 if (it == radj.end())
continue;
128 for (std::size_t k = 0; k < it->second.size(); ++k) {
129 const int pred = it->second[k];
130 if (pred != startNode) {
131 canReach.insert(pred);
132 queue.push_back(pred);
150 const std::vector<int>& serviceNodes,
151 const std::vector<int>& forkNodes,
152 const std::vector<int>& joinNodes) {
153 detail::wf_check(linkMatrix);
154 const std::set<int> forkSet(forkNodes.begin(), forkNodes.end());
155 const std::set<int> joinSet(joinNodes.begin(), joinNodes.end());
156 const std::set<int> serviceSet(serviceNodes.begin(), serviceNodes.end());
157 const std::map<int, std::vector<int>> adj = detail::wf_adjacency(linkMatrix);
159 std::vector<std::vector<int>> patterns;
160 for (std::size_t a = 0; a < forkNodes.size(); ++a) {
161 for (std::size_t b = 0; b < joinNodes.size(); ++b) {
162 const int fork = forkNodes[a];
163 const int join = joinNodes[b];
164 if (!detail::wf_valid_fork_join_pair(fork, join, adj, forkSet, joinSet))
continue;
166 const std::set<int> fromFork = detail::wf_reachable(linkMatrix, fork, join);
167 const std::set<int> toJoin = detail::wf_can_reach(linkMatrix, join, fork);
168 std::vector<int> parallelServices;
169 for (std::set<int>::const_iterator it = fromFork.begin(); it != fromFork.end(); ++it)
170 if (toJoin.count(*it) && serviceSet.count(*it)) parallelServices.push_back(*it);
171 if (parallelServices.size() > 1) patterns.push_back(parallelServices);
183 const std::vector<int>& forkNodes,
184 const std::vector<int>& joinNodes) {
185 detail::wf_check(linkMatrix);
186 if (pattern.size() < 2)
return false;
187 const std::set<int> forkSet(forkNodes.begin(), forkNodes.end());
188 const std::set<int> joinSet(joinNodes.begin(), joinNodes.end());
190 std::set<int> sources, targets;
191 for (std::size_t p = 0; p < pattern.size(); ++p) {
192 for (std::size_t i = 0; i < linkMatrix.
rows(); ++i) {
193 const int s = detail::wf_id(linkMatrix, i, 0);
194 const int e = detail::wf_id(linkMatrix, i, 1);
195 if (e == pattern[p] && forkSet.count(s)) sources.insert(s);
196 if (s == pattern[p] && joinSet.count(e)) targets.insert(e);
199 return sources.size() == 1 && targets.size() == 1;
207 std::size_t total = 0;
209 for (std::size_t i = 0; i < patterns.size(); ++i) {
210 total += patterns[i].size();
211 if (patterns[i].size() > mx) mx = patterns[i].size();
215 if (!patterns.empty())
The exception types the port throws.
Dense matrix and non-owning view.
bool validate_parallel_pattern(const std::vector< int > &pattern, const Matrix< T > &linkMatrix, const std::vector< int > &forkNodes, const std::vector< int > &joinNodes)
A pattern is valid when its nodes have exactly one common fork predecessor and exactly one common joi...
ParallelStats< T > get_parallel_stats(const std::vector< std::vector< int > > &patterns)
Count, total, mean and maximum degree of parallelism.
std::vector< std::vector< int > > detect_parallel(const Matrix< T > &linkMatrix, const std::vector< int > &serviceNodes, const std::vector< int > &forkNodes, const std::vector< int > &joinNodes)
Number-type abstraction for the templated API port.
Mirrors the Java getParallelStats map.
std::size_t totalParallelNodes
std::size_t maxParallelism
Shared conventions of the workflow pattern detectors.