xref: /llvm-project/mlir/lib/Conversion/PDLToPDLInterp/RootOrdering.h (revision 9eb8e7b176e9fc38c8df86bd927663c6409ac262)
16df7cc7fSStanislav Funiak //===- RootOrdering.h - Optimal root ordering  ------------------*- C++ -*-===//
26df7cc7fSStanislav Funiak //
36df7cc7fSStanislav Funiak // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
46df7cc7fSStanislav Funiak // See https://llvm.org/LICENSE.txt for license information.
56df7cc7fSStanislav Funiak // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
66df7cc7fSStanislav Funiak //
76df7cc7fSStanislav Funiak //===----------------------------------------------------------------------===//
86df7cc7fSStanislav Funiak //
96df7cc7fSStanislav Funiak // This file contains definition for a cost graph over candidate roots and
106df7cc7fSStanislav Funiak // an implementation of an algorithm to determine the optimal ordering over
116df7cc7fSStanislav Funiak // these roots. Each edge in this graph indicates that the target root can be
126df7cc7fSStanislav Funiak // connected (via a chain of positions) to the source root, and their cost
136df7cc7fSStanislav Funiak // indicates the estimated cost of such traversal. The optimal root ordering
146df7cc7fSStanislav Funiak // is then formulated as that of finding a spanning arborescence (i.e., a
156df7cc7fSStanislav Funiak // directed spanning tree) of minimal weight.
166df7cc7fSStanislav Funiak //
176df7cc7fSStanislav Funiak //===----------------------------------------------------------------------===//
186df7cc7fSStanislav Funiak 
196df7cc7fSStanislav Funiak #ifndef MLIR_LIB_CONVERSION_PDLTOPDLINTERP_ROOTORDERING_H_
206df7cc7fSStanislav Funiak #define MLIR_LIB_CONVERSION_PDLTOPDLINTERP_ROOTORDERING_H_
216df7cc7fSStanislav Funiak 
226df7cc7fSStanislav Funiak #include "mlir/IR/Value.h"
236df7cc7fSStanislav Funiak #include "llvm/ADT/DenseMap.h"
246df7cc7fSStanislav Funiak #include "llvm/ADT/SmallVector.h"
256df7cc7fSStanislav Funiak #include <functional>
266df7cc7fSStanislav Funiak #include <vector>
276df7cc7fSStanislav Funiak 
286df7cc7fSStanislav Funiak namespace mlir {
296df7cc7fSStanislav Funiak namespace pdl_to_pdl_interp {
306df7cc7fSStanislav Funiak 
316df7cc7fSStanislav Funiak /// The information associated with an edge in the cost graph. Each node in
326df7cc7fSStanislav Funiak /// the cost graph corresponds to a candidate root detected in the pdl.pattern,
336df7cc7fSStanislav Funiak /// and each edge in the cost graph corresponds to connecting the two candidate
346df7cc7fSStanislav Funiak /// roots via a chain of operations. The cost of an edge is the smallest number
356df7cc7fSStanislav Funiak /// of upward traversals required to go from the source to the target root, and
366df7cc7fSStanislav Funiak /// the connector is a `Value` in the intersection of the two subtrees rooted at
376df7cc7fSStanislav Funiak /// the source and target root that results in that smallest number of upward
386df7cc7fSStanislav Funiak /// traversals. Consider the following pattern with 3 roots op3, op4, and op5:
396df7cc7fSStanislav Funiak ///
406df7cc7fSStanislav Funiak ///                 argA ---> op1 ---> op2 ---> op3 ---> res3
416df7cc7fSStanislav Funiak ///                            ^        ^
426df7cc7fSStanislav Funiak ///                            |        |
436df7cc7fSStanislav Funiak ///                           argB     argC
446df7cc7fSStanislav Funiak ///                            |        |
456df7cc7fSStanislav Funiak ///                            v        v
466df7cc7fSStanislav Funiak ///                 res4 <--- op4      op5 ---> res5
476df7cc7fSStanislav Funiak ///                            ^        ^
486df7cc7fSStanislav Funiak ///                            |        |
496df7cc7fSStanislav Funiak ///                           op6      op7
506df7cc7fSStanislav Funiak ///
516df7cc7fSStanislav Funiak /// The cost of the edge op3 -> op4 is 1 (the upward traversal argB -> op4),
526df7cc7fSStanislav Funiak /// with argB being the connector `Value` and similarly for op3 -> op5 (cost 1,
536df7cc7fSStanislav Funiak /// connector argC). The cost of the edge op4 -> op3 is 3 (upward traversals
546df7cc7fSStanislav Funiak /// argB -> op1 -> op2 -> op3, connector argB), while the cost of edge op5 ->
556df7cc7fSStanislav Funiak /// op3 is 2 (uwpard traversals argC -> op2 -> op3). There are no edges between
566df7cc7fSStanislav Funiak /// op4 and op5 in the cost graph, because the subtrees rooted at these two
576df7cc7fSStanislav Funiak /// roots do not intersect. It is easy to see that the optimal root for this
586df7cc7fSStanislav Funiak /// pattern is op3, resulting in the spanning arborescence op3 -> {op4, op5}.
59*9eb8e7b1SStanislav Funiak struct RootOrderingEntry {
606df7cc7fSStanislav Funiak   /// The depth of the connector `Value` w.r.t. the target root.
616df7cc7fSStanislav Funiak   ///
62*9eb8e7b1SStanislav Funiak   /// This is a pair where the first value is the additive cost (the depth of
63*9eb8e7b1SStanislav Funiak   /// the connector), and the second value is a priority for breaking ties
64*9eb8e7b1SStanislav Funiak   /// (with 0 being the highest). Typically, the priority is a unique edge ID.
656df7cc7fSStanislav Funiak   std::pair<unsigned, unsigned> cost;
666df7cc7fSStanislav Funiak 
676df7cc7fSStanislav Funiak   /// The connector value in the intersection of the two subtrees rooted at
686df7cc7fSStanislav Funiak   /// the source and target root that results in that smallest depth w.r.t.
696df7cc7fSStanislav Funiak   /// the target root.
706df7cc7fSStanislav Funiak   Value connector;
716df7cc7fSStanislav Funiak };
726df7cc7fSStanislav Funiak 
736df7cc7fSStanislav Funiak /// A directed graph representing the cost of ordering the roots in the
746df7cc7fSStanislav Funiak /// predicate tree. It is represented as an adjacency map, where the outer map
756df7cc7fSStanislav Funiak /// is indexed by the target node, and the inner map is indexed by the source
766df7cc7fSStanislav Funiak /// node. Each edge is associated with a cost and the underlying connector
776df7cc7fSStanislav Funiak /// value.
78*9eb8e7b1SStanislav Funiak using RootOrderingGraph = DenseMap<Value, DenseMap<Value, RootOrderingEntry>>;
796df7cc7fSStanislav Funiak 
806df7cc7fSStanislav Funiak /// The optimal branching algorithm solver. This solver accepts a graph and the
816df7cc7fSStanislav Funiak /// root in its constructor, and is invoked via the solve() member function.
826df7cc7fSStanislav Funiak /// This is a direct implementation of the Edmonds' algorithm, see
836df7cc7fSStanislav Funiak /// https://en.wikipedia.org/wiki/Edmonds%27_algorithm. The worst-case
846df7cc7fSStanislav Funiak /// computational complexity of this algorithm is O(N^3), for a single root.
856df7cc7fSStanislav Funiak /// The PDL-to-PDLInterp lowering calls this N times (once for each candidate
866df7cc7fSStanislav Funiak /// root), so the overall complexity root ordering is O(N^4). If needed, this
876df7cc7fSStanislav Funiak /// could be reduced to O(N^3) with a more efficient algorithm. However, note
886df7cc7fSStanislav Funiak /// that the underlying implementation is very efficient, and N in our
896df7cc7fSStanislav Funiak /// instances tends to be very small (<10).
906df7cc7fSStanislav Funiak class OptimalBranching {
916df7cc7fSStanislav Funiak public:
926df7cc7fSStanislav Funiak   /// A list of edges (child, parent).
936df7cc7fSStanislav Funiak   using EdgeList = std::vector<std::pair<Value, Value>>;
946df7cc7fSStanislav Funiak 
956df7cc7fSStanislav Funiak   /// Constructs the solver for the given graph and root value.
966df7cc7fSStanislav Funiak   OptimalBranching(RootOrderingGraph graph, Value root);
976df7cc7fSStanislav Funiak 
986df7cc7fSStanislav Funiak   /// Runs the Edmonds' algorithm for the current `graph`, returning the total
996df7cc7fSStanislav Funiak   /// cost of the minimum-weight spanning arborescence (sum of the edge costs).
1006df7cc7fSStanislav Funiak   /// This function first determines the optimal local choice of the parents
1016df7cc7fSStanislav Funiak   /// and stores this choice in the `parents` mapping. If this choice results
1026df7cc7fSStanislav Funiak   /// in an acyclic graph, the function returns immediately. Otherwise, it
1036df7cc7fSStanislav Funiak   /// takes an arbitrary cycle, contracts it, and recurses on the new graph
1046df7cc7fSStanislav Funiak   /// (which is guaranteed to have fewer nodes than we began with). After we
1056df7cc7fSStanislav Funiak   /// return from recursion, we redirect the edges to/from the contracted node,
1066df7cc7fSStanislav Funiak   /// so the `parents` map contains a valid solution for the current graph.
1076df7cc7fSStanislav Funiak   unsigned solve();
1086df7cc7fSStanislav Funiak 
1096df7cc7fSStanislav Funiak   /// Returns the computed parent map. This is the unique predecessor for each
1106df7cc7fSStanislav Funiak   /// node (root) in the optimal branching.
getRootOrderingParents()1116df7cc7fSStanislav Funiak   const DenseMap<Value, Value> &getRootOrderingParents() const {
1126df7cc7fSStanislav Funiak     return parents;
1136df7cc7fSStanislav Funiak   }
1146df7cc7fSStanislav Funiak 
1156df7cc7fSStanislav Funiak   /// Returns the computed edges as visited in the preorder traversal.
1166df7cc7fSStanislav Funiak   /// The specified array determines the order for breaking any ties.
1176df7cc7fSStanislav Funiak   EdgeList preOrderTraversal(ArrayRef<Value> nodes) const;
1186df7cc7fSStanislav Funiak 
1196df7cc7fSStanislav Funiak private:
1206df7cc7fSStanislav Funiak   /// The graph whose optimal branching we wish to determine.
1216df7cc7fSStanislav Funiak   RootOrderingGraph graph;
1226df7cc7fSStanislav Funiak 
1236df7cc7fSStanislav Funiak   /// The root of the optimal branching.
1246df7cc7fSStanislav Funiak   Value root;
1256df7cc7fSStanislav Funiak 
1266df7cc7fSStanislav Funiak   /// The computed parent mapping. This is the unique predecessor for each node
1276df7cc7fSStanislav Funiak   /// in the optimal branching. The keys of this map correspond to the keys of
1286df7cc7fSStanislav Funiak   /// the outer map of the input graph, and each value is one of the keys of
1296df7cc7fSStanislav Funiak   /// the inner map for this node. Also used as an intermediate (possibly
1306df7cc7fSStanislav Funiak   /// cyclical) result in the optimal branching algorithm.
1316df7cc7fSStanislav Funiak   DenseMap<Value, Value> parents;
1326df7cc7fSStanislav Funiak };
1336df7cc7fSStanislav Funiak 
134be0a7e9fSMehdi Amini } // namespace pdl_to_pdl_interp
135be0a7e9fSMehdi Amini } // namespace mlir
1366df7cc7fSStanislav Funiak 
1376df7cc7fSStanislav Funiak #endif // MLIR_CONVERSION_PDLTOPDLINTERP_ROOTORDERING_H_
138