| //===-- HeuristicSolver.h - Heuristic PBQP Solver --------------*- C++ -*-===// | |
| // | |
| // The LLVM Compiler Infrastructure | |
| // | |
| // This file is distributed under the University of Illinois Open Source | |
| // License. See LICENSE.TXT for details. | |
| // | |
| //===----------------------------------------------------------------------===// | |
| // | |
| // Heuristic PBQP solver. This solver is able to perform optimal reductions for | |
| // nodes of degree 0, 1 or 2. For nodes of degree >2 a plugable heuristic is | |
| // used to select a node for reduction. | |
| // | |
| //===----------------------------------------------------------------------===// | |
| #ifndef LLVM_CODEGEN_PBQP_HEURISTICSOLVER_H | |
| #define LLVM_CODEGEN_PBQP_HEURISTICSOLVER_H | |
| #include "Graph.h" | |
| #include "Solution.h" | |
| #include <vector> | |
| #include <limits> | |
| namespace PBQP { | |
| /// \brief Heuristic PBQP solver implementation. | |
| /// | |
| /// This class should usually be created (and destroyed) indirectly via a call | |
| /// to HeuristicSolver<HImpl>::solve(Graph&). | |
| /// See the comments for HeuristicSolver. | |
| /// | |
| /// HeuristicSolverImpl provides the R0, R1 and R2 reduction rules, | |
| /// backpropagation phase, and maintains the internal copy of the graph on | |
| /// which the reduction is carried out (the original being kept to facilitate | |
| /// backpropagation). | |
| template <typename HImpl> | |
| class HeuristicSolverImpl { | |
| private: | |
| typedef typename HImpl::NodeData HeuristicNodeData; | |
| typedef typename HImpl::EdgeData HeuristicEdgeData; | |
| typedef std::list<Graph::EdgeItr> SolverEdges; | |
| public: | |
| /// \brief Iterator type for edges in the solver graph. | |
| typedef SolverEdges::iterator SolverEdgeItr; | |
| private: | |
| class NodeData { | |
| public: | |
| NodeData() : solverDegree(0) {} | |
| HeuristicNodeData& getHeuristicData() { return hData; } | |
| SolverEdgeItr addSolverEdge(Graph::EdgeItr eItr) { | |
| ++solverDegree; | |
| return solverEdges.insert(solverEdges.end(), eItr); | |
| } | |
| void removeSolverEdge(SolverEdgeItr seItr) { | |
| --solverDegree; | |
| solverEdges.erase(seItr); | |
| } | |
| SolverEdgeItr solverEdgesBegin() { return solverEdges.begin(); } | |
| SolverEdgeItr solverEdgesEnd() { return solverEdges.end(); } | |
| unsigned getSolverDegree() const { return solverDegree; } | |
| void clearSolverEdges() { | |
| solverDegree = 0; | |
| solverEdges.clear(); | |
| } | |
| private: | |
| HeuristicNodeData hData; | |
| unsigned solverDegree; | |
| SolverEdges solverEdges; | |
| }; | |
| class EdgeData { | |
| public: | |
| HeuristicEdgeData& getHeuristicData() { return hData; } | |
| void setN1SolverEdgeItr(SolverEdgeItr n1SolverEdgeItr) { | |
| this->n1SolverEdgeItr = n1SolverEdgeItr; | |
| } | |
| SolverEdgeItr getN1SolverEdgeItr() { return n1SolverEdgeItr; } | |
| void setN2SolverEdgeItr(SolverEdgeItr n2SolverEdgeItr){ | |
| this->n2SolverEdgeItr = n2SolverEdgeItr; | |
| } | |
| SolverEdgeItr getN2SolverEdgeItr() { return n2SolverEdgeItr; } | |
| private: | |
| HeuristicEdgeData hData; | |
| SolverEdgeItr n1SolverEdgeItr, n2SolverEdgeItr; | |
| }; | |
| Graph &g; | |
| HImpl h; | |
| Solution s; | |
| std::vector<Graph::NodeItr> stack; | |
| typedef std::list<NodeData> NodeDataList; | |
| NodeDataList nodeDataList; | |
| typedef std::list<EdgeData> EdgeDataList; | |
| EdgeDataList edgeDataList; | |
| public: | |
| /// \brief Construct a heuristic solver implementation to solve the given | |
| /// graph. | |
| /// @param g The graph representing the problem instance to be solved. | |
| HeuristicSolverImpl(Graph &g) : g(g), h(*this) {} | |
| /// \brief Get the graph being solved by this solver. | |
| /// @return The graph representing the problem instance being solved by this | |
| /// solver. | |
| Graph& getGraph() { return g; } | |
| /// \brief Get the heuristic data attached to the given node. | |
| /// @param nItr Node iterator. | |
| /// @return The heuristic data attached to the given node. | |
| HeuristicNodeData& getHeuristicNodeData(Graph::NodeItr nItr) { | |
| return getSolverNodeData(nItr).getHeuristicData(); | |
| } | |
| /// \brief Get the heuristic data attached to the given edge. | |
| /// @param eItr Edge iterator. | |
| /// @return The heuristic data attached to the given node. | |
| HeuristicEdgeData& getHeuristicEdgeData(Graph::EdgeItr eItr) { | |
| return getSolverEdgeData(eItr).getHeuristicData(); | |
| } | |
| /// \brief Begin iterator for the set of edges adjacent to the given node in | |
| /// the solver graph. | |
| /// @param nItr Node iterator. | |
| /// @return Begin iterator for the set of edges adjacent to the given node | |
| /// in the solver graph. | |
| SolverEdgeItr solverEdgesBegin(Graph::NodeItr nItr) { | |
| return getSolverNodeData(nItr).solverEdgesBegin(); | |
| } | |
| /// \brief End iterator for the set of edges adjacent to the given node in | |
| /// the solver graph. | |
| /// @param nItr Node iterator. | |
| /// @return End iterator for the set of edges adjacent to the given node in | |
| /// the solver graph. | |
| SolverEdgeItr solverEdgesEnd(Graph::NodeItr nItr) { | |
| return getSolverNodeData(nItr).solverEdgesEnd(); | |
| } | |
| /// \brief Remove a node from the solver graph. | |
| /// @param eItr Edge iterator for edge to be removed. | |
| /// | |
| /// Does <i>not</i> notify the heuristic of the removal. That should be | |
| /// done manually if necessary. | |
| void removeSolverEdge(Graph::EdgeItr eItr) { | |
| EdgeData &eData = getSolverEdgeData(eItr); | |
| NodeData &n1Data = getSolverNodeData(g.getEdgeNode1(eItr)), | |
| &n2Data = getSolverNodeData(g.getEdgeNode2(eItr)); | |
| n1Data.removeSolverEdge(eData.getN1SolverEdgeItr()); | |
| n2Data.removeSolverEdge(eData.getN2SolverEdgeItr()); | |
| } | |
| /// \brief Compute a solution to the PBQP problem instance with which this | |
| /// heuristic solver was constructed. | |
| /// @return A solution to the PBQP problem. | |
| /// | |
| /// Performs the full PBQP heuristic solver algorithm, including setup, | |
| /// calls to the heuristic (which will call back to the reduction rules in | |
| /// this class), and cleanup. | |
| Solution computeSolution() { | |
| setup(); | |
| h.setup(); | |
| h.reduce(); | |
| backpropagate(); | |
| h.cleanup(); | |
| cleanup(); | |
| return s; | |
| } | |
| /// \brief Add to the end of the stack. | |
| /// @param nItr Node iterator to add to the reduction stack. | |
| void pushToStack(Graph::NodeItr nItr) { | |
| getSolverNodeData(nItr).clearSolverEdges(); | |
| stack.push_back(nItr); | |
| } | |
| /// \brief Returns the solver degree of the given node. | |
| /// @param nItr Node iterator for which degree is requested. | |
| /// @return Node degree in the <i>solver</i> graph (not the original graph). | |
| unsigned getSolverDegree(Graph::NodeItr nItr) { | |
| return getSolverNodeData(nItr).getSolverDegree(); | |
| } | |
| /// \brief Set the solution of the given node. | |
| /// @param nItr Node iterator to set solution for. | |
| /// @param selection Selection for node. | |
| void setSolution(const Graph::NodeItr &nItr, unsigned selection) { | |
| s.setSelection(nItr, selection); | |
| for (Graph::AdjEdgeItr aeItr = g.adjEdgesBegin(nItr), | |
| aeEnd = g.adjEdgesEnd(nItr); | |
| aeItr != aeEnd; ++aeItr) { | |
| Graph::EdgeItr eItr(*aeItr); | |
| Graph::NodeItr anItr(g.getEdgeOtherNode(eItr, nItr)); | |
| getSolverNodeData(anItr).addSolverEdge(eItr); | |
| } | |
| } | |
| /// \brief Apply rule R0. | |
| /// @param nItr Node iterator for node to apply R0 to. | |
| /// | |
| /// Node will be automatically pushed to the solver stack. | |
| void applyR0(Graph::NodeItr nItr) { | |
| assert(getSolverNodeData(nItr).getSolverDegree() == 0 && | |
| "R0 applied to node with degree != 0."); | |
| // Nothing to do. Just push the node onto the reduction stack. | |
| pushToStack(nItr); | |
| s.recordR0(); | |
| } | |
| /// \brief Apply rule R1. | |
| /// @param xnItr Node iterator for node to apply R1 to. | |
| /// | |
| /// Node will be automatically pushed to the solver stack. | |
| void applyR1(Graph::NodeItr xnItr) { | |
| NodeData &nd = getSolverNodeData(xnItr); | |
| assert(nd.getSolverDegree() == 1 && | |
| "R1 applied to node with degree != 1."); | |
| Graph::EdgeItr eItr = *nd.solverEdgesBegin(); | |
| const Matrix &eCosts = g.getEdgeCosts(eItr); | |
| const Vector &xCosts = g.getNodeCosts(xnItr); | |
| // Duplicate a little to avoid transposing matrices. | |
| if (xnItr == g.getEdgeNode1(eItr)) { | |
| Graph::NodeItr ynItr = g.getEdgeNode2(eItr); | |
| Vector &yCosts = g.getNodeCosts(ynItr); | |
| for (unsigned j = 0; j < yCosts.getLength(); ++j) { | |
| PBQPNum min = eCosts[0][j] + xCosts[0]; | |
| for (unsigned i = 1; i < xCosts.getLength(); ++i) { | |
| PBQPNum c = eCosts[i][j] + xCosts[i]; | |
| if (c < min) | |
| min = c; | |
| } | |
| yCosts[j] += min; | |
| } | |
| h.handleRemoveEdge(eItr, ynItr); | |
| } else { | |
| Graph::NodeItr ynItr = g.getEdgeNode1(eItr); | |
| Vector &yCosts = g.getNodeCosts(ynItr); | |
| for (unsigned i = 0; i < yCosts.getLength(); ++i) { | |
| PBQPNum min = eCosts[i][0] + xCosts[0]; | |
| for (unsigned j = 1; j < xCosts.getLength(); ++j) { | |
| PBQPNum c = eCosts[i][j] + xCosts[j]; | |
| if (c < min) | |
| min = c; | |
| } | |
| yCosts[i] += min; | |
| } | |
| h.handleRemoveEdge(eItr, ynItr); | |
| } | |
| removeSolverEdge(eItr); | |
| assert(nd.getSolverDegree() == 0 && | |
| "Degree 1 with edge removed should be 0."); | |
| pushToStack(xnItr); | |
| s.recordR1(); | |
| } | |
| /// \brief Apply rule R2. | |
| /// @param xnItr Node iterator for node to apply R2 to. | |
| /// | |
| /// Node will be automatically pushed to the solver stack. | |
| void applyR2(Graph::NodeItr xnItr) { | |
| assert(getSolverNodeData(xnItr).getSolverDegree() == 2 && | |
| "R2 applied to node with degree != 2."); | |
| NodeData &nd = getSolverNodeData(xnItr); | |
| const Vector &xCosts = g.getNodeCosts(xnItr); | |
| SolverEdgeItr aeItr = nd.solverEdgesBegin(); | |
| Graph::EdgeItr yxeItr = *aeItr, | |
| zxeItr = *(++aeItr); | |
| Graph::NodeItr ynItr = g.getEdgeOtherNode(yxeItr, xnItr), | |
| znItr = g.getEdgeOtherNode(zxeItr, xnItr); | |
| bool flipEdge1 = (g.getEdgeNode1(yxeItr) == xnItr), | |
| flipEdge2 = (g.getEdgeNode1(zxeItr) == xnItr); | |
| const Matrix *yxeCosts = flipEdge1 ? | |
| new Matrix(g.getEdgeCosts(yxeItr).transpose()) : | |
| &g.getEdgeCosts(yxeItr); | |
| const Matrix *zxeCosts = flipEdge2 ? | |
| new Matrix(g.getEdgeCosts(zxeItr).transpose()) : | |
| &g.getEdgeCosts(zxeItr); | |
| unsigned xLen = xCosts.getLength(), | |
| yLen = yxeCosts->getRows(), | |
| zLen = zxeCosts->getRows(); | |
| Matrix delta(yLen, zLen); | |
| for (unsigned i = 0; i < yLen; ++i) { | |
| for (unsigned j = 0; j < zLen; ++j) { | |
| PBQPNum min = (*yxeCosts)[i][0] + (*zxeCosts)[j][0] + xCosts[0]; | |
| for (unsigned k = 1; k < xLen; ++k) { | |
| PBQPNum c = (*yxeCosts)[i][k] + (*zxeCosts)[j][k] + xCosts[k]; | |
| if (c < min) { | |
| min = c; | |
| } | |
| } | |
| delta[i][j] = min; | |
| } | |
| } | |
| if (flipEdge1) | |
| delete yxeCosts; | |
| if (flipEdge2) | |
| delete zxeCosts; | |
| Graph::EdgeItr yzeItr = g.findEdge(ynItr, znItr); | |
| bool addedEdge = false; | |
| if (yzeItr == g.edgesEnd()) { | |
| yzeItr = g.addEdge(ynItr, znItr, delta); | |
| addedEdge = true; | |
| } else { | |
| Matrix &yzeCosts = g.getEdgeCosts(yzeItr); | |
| h.preUpdateEdgeCosts(yzeItr); | |
| if (ynItr == g.getEdgeNode1(yzeItr)) { | |
| yzeCosts += delta; | |
| } else { | |
| yzeCosts += delta.transpose(); | |
| } | |
| } | |
| bool nullCostEdge = tryNormaliseEdgeMatrix(yzeItr); | |
| if (!addedEdge) { | |
| // If we modified the edge costs let the heuristic know. | |
| h.postUpdateEdgeCosts(yzeItr); | |
| } | |
| if (nullCostEdge) { | |
| // If this edge ended up null remove it. | |
| if (!addedEdge) { | |
| // We didn't just add it, so we need to notify the heuristic | |
| // and remove it from the solver. | |
| h.handleRemoveEdge(yzeItr, ynItr); | |
| h.handleRemoveEdge(yzeItr, znItr); | |
| removeSolverEdge(yzeItr); | |
| } | |
| g.removeEdge(yzeItr); | |
| } else if (addedEdge) { | |
| // If the edge was added, and non-null, finish setting it up, add it to | |
| // the solver & notify heuristic. | |
| edgeDataList.push_back(EdgeData()); | |
| g.setEdgeData(yzeItr, &edgeDataList.back()); | |
| addSolverEdge(yzeItr); | |
| h.handleAddEdge(yzeItr); | |
| } | |
| h.handleRemoveEdge(yxeItr, ynItr); | |
| removeSolverEdge(yxeItr); | |
| h.handleRemoveEdge(zxeItr, znItr); | |
| removeSolverEdge(zxeItr); | |
| pushToStack(xnItr); | |
| s.recordR2(); | |
| } | |
| /// \brief Record an application of the RN rule. | |
| /// | |
| /// For use by the HeuristicBase. | |
| void recordRN() { s.recordRN(); } | |
| private: | |
| NodeData& getSolverNodeData(Graph::NodeItr nItr) { | |
| return *static_cast<NodeData*>(g.getNodeData(nItr)); | |
| } | |
| EdgeData& getSolverEdgeData(Graph::EdgeItr eItr) { | |
| return *static_cast<EdgeData*>(g.getEdgeData(eItr)); | |
| } | |
| void addSolverEdge(Graph::EdgeItr eItr) { | |
| EdgeData &eData = getSolverEdgeData(eItr); | |
| NodeData &n1Data = getSolverNodeData(g.getEdgeNode1(eItr)), | |
| &n2Data = getSolverNodeData(g.getEdgeNode2(eItr)); | |
| eData.setN1SolverEdgeItr(n1Data.addSolverEdge(eItr)); | |
| eData.setN2SolverEdgeItr(n2Data.addSolverEdge(eItr)); | |
| } | |
| void setup() { | |
| if (h.solverRunSimplify()) { | |
| simplify(); | |
| } | |
| // Create node data objects. | |
| for (Graph::NodeItr nItr = g.nodesBegin(), nEnd = g.nodesEnd(); | |
| nItr != nEnd; ++nItr) { | |
| nodeDataList.push_back(NodeData()); | |
| g.setNodeData(nItr, &nodeDataList.back()); | |
| } | |
| // Create edge data objects. | |
| for (Graph::EdgeItr eItr = g.edgesBegin(), eEnd = g.edgesEnd(); | |
| eItr != eEnd; ++eItr) { | |
| edgeDataList.push_back(EdgeData()); | |
| g.setEdgeData(eItr, &edgeDataList.back()); | |
| addSolverEdge(eItr); | |
| } | |
| } | |
| void simplify() { | |
| disconnectTrivialNodes(); | |
| eliminateIndependentEdges(); | |
| } | |
| // Eliminate trivial nodes. | |
| void disconnectTrivialNodes() { | |
| unsigned numDisconnected = 0; | |
| for (Graph::NodeItr nItr = g.nodesBegin(), nEnd = g.nodesEnd(); | |
| nItr != nEnd; ++nItr) { | |
| if (g.getNodeCosts(nItr).getLength() == 1) { | |
| std::vector<Graph::EdgeItr> edgesToRemove; | |
| for (Graph::AdjEdgeItr aeItr = g.adjEdgesBegin(nItr), | |
| aeEnd = g.adjEdgesEnd(nItr); | |
| aeItr != aeEnd; ++aeItr) { | |
| Graph::EdgeItr eItr = *aeItr; | |
| if (g.getEdgeNode1(eItr) == nItr) { | |
| Graph::NodeItr otherNodeItr = g.getEdgeNode2(eItr); | |
| g.getNodeCosts(otherNodeItr) += | |
| g.getEdgeCosts(eItr).getRowAsVector(0); | |
| } | |
| else { | |
| Graph::NodeItr otherNodeItr = g.getEdgeNode1(eItr); | |
| g.getNodeCosts(otherNodeItr) += | |
| g.getEdgeCosts(eItr).getColAsVector(0); | |
| } | |
| edgesToRemove.push_back(eItr); | |
| } | |
| if (!edgesToRemove.empty()) | |
| ++numDisconnected; | |
| while (!edgesToRemove.empty()) { | |
| g.removeEdge(edgesToRemove.back()); | |
| edgesToRemove.pop_back(); | |
| } | |
| } | |
| } | |
| } | |
| void eliminateIndependentEdges() { | |
| std::vector<Graph::EdgeItr> edgesToProcess; | |
| unsigned numEliminated = 0; | |
| for (Graph::EdgeItr eItr = g.edgesBegin(), eEnd = g.edgesEnd(); | |
| eItr != eEnd; ++eItr) { | |
| edgesToProcess.push_back(eItr); | |
| } | |
| while (!edgesToProcess.empty()) { | |
| if (tryToEliminateEdge(edgesToProcess.back())) | |
| ++numEliminated; | |
| edgesToProcess.pop_back(); | |
| } | |
| } | |
| bool tryToEliminateEdge(Graph::EdgeItr eItr) { | |
| if (tryNormaliseEdgeMatrix(eItr)) { | |
| g.removeEdge(eItr); | |
| return true; | |
| } | |
| return false; | |
| } | |
| bool tryNormaliseEdgeMatrix(Graph::EdgeItr &eItr) { | |
| const PBQPNum infinity = std::numeric_limits<PBQPNum>::infinity(); | |
| Matrix &edgeCosts = g.getEdgeCosts(eItr); | |
| Vector &uCosts = g.getNodeCosts(g.getEdgeNode1(eItr)), | |
| &vCosts = g.getNodeCosts(g.getEdgeNode2(eItr)); | |
| for (unsigned r = 0; r < edgeCosts.getRows(); ++r) { | |
| PBQPNum rowMin = infinity; | |
| for (unsigned c = 0; c < edgeCosts.getCols(); ++c) { | |
| if (vCosts[c] != infinity && edgeCosts[r][c] < rowMin) | |
| rowMin = edgeCosts[r][c]; | |
| } | |
| uCosts[r] += rowMin; | |
| if (rowMin != infinity) { | |
| edgeCosts.subFromRow(r, rowMin); | |
| } | |
| else { | |
| edgeCosts.setRow(r, 0); | |
| } | |
| } | |
| for (unsigned c = 0; c < edgeCosts.getCols(); ++c) { | |
| PBQPNum colMin = infinity; | |
| for (unsigned r = 0; r < edgeCosts.getRows(); ++r) { | |
| if (uCosts[r] != infinity && edgeCosts[r][c] < colMin) | |
| colMin = edgeCosts[r][c]; | |
| } | |
| vCosts[c] += colMin; | |
| if (colMin != infinity) { | |
| edgeCosts.subFromCol(c, colMin); | |
| } | |
| else { | |
| edgeCosts.setCol(c, 0); | |
| } | |
| } | |
| return edgeCosts.isZero(); | |
| } | |
| void backpropagate() { | |
| while (!stack.empty()) { | |
| computeSolution(stack.back()); | |
| stack.pop_back(); | |
| } | |
| } | |
| void computeSolution(Graph::NodeItr nItr) { | |
| NodeData &nodeData = getSolverNodeData(nItr); | |
| Vector v(g.getNodeCosts(nItr)); | |
| // Solve based on existing solved edges. | |
| for (SolverEdgeItr solvedEdgeItr = nodeData.solverEdgesBegin(), | |
| solvedEdgeEnd = nodeData.solverEdgesEnd(); | |
| solvedEdgeItr != solvedEdgeEnd; ++solvedEdgeItr) { | |
| Graph::EdgeItr eItr(*solvedEdgeItr); | |
| Matrix &edgeCosts = g.getEdgeCosts(eItr); | |
| if (nItr == g.getEdgeNode1(eItr)) { | |
| Graph::NodeItr adjNode(g.getEdgeNode2(eItr)); | |
| unsigned adjSolution = s.getSelection(adjNode); | |
| v += edgeCosts.getColAsVector(adjSolution); | |
| } | |
| else { | |
| Graph::NodeItr adjNode(g.getEdgeNode1(eItr)); | |
| unsigned adjSolution = s.getSelection(adjNode); | |
| v += edgeCosts.getRowAsVector(adjSolution); | |
| } | |
| } | |
| setSolution(nItr, v.minIndex()); | |
| } | |
| void cleanup() { | |
| h.cleanup(); | |
| nodeDataList.clear(); | |
| edgeDataList.clear(); | |
| } | |
| }; | |
| /// \brief PBQP heuristic solver class. | |
| /// | |
| /// Given a PBQP Graph g representing a PBQP problem, you can find a solution | |
| /// by calling | |
| /// <tt>Solution s = HeuristicSolver<H>::solve(g);</tt> | |
| /// | |
| /// The choice of heuristic for the H parameter will affect both the solver | |
| /// speed and solution quality. The heuristic should be chosen based on the | |
| /// nature of the problem being solved. | |
| /// Currently the only solver included with LLVM is the Briggs heuristic for | |
| /// register allocation. | |
| template <typename HImpl> | |
| class HeuristicSolver { | |
| public: | |
| static Solution solve(Graph &g) { | |
| HeuristicSolverImpl<HImpl> hs(g); | |
| return hs.computeSolution(); | |
| } | |
| }; | |
| } | |
| #endif // LLVM_CODEGEN_PBQP_HEURISTICSOLVER_H |