MLIR 22.0.0git
RootOrdering.cpp
Go to the documentation of this file.
1//===- RootOrdering.cpp - Optimal root ordering ---------------------------===//
2//
3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4// See https://llvm.org/LICENSE.txt for license information.
5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6//
7//===----------------------------------------------------------------------===//
8//
9// An implementation of Edmonds' optimal branching algorithm. This is a
10// directed analogue of the minimum spanning tree problem for a given root.
11//
12//===----------------------------------------------------------------------===//
13
14#include "RootOrdering.h"
15
16#include "llvm/ADT/DenseMap.h"
17#include "llvm/ADT/SmallVector.h"
18#include <utility>
19
20using namespace mlir;
21using namespace mlir::pdl_to_pdl_interp;
22
23/// Returns the cycle implied by the specified parent relation, starting at the
24/// given node.
26 Value rep) {
28 Value node = rep;
29 do {
30 cycle.push_back(node);
31 node = parents.lookup(node);
32 assert(node && "got an empty value in the cycle");
33 } while (node != rep);
34 return cycle;
35}
36
37/// Contracts the specified cycle in the given graph in-place.
38/// The parentsCost map specifies, for each node in the cycle, the lowest cost
39/// among the edges entering that node. Then, the nodes in the cycle C are
40/// replaced with a single node v_C (the first node in the cycle). All edges
41/// (u, v) entering the cycle, v \in C, are replaced with a single edge
42/// (u, v_C) with an appropriately chosen cost, and the selected node v is
43/// marked in the output map actualTarget[u]. All edges (u, v) leaving the
44/// cycle, u \in C, are replaced with a single edge (v_C, v), and the selected
45/// node u is marked in the ouptut map actualSource[v].
46static void contract(RootOrderingGraph &graph, ArrayRef<Value> cycle,
47 const DenseMap<Value, unsigned> &parentDepths,
48 DenseMap<Value, Value> &actualSource,
49 DenseMap<Value, Value> &actualTarget) {
50 Value rep = cycle.front();
51 DenseSet<Value> cycleSet(cycle.begin(), cycle.end());
52
53 // Now, contract the cycle, marking the actual sources and targets.
55 for (auto outer = graph.begin(), e = graph.end(); outer != e; ++outer) {
56 Value target = outer->first;
57 if (cycleSet.contains(target)) {
58 // Target in the cycle => edges incoming to the cycle or within the cycle.
59 unsigned parentDepth = parentDepths.lookup(target);
60 for (const auto &inner : outer->second) {
61 Value source = inner.first;
62 // Ignore edges within the cycle.
63 if (cycleSet.contains(source))
64 continue;
65
66 // Edge incoming to the cycle.
67 std::pair<unsigned, unsigned> cost = inner.second.cost;
68 assert(parentDepth <= cost.first && "invalid parent depth");
69
70 // Subtract the cost of the parent within the cycle from the cost of
71 // the edge incoming to the cycle. This update ensures that the cost
72 // of the minimum-weight spanning arborescence of the entire graph is
73 // the cost of arborescence for the contracted graph plus the cost of
74 // the cycle, no matter which edge in the cycle we choose to drop.
75 cost.first -= parentDepth;
76 auto it = repEntries.find(source);
77 if (it == repEntries.end() || it->second.cost > cost) {
78 actualTarget[source] = target;
79 // Do not bother populating the connector (the connector is only
80 // relevant for the final traversal, not for the optimal branching).
81 repEntries[source].cost = cost;
82 }
83 }
84 // Erase the node in the cycle.
85 graph.erase(outer);
86 } else {
87 // Target not in cycle => edges going away from or unrelated to the cycle.
88 DenseMap<Value, RootOrderingEntry> &entries = outer->second;
89 Value bestSource;
90 std::pair<unsigned, unsigned> bestCost;
91 auto inner = entries.begin(), innerE = entries.end();
92 while (inner != innerE) {
93 Value source = inner->first;
94 if (cycleSet.contains(source)) {
95 // Going-away edge => get its cost and erase it.
96 if (!bestSource || bestCost > inner->second.cost) {
97 bestSource = source;
98 bestCost = inner->second.cost;
99 }
100 entries.erase(inner++);
101 } else {
102 ++inner;
103 }
104 }
105
106 // There were going-away edges, contract them.
107 if (bestSource) {
108 entries[rep].cost = bestCost;
109 actualSource[target] = bestSource;
110 }
111 }
112 }
113
114 // Store the edges to the representative.
115 graph[rep] = std::move(repEntries);
116}
117
119 : graph(std::move(graph)), root(root) {}
120
122 // Initialize the parents and total cost.
123 parents.clear();
124 parents[root] = Value();
125 unsigned totalCost = 0;
126
127 // A map that stores the cost of the optimal local choice for each node
128 // in a directed cycle. This map is cleared every time we seed the search.
129 DenseMap<Value, unsigned> parentDepths;
130 parentDepths.reserve(graph.size());
131
132 // Determine if the optimal local choice results in an acyclic graph. This is
133 // done by computing the optimal local choice and traversing up the computed
134 // parents. On success, `parents` will contain the parent of each node.
135 for (const auto &outer : graph) {
136 Value node = outer.first;
137 if (parents.count(node)) // already visited
138 continue;
139
140 // Follow the trail of best sources until we reach an already visited node.
141 // The code will assert if we cannot reach an already visited node, i.e.,
142 // the graph is not strongly connected.
143 parentDepths.clear();
144 do {
145 auto it = graph.find(node);
146 assert(it != graph.end() && "the graph is not strongly connected");
147
148 // Find the best local parent, taking into account both the depth and the
149 // tie breaking rules.
150 Value &bestSource = parents[node];
151 std::pair<unsigned, unsigned> bestCost;
152 for (const auto &inner : it->second) {
153 const RootOrderingEntry &entry = inner.second;
154 if (!bestSource /* initial */ || bestCost > entry.cost) {
155 bestSource = inner.first;
156 bestCost = entry.cost;
157 }
158 }
159 assert(bestSource && "the graph is not strongly connected");
160 parentDepths[node] = bestCost.first;
161 node = bestSource;
162 totalCost += bestCost.first;
163 } while (!parents.count(node));
164
165 // If we reached a non-root node, we have a cycle.
166 if (parentDepths.count(node)) {
167 // Determine the cycle starting at the representative node.
168 SmallVector<Value> cycle = getCycle(parents, node);
169
170 // The following maps disambiguate the source / target of the edges
171 // going out of / into the cycle.
172 DenseMap<Value, Value> actualSource, actualTarget;
173
174 // Contract the cycle and recurse.
175 contract(graph, cycle, parentDepths, actualSource, actualTarget);
176 totalCost = solve();
177
178 // Redirect the going-away edges.
179 for (auto &p : parents)
180 if (p.second == node)
181 // The parent is the node representating the cycle; replace it
182 // with the actual (best) source in the cycle.
183 p.second = actualSource.lookup(p.first);
184
185 // Redirect the unique incoming edge and copy the cycle.
186 Value parent = parents.lookup(node);
187 Value entry = actualTarget.lookup(parent);
188 cycle.push_back(node); // complete the cycle
189 for (size_t i = 0, e = cycle.size() - 1; i < e; ++i) {
190 totalCost += parentDepths.lookup(cycle[i]);
191 if (cycle[i] == entry)
192 parents[cycle[i]] = parent; // break the cycle
193 else
194 parents[cycle[i]] = cycle[i + 1];
195 }
196
197 // `parents` has a complete solution.
198 break;
199 }
200 }
201
202 return totalCost;
203}
204
207 // Invert the parent mapping.
209 for (Value node : nodes) {
210 if (node != root) {
211 Value parent = parents.lookup(node);
212 assert(parent && "invalid parent");
213 children[parent].push_back(node);
214 }
215 }
216
217 // The result which simultaneously acts as a queue.
219 result.reserve(nodes.size());
220 result.emplace_back(root, Value());
221
222 // Perform a BFS, pushing into the queue.
223 for (size_t i = 0; i < result.size(); ++i) {
224 Value node = result[i].first;
225 for (Value child : children[node])
226 result.emplace_back(child, node);
227 }
228
229 return result;
230}
static void contract(RootOrderingGraph &graph, ArrayRef< Value > cycle, const DenseMap< Value, unsigned > &parentDepths, DenseMap< Value, Value > &actualSource, DenseMap< Value, Value > &actualTarget)
Contracts the specified cycle in the given graph in-place.
static SmallVector< Value > getCycle(const DenseMap< Value, Value > &parents, Value rep)
Returns the cycle implied by the specified parent relation, starting at the given node.
This class represents an instance of an SSA value in the MLIR system, representing a computable value...
Definition Value.h:96
unsigned solve()
Runs the Edmonds' algorithm for the current graph, returning the total cost of the minimum-weight spa...
std::vector< std::pair< Value, Value > > EdgeList
A list of edges (child, parent).
OptimalBranching(RootOrderingGraph graph, Value root)
Constructs the solver for the given graph and root value.
EdgeList preOrderTraversal(ArrayRef< Value > nodes) const
Returns the computed edges as visited in the preorder traversal.
DenseMap< Value, DenseMap< Value, RootOrderingEntry > > RootOrderingGraph
A directed graph representing the cost of ordering the roots in the predicate tree.
Include the generated interface declarations.
llvm::DenseSet< ValueT, ValueInfoT > DenseSet
Definition LLVM.h:128
llvm::DenseMap< KeyT, ValueT, KeyInfoT, BucketT > DenseMap
Definition LLVM.h:126
The information associated with an edge in the cost graph.
std::pair< unsigned, unsigned > cost
The depth of the connector Value w.r.t.