Jlm
TarjanScc.hpp
Go to the documentation of this file.
1 /*
2  * Copyright 2024 HÃ¥vard Krogstie <krogstie.havard@gmail.com>
3  * See COPYING for terms of redistribution.
4  */
5 
6 #ifndef JLM_UTIL_TARJANSCC_HPP
7 #define JLM_UTIL_TARJANSCC_HPP
8 
9 #include <jlm/util/common.hpp>
10 
11 #include <cstddef>
12 #include <cstdint>
13 #include <limits>
14 #include <stack>
15 #include <vector>
16 
17 namespace jlm::util
18 {
19 
50 template<typename NodeType, typename UnificationRootFunctor, typename SuccessorFunctor>
51 NodeType
53  NodeType numNodes,
54  UnificationRootFunctor unificationRoot,
55  SuccessorFunctor & successors,
56  std::vector<NodeType> & sccIndex,
57  std::vector<NodeType> & reverseTopologicalOrder)
58 {
59  NodeType sccsFinished = 0;
60 
61  // What SCC each node ends up in
62  sccIndex.resize(numNodes);
63  // Partially ordered topological order
64  reverseTopologicalOrder.resize(0);
65 
66  // Use the max values of the NodeType as sentinel values
67  const NodeType NOT_VISITED = std::numeric_limits<NodeType>::max();
68  const NodeType SCC_FINISHED = NOT_VISITED - 1;
69  JLM_ASSERT(numNodes <= SCC_FINISHED);
70 
71  // Non-recursive implementation of Tarjan's SCC
72  std::vector<NodeType> order(numNodes, NOT_VISITED);
73  NodeType nextOrder = 0;
74  // The lowest order seen through any path of edges that do not enter finished SCCs
75  std::vector<NodeType> lowLink(numNodes);
76 
77  // The DFS stack is a regular DFS traversal stack.
78  // Note that the same element can be pushed many times, but will only be visited twice
79  std::stack<NodeType> dfsStack;
80  // The SCC stack is only popped once an SCC is found
81  std::stack<NodeType> sccStack;
82 
83  // Find SCCs
84  for (NodeType startNode = 0; startNode < numNodes; startNode++)
85  {
86  // Skip nodes that are not roots
87  if (unificationRoot(startNode) != startNode)
88  continue;
89 
90  // Only start DFSs at unvisited nodes
91  if (order[startNode] != NOT_VISITED)
92  continue;
93 
94  dfsStack.push(startNode);
95  while (!dfsStack.empty())
96  {
97  auto node = dfsStack.top();
98 
99  if (order[node] == NOT_VISITED) // This is the first time node is visited
100  {
101  sccStack.push(node);
102  order[node] = nextOrder++;
103  lowLink[node] = order[node];
104 
105  for (auto next : successors(node))
106  {
107  next = unificationRoot(next);
108 
109  // Visit nodes that have not been visited before
110  if (order[next] == NOT_VISITED)
111  dfsStack.push(next);
112  }
113  }
114  else if (order[node] == SCC_FINISHED) // This node has already been fully processed
115  {
116  dfsStack.pop();
117  }
118  else // This node is being visited for the second time, i.e., the dfs post-visit
119  {
120  dfsStack.pop();
121 
122  for (auto next : successors(node))
123  {
124  next = unificationRoot(next);
125 
126  // Ignore edges to nodes that are already part of a finished SCC
127  if (order[next] == SCC_FINISHED)
128  continue;
129  lowLink[node] = std::min(lowLink[node], lowLink[next]);
130  }
131 
132  // This node is the root of an SCC
133  if (lowLink[node] == order[node])
134  {
135  const auto thisSccIndex = sccsFinished;
136 
137  while (true)
138  {
139  auto top = sccStack.top();
140  sccStack.pop();
141  order[top] = SCC_FINISHED;
142  sccIndex[top] = thisSccIndex;
143  reverseTopologicalOrder.push_back(top);
144 
145  if (top == node)
146  break;
147  }
148 
149  sccsFinished++;
150  }
151  }
152  }
153  }
154  JLM_ASSERT(sccStack.empty());
155 
156  return sccsFinished;
157 }
158 
175 template<typename NodeType, typename SuccessorFunctor>
176 NodeType
178  NodeType numNodes,
179  SuccessorFunctor & successors,
180  std::vector<NodeType> & sccIndex,
181  std::vector<NodeType> & reverseTopologicalOrder)
182 {
183  const auto identity = [](NodeType n)
184  {
185  return n;
186  };
187 
188  // Because all nodes are roots, we know how long this vector needs to be
189  reverseTopologicalOrder.reserve(numNodes);
190 
192  numNodes,
193  identity,
194  successors,
195  sccIndex,
196  reverseTopologicalOrder);
197 }
198 
199 }
200 
201 #endif // JLM_UTIL_TARJANSCC_HPP
#define JLM_ASSERT(x)
Definition: common.hpp:16
NodeType FindStronglyConnectedComponents(NodeType numNodes, UnificationRootFunctor unificationRoot, SuccessorFunctor &successors, std::vector< NodeType > &sccIndex, std::vector< NodeType > &reverseTopologicalOrder)
Definition: TarjanScc.hpp:52