//===-- HeuristcBase.h --- Heuristic base class for PBQP --------*- C++ -*-===// // // The LLVM Compiler Infrastructure // // This file is distributed under the University of Illinois Open Source // License. See LICENSE.TXT for details. // //===----------------------------------------------------------------------===// #ifndef LLVM_CODEGEN_PBQP_HEURISTICBASE_H #define LLVM_CODEGEN_PBQP_HEURISTICBASE_H #include "HeuristicSolver.h" namespace PBQP { /// \brief Abstract base class for heuristic implementations. /// /// This class provides a handy base for heuristic implementations with common /// solver behaviour implemented for a number of methods. /// /// To implement your own heuristic using this class as a base you'll have to /// implement, as a minimum, the following methods: /// /// /// These methods are implemented in this class for documentation purposes, /// but will assert if called. /// /// Note that this class uses the curiously recursive template idiom to /// forward calls to the derived class. These methods need not be made /// virtual, and indeed probably shouldn't for performance reasons. /// /// You'll also need to provide NodeData and EdgeData structs in your class. /// These can be used to attach data relevant to your heuristic to each /// node/edge in the PBQP graph. template class HeuristicBase { private: typedef std::list OptimalList; HeuristicSolverImpl &s; Graph &g; OptimalList optimalList; // Return a reference to the derived heuristic. HImpl& impl() { return static_cast(*this); } // Add the given node to the optimal reductions list. Keep an iterator to // its location for fast removal. void addToOptimalReductionList(Graph::NodeId nId) { optimalList.insert(optimalList.end(), nId); } public: /// \brief Construct an instance with a reference to the given solver. /// @param solver The solver which is using this heuristic instance. HeuristicBase(HeuristicSolverImpl &solver) : s(solver), g(s.getGraph()) { } /// \brief Get the solver which is using this heuristic instance. /// @return The solver which is using this heuristic instance. /// /// You can use this method to get access to the solver in your derived /// heuristic implementation. HeuristicSolverImpl& getSolver() { return s; } /// \brief Get the graph representing the problem to be solved. /// @return The graph representing the problem to be solved. Graph& getGraph() { return g; } /// \brief Tell the solver to simplify the graph before the reduction phase. /// @return Whether or not the solver should run a simplification phase /// prior to the main setup and reduction. /// /// HeuristicBase returns true from this method as it's a sensible default, /// however you can over-ride it in your derived class if you want different /// behaviour. bool solverRunSimplify() const { return true; } /// \brief Decide whether a node should be optimally or heuristically /// reduced. /// @return Whether or not the given node should be listed for optimal /// reduction (via R0, R1 or R2). /// /// HeuristicBase returns true for any node with degree less than 3. This is /// sane and sensible for many situations, but not all. You can over-ride /// this method in your derived class if you want a different selection /// criteria. Note however that your criteria for selecting optimal nodes /// should be at least as strong as this. I.e. Nodes of degree 3 or /// higher should not be selected under any circumstances. bool shouldOptimallyReduce(Graph::NodeId nId) { if (g.getNodeDegree(nId) < 3) return true; // else return false; } /// \brief Add the given node to the list of nodes to be optimally reduced. /// @param nItr Node iterator to be added. /// /// You probably don't want to over-ride this, except perhaps to record /// statistics before calling this implementation. HeuristicBase relies on /// its behaviour. void addToOptimalReduceList(Graph::NodeId nId) { optimalList.push_back(nId); } /// \brief Initialise the heuristic. /// /// HeuristicBase iterates over all nodes in the problem and adds them to /// the appropriate list using addToOptimalReduceList or /// addToHeuristicReduceList based on the result of shouldOptimallyReduce. /// /// This behaviour should be fine for most situations. void setup() { for (Graph::NodeItr nItr = g.nodesBegin(), nEnd = g.nodesEnd(); nItr != nEnd; ++nItr) { if (impl().shouldOptimallyReduce(*nItr)) { addToOptimalReduceList(*nItr); } else { impl().addToHeuristicReduceList(*nItr); } } } /// \brief Optimally reduce one of the nodes in the optimal reduce list. /// @return True if a reduction takes place, false if the optimal reduce /// list is empty. /// /// Selects a node from the optimal reduce list and removes it, applying /// R0, R1 or R2 as appropriate based on the selected node's degree. bool optimalReduce() { if (optimalList.empty()) return false; Graph::NodeId nId = optimalList.front(); optimalList.pop_front(); switch (s.getSolverDegree(nId)) { case 0: s.applyR0(nId); break; case 1: s.applyR1(nId); break; case 2: s.applyR2(nId); break; default: llvm_unreachable( "Optimal reductions of degree > 2 nodes is invalid."); } return true; } /// \brief Perform the PBQP reduction process. /// /// Reduces the problem to the empty graph by repeated application of the /// reduction rules R0, R1, R2 and RN. /// R0, R1 or R2 are always applied if possible before RN is used. void reduce() { bool finished = false; while (!finished) { if (!optimalReduce()) { if (impl().heuristicReduce()) { getSolver().recordRN(); } else { finished = true; } } } } /// \brief Add a node to the heuristic reduce list. /// @param nItr Node iterator to add to the heuristic reduce list. void addToHeuristicList(Graph::NodeId nId) { llvm_unreachable("Must be implemented in derived class."); } /// \brief Heuristically reduce one of the nodes in the heuristic /// reduce list. /// @return True if a reduction takes place, false if the heuristic reduce /// list is empty. bool heuristicReduce() { llvm_unreachable("Must be implemented in derived class."); return false; } /// \brief Prepare a change in the costs on the given edge. /// @param eItr Edge iterator. void preUpdateEdgeCosts(Graph::EdgeId eId) { llvm_unreachable("Must be implemented in derived class."); } /// \brief Handle the change in the costs on the given edge. /// @param eItr Edge iterator. void postUpdateEdgeCostts(Graph::EdgeId eId) { llvm_unreachable("Must be implemented in derived class."); } /// \brief Handle the addition of a new edge into the PBQP graph. /// @param eItr Edge iterator for the added edge. void handleAddEdge(Graph::EdgeId eId) { llvm_unreachable("Must be implemented in derived class."); } /// \brief Handle disconnection of an edge from a node. /// @param eItr Edge iterator for edge being disconnected. /// @param nItr Node iterator for the node being disconnected from. /// /// Edges are frequently removed due to the removal of a node. This /// method allows for the effect to be computed only for the remaining /// node in the graph. void handleRemoveEdge(Graph::EdgeId eId, Graph::NodeId nId) { llvm_unreachable("Must be implemented in derived class."); } /// \brief Clean up any structures used by HeuristicBase. /// /// At present this just performs a sanity check: that the optimal reduce /// list is empty now that reduction has completed. /// /// If your derived class has more complex structures which need tearing /// down you should over-ride this method but include a call back to this /// implementation. void cleanup() { assert(optimalList.empty() && "Nodes left over in optimal reduce list?"); } }; } #endif // LLVM_CODEGEN_PBQP_HEURISTICBASE_H