LLVM API Documentation

HeuristicSolver.h
Go to the documentation of this file.
00001 //===-- HeuristicSolver.h - Heuristic PBQP Solver --------------*- C++ -*-===//
00002 //
00003 //                     The LLVM Compiler Infrastructure
00004 //
00005 // This file is distributed under the University of Illinois Open Source
00006 // License. See LICENSE.TXT for details.
00007 //
00008 //===----------------------------------------------------------------------===//
00009 //
00010 // Heuristic PBQP solver. This solver is able to perform optimal reductions for
00011 // nodes of degree 0, 1 or 2. For nodes of degree >2 a plugable heuristic is
00012 // used to select a node for reduction. 
00013 //
00014 //===----------------------------------------------------------------------===//
00015 
00016 #ifndef LLVM_CODEGEN_PBQP_HEURISTICSOLVER_H
00017 #define LLVM_CODEGEN_PBQP_HEURISTICSOLVER_H
00018 
00019 #include "Graph.h"
00020 #include "Solution.h"
00021 #include <limits>
00022 #include <vector>
00023 
00024 namespace PBQP {
00025 
00026   /// \brief Heuristic PBQP solver implementation.
00027   ///
00028   /// This class should usually be created (and destroyed) indirectly via a call
00029   /// to HeuristicSolver<HImpl>::solve(Graph&).
00030   /// See the comments for HeuristicSolver.
00031   ///
00032   /// HeuristicSolverImpl provides the R0, R1 and R2 reduction rules,
00033   /// backpropagation phase, and maintains the internal copy of the graph on
00034   /// which the reduction is carried out (the original being kept to facilitate
00035   /// backpropagation).
00036   template <typename HImpl>
00037   class HeuristicSolverImpl {
00038   private:
00039 
00040     typedef typename HImpl::NodeData HeuristicNodeData;
00041     typedef typename HImpl::EdgeData HeuristicEdgeData;
00042 
00043     typedef std::list<Graph::EdgeItr> SolverEdges;
00044 
00045   public:
00046   
00047     /// \brief Iterator type for edges in the solver graph.
00048     typedef SolverEdges::iterator SolverEdgeItr;
00049 
00050   private:
00051 
00052     class NodeData {
00053     public:
00054       NodeData() : solverDegree(0) {}
00055 
00056       HeuristicNodeData& getHeuristicData() { return hData; }
00057 
00058       SolverEdgeItr addSolverEdge(Graph::EdgeItr eItr) {
00059         ++solverDegree;
00060         return solverEdges.insert(solverEdges.end(), eItr);
00061       }
00062 
00063       void removeSolverEdge(SolverEdgeItr seItr) {
00064         --solverDegree;
00065         solverEdges.erase(seItr);
00066       }
00067 
00068       SolverEdgeItr solverEdgesBegin() { return solverEdges.begin(); }
00069       SolverEdgeItr solverEdgesEnd() { return solverEdges.end(); }
00070       unsigned getSolverDegree() const { return solverDegree; }
00071       void clearSolverEdges() {
00072         solverDegree = 0;
00073         solverEdges.clear(); 
00074       }
00075       
00076     private:
00077       HeuristicNodeData hData;
00078       unsigned solverDegree;
00079       SolverEdges solverEdges;
00080     };
00081  
00082     class EdgeData {
00083     public:
00084       HeuristicEdgeData& getHeuristicData() { return hData; }
00085 
00086       void setN1SolverEdgeItr(SolverEdgeItr n1SolverEdgeItr) {
00087         this->n1SolverEdgeItr = n1SolverEdgeItr;
00088       }
00089 
00090       SolverEdgeItr getN1SolverEdgeItr() { return n1SolverEdgeItr; }
00091 
00092       void setN2SolverEdgeItr(SolverEdgeItr n2SolverEdgeItr){
00093         this->n2SolverEdgeItr = n2SolverEdgeItr;
00094       }
00095 
00096       SolverEdgeItr getN2SolverEdgeItr() { return n2SolverEdgeItr; }
00097 
00098     private:
00099 
00100       HeuristicEdgeData hData;
00101       SolverEdgeItr n1SolverEdgeItr, n2SolverEdgeItr;
00102     };
00103 
00104     Graph &g;
00105     HImpl h;
00106     Solution s;
00107     std::vector<Graph::NodeItr> stack;
00108 
00109     typedef std::list<NodeData> NodeDataList;
00110     NodeDataList nodeDataList;
00111 
00112     typedef std::list<EdgeData> EdgeDataList;
00113     EdgeDataList edgeDataList;
00114 
00115   public:
00116 
00117     /// \brief Construct a heuristic solver implementation to solve the given
00118     ///        graph.
00119     /// @param g The graph representing the problem instance to be solved.
00120     HeuristicSolverImpl(Graph &g) : g(g), h(*this) {}  
00121 
00122     /// \brief Get the graph being solved by this solver.
00123     /// @return The graph representing the problem instance being solved by this
00124     ///         solver.
00125     Graph& getGraph() { return g; }
00126 
00127     /// \brief Get the heuristic data attached to the given node.
00128     /// @param nItr Node iterator.
00129     /// @return The heuristic data attached to the given node.
00130     HeuristicNodeData& getHeuristicNodeData(Graph::NodeItr nItr) {
00131       return getSolverNodeData(nItr).getHeuristicData();
00132     }
00133 
00134     /// \brief Get the heuristic data attached to the given edge.
00135     /// @param eItr Edge iterator.
00136     /// @return The heuristic data attached to the given node.
00137     HeuristicEdgeData& getHeuristicEdgeData(Graph::EdgeItr eItr) {
00138       return getSolverEdgeData(eItr).getHeuristicData();
00139     }
00140 
00141     /// \brief Begin iterator for the set of edges adjacent to the given node in
00142     ///        the solver graph.
00143     /// @param nItr Node iterator.
00144     /// @return Begin iterator for the set of edges adjacent to the given node
00145     ///         in the solver graph. 
00146     SolverEdgeItr solverEdgesBegin(Graph::NodeItr nItr) {
00147       return getSolverNodeData(nItr).solverEdgesBegin();
00148     }
00149 
00150     /// \brief End iterator for the set of edges adjacent to the given node in
00151     ///        the solver graph.
00152     /// @param nItr Node iterator.
00153     /// @return End iterator for the set of edges adjacent to the given node in
00154     ///         the solver graph. 
00155     SolverEdgeItr solverEdgesEnd(Graph::NodeItr nItr) {
00156       return getSolverNodeData(nItr).solverEdgesEnd();
00157     }
00158 
00159     /// \brief Remove a node from the solver graph.
00160     /// @param eItr Edge iterator for edge to be removed.
00161     ///
00162     /// Does <i>not</i> notify the heuristic of the removal. That should be
00163     /// done manually if necessary.
00164     void removeSolverEdge(Graph::EdgeItr eItr) {
00165       EdgeData &eData = getSolverEdgeData(eItr);
00166       NodeData &n1Data = getSolverNodeData(g.getEdgeNode1(eItr)),
00167                &n2Data = getSolverNodeData(g.getEdgeNode2(eItr));
00168 
00169       n1Data.removeSolverEdge(eData.getN1SolverEdgeItr());
00170       n2Data.removeSolverEdge(eData.getN2SolverEdgeItr());
00171     }
00172 
00173     /// \brief Compute a solution to the PBQP problem instance with which this
00174     ///        heuristic solver was constructed.
00175     /// @return A solution to the PBQP problem.
00176     ///
00177     /// Performs the full PBQP heuristic solver algorithm, including setup,
00178     /// calls to the heuristic (which will call back to the reduction rules in
00179     /// this class), and cleanup.
00180     Solution computeSolution() {
00181       setup();
00182       h.setup();
00183       h.reduce();
00184       backpropagate();
00185       h.cleanup();
00186       cleanup();
00187       return s;
00188     }
00189 
00190     /// \brief Add to the end of the stack.
00191     /// @param nItr Node iterator to add to the reduction stack.
00192     void pushToStack(Graph::NodeItr nItr) {
00193       getSolverNodeData(nItr).clearSolverEdges();
00194       stack.push_back(nItr);
00195     }
00196 
00197     /// \brief Returns the solver degree of the given node.
00198     /// @param nItr Node iterator for which degree is requested.
00199     /// @return Node degree in the <i>solver</i> graph (not the original graph).
00200     unsigned getSolverDegree(Graph::NodeItr nItr) {
00201       return  getSolverNodeData(nItr).getSolverDegree();
00202     }
00203 
00204     /// \brief Set the solution of the given node.
00205     /// @param nItr Node iterator to set solution for.
00206     /// @param selection Selection for node.
00207     void setSolution(const Graph::NodeItr &nItr, unsigned selection) {
00208       s.setSelection(nItr, selection);
00209 
00210       for (Graph::AdjEdgeItr aeItr = g.adjEdgesBegin(nItr),
00211                              aeEnd = g.adjEdgesEnd(nItr);
00212            aeItr != aeEnd; ++aeItr) {
00213         Graph::EdgeItr eItr(*aeItr);
00214         Graph::NodeItr anItr(g.getEdgeOtherNode(eItr, nItr));
00215         getSolverNodeData(anItr).addSolverEdge(eItr);
00216       }
00217     }
00218 
00219     /// \brief Apply rule R0.
00220     /// @param nItr Node iterator for node to apply R0 to.
00221     ///
00222     /// Node will be automatically pushed to the solver stack.
00223     void applyR0(Graph::NodeItr nItr) {
00224       assert(getSolverNodeData(nItr).getSolverDegree() == 0 &&
00225              "R0 applied to node with degree != 0.");
00226 
00227       // Nothing to do. Just push the node onto the reduction stack.
00228       pushToStack(nItr);
00229 
00230       s.recordR0();
00231     }
00232 
00233     /// \brief Apply rule R1.
00234     /// @param xnItr Node iterator for node to apply R1 to.
00235     ///
00236     /// Node will be automatically pushed to the solver stack.
00237     void applyR1(Graph::NodeItr xnItr) {
00238       NodeData &nd = getSolverNodeData(xnItr);
00239       assert(nd.getSolverDegree() == 1 &&
00240              "R1 applied to node with degree != 1.");
00241 
00242       Graph::EdgeItr eItr = *nd.solverEdgesBegin();
00243 
00244       const Matrix &eCosts = g.getEdgeCosts(eItr);
00245       const Vector &xCosts = g.getNodeCosts(xnItr);
00246       
00247       // Duplicate a little to avoid transposing matrices.
00248       if (xnItr == g.getEdgeNode1(eItr)) {
00249         Graph::NodeItr ynItr = g.getEdgeNode2(eItr);
00250         Vector &yCosts = g.getNodeCosts(ynItr);
00251         for (unsigned j = 0; j < yCosts.getLength(); ++j) {
00252           PBQPNum min = eCosts[0][j] + xCosts[0];
00253           for (unsigned i = 1; i < xCosts.getLength(); ++i) {
00254             PBQPNum c = eCosts[i][j] + xCosts[i];
00255             if (c < min)
00256               min = c;
00257           }
00258           yCosts[j] += min;
00259         }
00260         h.handleRemoveEdge(eItr, ynItr);
00261      } else {
00262         Graph::NodeItr ynItr = g.getEdgeNode1(eItr);
00263         Vector &yCosts = g.getNodeCosts(ynItr);
00264         for (unsigned i = 0; i < yCosts.getLength(); ++i) {
00265           PBQPNum min = eCosts[i][0] + xCosts[0];
00266           for (unsigned j = 1; j < xCosts.getLength(); ++j) {
00267             PBQPNum c = eCosts[i][j] + xCosts[j];
00268             if (c < min)
00269               min = c;
00270           }
00271           yCosts[i] += min;
00272         }
00273         h.handleRemoveEdge(eItr, ynItr);
00274       }
00275       removeSolverEdge(eItr);
00276       assert(nd.getSolverDegree() == 0 &&
00277              "Degree 1 with edge removed should be 0.");
00278       pushToStack(xnItr);
00279       s.recordR1();
00280     }
00281 
00282     /// \brief Apply rule R2.
00283     /// @param xnItr Node iterator for node to apply R2 to.
00284     ///
00285     /// Node will be automatically pushed to the solver stack.
00286     void applyR2(Graph::NodeItr xnItr) {
00287       assert(getSolverNodeData(xnItr).getSolverDegree() == 2 &&
00288              "R2 applied to node with degree != 2.");
00289 
00290       NodeData &nd = getSolverNodeData(xnItr);
00291       const Vector &xCosts = g.getNodeCosts(xnItr);
00292 
00293       SolverEdgeItr aeItr = nd.solverEdgesBegin();
00294       Graph::EdgeItr yxeItr = *aeItr,
00295                      zxeItr = *(++aeItr);
00296 
00297       Graph::NodeItr ynItr = g.getEdgeOtherNode(yxeItr, xnItr),
00298                      znItr = g.getEdgeOtherNode(zxeItr, xnItr);
00299 
00300       bool flipEdge1 = (g.getEdgeNode1(yxeItr) == xnItr),
00301            flipEdge2 = (g.getEdgeNode1(zxeItr) == xnItr);
00302 
00303       const Matrix *yxeCosts = flipEdge1 ?
00304         new Matrix(g.getEdgeCosts(yxeItr).transpose()) :
00305         &g.getEdgeCosts(yxeItr);
00306 
00307       const Matrix *zxeCosts = flipEdge2 ?
00308         new Matrix(g.getEdgeCosts(zxeItr).transpose()) :
00309         &g.getEdgeCosts(zxeItr);
00310 
00311       unsigned xLen = xCosts.getLength(),
00312                yLen = yxeCosts->getRows(),
00313                zLen = zxeCosts->getRows();
00314                
00315       Matrix delta(yLen, zLen);
00316 
00317       for (unsigned i = 0; i < yLen; ++i) {
00318         for (unsigned j = 0; j < zLen; ++j) {
00319           PBQPNum min = (*yxeCosts)[i][0] + (*zxeCosts)[j][0] + xCosts[0];
00320           for (unsigned k = 1; k < xLen; ++k) {
00321             PBQPNum c = (*yxeCosts)[i][k] + (*zxeCosts)[j][k] + xCosts[k];
00322             if (c < min) {
00323               min = c;
00324             }
00325           }
00326           delta[i][j] = min;
00327         }
00328       }
00329 
00330       if (flipEdge1)
00331         delete yxeCosts;
00332 
00333       if (flipEdge2)
00334         delete zxeCosts;
00335 
00336       Graph::EdgeItr yzeItr = g.findEdge(ynItr, znItr);
00337       bool addedEdge = false;
00338 
00339       if (yzeItr == g.edgesEnd()) {
00340         yzeItr = g.addEdge(ynItr, znItr, delta);
00341         addedEdge = true;
00342       } else {
00343         Matrix &yzeCosts = g.getEdgeCosts(yzeItr);
00344         h.preUpdateEdgeCosts(yzeItr);
00345         if (ynItr == g.getEdgeNode1(yzeItr)) {
00346           yzeCosts += delta;
00347         } else {
00348           yzeCosts += delta.transpose();
00349         }
00350       }
00351 
00352       bool nullCostEdge = tryNormaliseEdgeMatrix(yzeItr);
00353 
00354       if (!addedEdge) {
00355         // If we modified the edge costs let the heuristic know.
00356         h.postUpdateEdgeCosts(yzeItr);
00357       }
00358  
00359       if (nullCostEdge) {
00360         // If this edge ended up null remove it.
00361         if (!addedEdge) {
00362           // We didn't just add it, so we need to notify the heuristic
00363           // and remove it from the solver.
00364           h.handleRemoveEdge(yzeItr, ynItr);
00365           h.handleRemoveEdge(yzeItr, znItr);
00366           removeSolverEdge(yzeItr);
00367         }
00368         g.removeEdge(yzeItr);
00369       } else if (addedEdge) {
00370         // If the edge was added, and non-null, finish setting it up, add it to
00371         // the solver & notify heuristic.
00372         edgeDataList.push_back(EdgeData());
00373         g.setEdgeData(yzeItr, &edgeDataList.back());
00374         addSolverEdge(yzeItr);
00375         h.handleAddEdge(yzeItr);
00376       }
00377 
00378       h.handleRemoveEdge(yxeItr, ynItr);
00379       removeSolverEdge(yxeItr);
00380       h.handleRemoveEdge(zxeItr, znItr);
00381       removeSolverEdge(zxeItr);
00382 
00383       pushToStack(xnItr);
00384       s.recordR2();
00385     }
00386 
00387     /// \brief Record an application of the RN rule.
00388     ///
00389     /// For use by the HeuristicBase.
00390     void recordRN() { s.recordRN(); } 
00391 
00392   private:
00393 
00394     NodeData& getSolverNodeData(Graph::NodeItr nItr) {
00395       return *static_cast<NodeData*>(g.getNodeData(nItr));
00396     }
00397 
00398     EdgeData& getSolverEdgeData(Graph::EdgeItr eItr) {
00399       return *static_cast<EdgeData*>(g.getEdgeData(eItr));
00400     }
00401 
00402     void addSolverEdge(Graph::EdgeItr eItr) {
00403       EdgeData &eData = getSolverEdgeData(eItr);
00404       NodeData &n1Data = getSolverNodeData(g.getEdgeNode1(eItr)),
00405                &n2Data = getSolverNodeData(g.getEdgeNode2(eItr));
00406 
00407       eData.setN1SolverEdgeItr(n1Data.addSolverEdge(eItr));
00408       eData.setN2SolverEdgeItr(n2Data.addSolverEdge(eItr));
00409     }
00410 
00411     void setup() {
00412       if (h.solverRunSimplify()) {
00413         simplify();
00414       }
00415 
00416       // Create node data objects.
00417       for (Graph::NodeItr nItr = g.nodesBegin(), nEnd = g.nodesEnd();
00418            nItr != nEnd; ++nItr) {
00419         nodeDataList.push_back(NodeData());
00420         g.setNodeData(nItr, &nodeDataList.back());
00421       }
00422 
00423       // Create edge data objects.
00424       for (Graph::EdgeItr eItr = g.edgesBegin(), eEnd = g.edgesEnd();
00425            eItr != eEnd; ++eItr) {
00426         edgeDataList.push_back(EdgeData());
00427         g.setEdgeData(eItr, &edgeDataList.back());
00428         addSolverEdge(eItr);
00429       }
00430     }
00431 
00432     void simplify() {
00433       disconnectTrivialNodes();
00434       eliminateIndependentEdges();
00435     }
00436 
00437     // Eliminate trivial nodes.
00438     void disconnectTrivialNodes() {
00439       unsigned numDisconnected = 0;
00440 
00441       for (Graph::NodeItr nItr = g.nodesBegin(), nEnd = g.nodesEnd();
00442            nItr != nEnd; ++nItr) {
00443 
00444         if (g.getNodeCosts(nItr).getLength() == 1) {
00445 
00446           std::vector<Graph::EdgeItr> edgesToRemove;
00447 
00448           for (Graph::AdjEdgeItr aeItr = g.adjEdgesBegin(nItr),
00449                                  aeEnd = g.adjEdgesEnd(nItr);
00450                aeItr != aeEnd; ++aeItr) {
00451 
00452             Graph::EdgeItr eItr = *aeItr;
00453 
00454             if (g.getEdgeNode1(eItr) == nItr) {
00455               Graph::NodeItr otherNodeItr = g.getEdgeNode2(eItr);
00456               g.getNodeCosts(otherNodeItr) +=
00457                 g.getEdgeCosts(eItr).getRowAsVector(0);
00458             }
00459             else {
00460               Graph::NodeItr otherNodeItr = g.getEdgeNode1(eItr);
00461               g.getNodeCosts(otherNodeItr) +=
00462                 g.getEdgeCosts(eItr).getColAsVector(0);
00463             }
00464 
00465             edgesToRemove.push_back(eItr);
00466           }
00467 
00468           if (!edgesToRemove.empty())
00469             ++numDisconnected;
00470 
00471           while (!edgesToRemove.empty()) {
00472             g.removeEdge(edgesToRemove.back());
00473             edgesToRemove.pop_back();
00474           }
00475         }
00476       }
00477     }
00478 
00479     void eliminateIndependentEdges() {
00480       std::vector<Graph::EdgeItr> edgesToProcess;
00481       unsigned numEliminated = 0;
00482 
00483       for (Graph::EdgeItr eItr = g.edgesBegin(), eEnd = g.edgesEnd();
00484            eItr != eEnd; ++eItr) {
00485         edgesToProcess.push_back(eItr);
00486       }
00487 
00488       while (!edgesToProcess.empty()) {
00489         if (tryToEliminateEdge(edgesToProcess.back()))
00490           ++numEliminated;
00491         edgesToProcess.pop_back();
00492       }
00493     }
00494 
00495     bool tryToEliminateEdge(Graph::EdgeItr eItr) {
00496       if (tryNormaliseEdgeMatrix(eItr)) {
00497         g.removeEdge(eItr);
00498         return true; 
00499       }
00500       return false;
00501     }
00502 
00503     bool tryNormaliseEdgeMatrix(Graph::EdgeItr &eItr) {
00504 
00505       const PBQPNum infinity = std::numeric_limits<PBQPNum>::infinity();
00506 
00507       Matrix &edgeCosts = g.getEdgeCosts(eItr);
00508       Vector &uCosts = g.getNodeCosts(g.getEdgeNode1(eItr)),
00509              &vCosts = g.getNodeCosts(g.getEdgeNode2(eItr));
00510 
00511       for (unsigned r = 0; r < edgeCosts.getRows(); ++r) {
00512         PBQPNum rowMin = infinity;
00513 
00514         for (unsigned c = 0; c < edgeCosts.getCols(); ++c) {
00515           if (vCosts[c] != infinity && edgeCosts[r][c] < rowMin)
00516             rowMin = edgeCosts[r][c];
00517         }
00518 
00519         uCosts[r] += rowMin;
00520 
00521         if (rowMin != infinity) {
00522           edgeCosts.subFromRow(r, rowMin);
00523         }
00524         else {
00525           edgeCosts.setRow(r, 0);
00526         }
00527       }
00528 
00529       for (unsigned c = 0; c < edgeCosts.getCols(); ++c) {
00530         PBQPNum colMin = infinity;
00531 
00532         for (unsigned r = 0; r < edgeCosts.getRows(); ++r) {
00533           if (uCosts[r] != infinity && edgeCosts[r][c] < colMin)
00534             colMin = edgeCosts[r][c];
00535         }
00536 
00537         vCosts[c] += colMin;
00538 
00539         if (colMin != infinity) {
00540           edgeCosts.subFromCol(c, colMin);
00541         }
00542         else {
00543           edgeCosts.setCol(c, 0);
00544         }
00545       }
00546 
00547       return edgeCosts.isZero();
00548     }
00549 
00550     void backpropagate() {
00551       while (!stack.empty()) {
00552         computeSolution(stack.back());
00553         stack.pop_back();
00554       }
00555     }
00556 
00557     void computeSolution(Graph::NodeItr nItr) {
00558 
00559       NodeData &nodeData = getSolverNodeData(nItr);
00560 
00561       Vector v(g.getNodeCosts(nItr));
00562 
00563       // Solve based on existing solved edges.
00564       for (SolverEdgeItr solvedEdgeItr = nodeData.solverEdgesBegin(),
00565                          solvedEdgeEnd = nodeData.solverEdgesEnd();
00566            solvedEdgeItr != solvedEdgeEnd; ++solvedEdgeItr) {
00567 
00568         Graph::EdgeItr eItr(*solvedEdgeItr);
00569         Matrix &edgeCosts = g.getEdgeCosts(eItr);
00570 
00571         if (nItr == g.getEdgeNode1(eItr)) {
00572           Graph::NodeItr adjNode(g.getEdgeNode2(eItr));
00573           unsigned adjSolution = s.getSelection(adjNode);
00574           v += edgeCosts.getColAsVector(adjSolution);
00575         }
00576         else {
00577           Graph::NodeItr adjNode(g.getEdgeNode1(eItr));
00578           unsigned adjSolution = s.getSelection(adjNode);
00579           v += edgeCosts.getRowAsVector(adjSolution);
00580         }
00581 
00582       }
00583 
00584       setSolution(nItr, v.minIndex());
00585     }
00586 
00587     void cleanup() {
00588       h.cleanup();
00589       nodeDataList.clear();
00590       edgeDataList.clear();
00591     }
00592   };
00593 
00594   /// \brief PBQP heuristic solver class.
00595   ///
00596   /// Given a PBQP Graph g representing a PBQP problem, you can find a solution
00597   /// by calling
00598   /// <tt>Solution s = HeuristicSolver<H>::solve(g);</tt>
00599   ///
00600   /// The choice of heuristic for the H parameter will affect both the solver
00601   /// speed and solution quality. The heuristic should be chosen based on the
00602   /// nature of the problem being solved.
00603   /// Currently the only solver included with LLVM is the Briggs heuristic for
00604   /// register allocation.
00605   template <typename HImpl>
00606   class HeuristicSolver {
00607   public:
00608     static Solution solve(Graph &g) {
00609       HeuristicSolverImpl<HImpl> hs(g);
00610       return hs.computeSolution();
00611     }
00612   };
00613 
00614 }
00615 
00616 #endif // LLVM_CODEGEN_PBQP_HEURISTICSOLVER_H