Jlm
Loading...
Searching...
No Matches
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
17namespace jlm::util
18{
19
50template<typename NodeType, typename UnificationRootFunctor, typename SuccessorFunctor>
51NodeType
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
175template<typename NodeType, typename SuccessorFunctor>
176NodeType
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