/*****************************************************************************/ /*! * \file search_impl_base.h * \brief Abstract API to the proof search engine * * Author: Clark Barrett, Vijay Ganesh (Clausal Normal Form Converter) * * Created: Fri Jan 17 13:35:03 2003 * *
* * License to use, copy, modify, sell and/or distribute this software * and its documentation for any purpose is hereby granted without * royalty, subject to the terms and conditions defined in the \ref * LICENSE file provided with this distribution. * *
* */ /*****************************************************************************/ #ifndef _cvc3__include__search_impl_base_h_ #define _cvc3__include__search_impl_base_h_ #include "search.h" #include "theory_core.h" #include "variable.h" namespace CVC3 { class SearchEngineRules; class VariableManager; //! API to to a generic proof search engine (a.k.a. SAT solver) /*! \ingroup SE */ class SearchImplBase :public SearchEngine { friend class DecisionEngine; protected: /*! \addtogroup SE * @{ */ //! Variable manager for classes Variable and Literal VariableManager* d_vm; /*! @brief The bottom-most scope for the current call to checkSAT (where conflict clauses are still valid). */ CDO d_bottomScope; TheoryCore::CoreSatAPI* d_coreSatAPI_implBase; //! Representation of a DP-suggested splitter class Splitter { Literal d_lit; public: // int priority; //! Constructor Splitter(const Literal& lit); //! Copy constructor Splitter(const Splitter& s); //! Assignment Splitter& operator=(const Splitter& s); //! Descructor ~Splitter(); operator Literal() { return d_lit; } //! The order is descending by priority ("reversed", highest first) // friend bool operator<(const Splitter& s1, const Splitter& s2) { // return (s1.priority > s2.priority // || (s1.priority == s2.priority && s1.expr > s2.expr)); // } }; //! Backtracking ordered set of DP-suggested splitters CDList d_dpSplitters; /*! @brief Theorem from the last successful checkValid call. It is used by getProof and getAssumptions. */ Theorem d_lastValid; /*! @brief Assumptions from the last unsuccessful checkValid call. These are used by getCounterExample. */ ExprHashMap d_lastCounterExample; /*! @brief Maintain the list of current assumptions (user asserts and splitters) for getAssumptions(). */ CDMap d_assumptions; //! Backtracking cache for the CNF generator CDMap d_cnfCache; //! Backtracking set of new variables generated by CNF translator /*! Specific search engines do not have to split on these variables */ CDMap d_cnfVars; //! Command line flag whether to convert to CNF const bool* d_cnfOption; //! Flag: whether to convert term ITEs into CNF const bool* d_ifLiftOption; //! Flag: ignore auxiliary CNF variables when searching for a splitter const bool* d_ignoreCnfVarsOption; //! Flag: Preserve the original formula with +cnf (for splitter heuristics) const bool* d_origFormulaOption; /*! * \name CNF Caches * * These caches are for subexpressions of the translated formula * phi, to avoid expanding phi into a tree. We cannot use * d_cnfCache for that, since it is effectively non-backtracking, * and we do not know if a subexpression of phi was translated at * the current level, or at some other (inactive) branch of the * decision tree. * @{ */ //! Cache for enqueueCNF() CDMap d_enqueueCNFCache; //! Cache for applyCNFRules() CDMap d_applyCNFRulesCache; //! Cache for replaceITE() CDMap d_replaceITECache; /*@}*/ // End of CNF Caches //! Construct a Literal out of an Expr or return an existing one Literal newLiteral(const Expr& e) { return Literal(d_vm, e); } /*! @brief Our version of simplifier: take Theorem(e), apply simplifier to get Theorem(e==e'), return Theorem(e') */ Theorem simplify(const Theorem& e) { return d_core->iffMP(e, d_core->simplify(e.getExpr())); } //! Notify the search engine about a new literal fact. /*! It should be called by SearchEngine::addFact() only. * Must be implemented by the subclasses of SearchEngine. * * IMPORTANT: do not call addFact() from this function; use * enqueueFact() or setInconsistent() instead. */ virtual void addLiteralFact(const Theorem& thm) = 0; //! Notify the search engine about a new non-literal fact. /*! It should be called by SearchEngine::addFact() only. * Must be implemented by the subclasses of SearchEngine. * * IMPORTANT: do not call addFact() from this function; use * enqueueFact() or setInconsistent() instead. */ virtual void addNonLiteralFact(const Theorem& thm) = 0; //! Add a new fact to the search engine bypassing CNF converter /*! Calls either addLiteralFact() or addNonLiteralFact() * appropriately, and converts to CNF when d_cnfOption is set. If * fromCore==true, this fact already comes from the core, and * doesn't need to be reported back to the core. */ void addCNFFact(const Theorem& thm, bool fromCore=false); public: int getBottomScope() { return d_bottomScope; } //! Check if e is a clause (a literal, or a disjunction of literals) bool isClause(const Expr& e); //! Check if e is a propositional clause /*! \sa isPropAtom() */ bool isPropClause(const Expr& e); //! Check whether e is a fresh variable introduced by the CNF converter /*! Search engines do not need to split on those variables in order * to be complete */ bool isCNFVar(const Expr& e) { return (d_cnfVars.count(e) > 0); } //! Check if a splitter is required for completeness /*! Currently, it checks that 'e' is not an auxiliary CNF variable */ bool isGoodSplitter(const Expr& e); private: //! Translate theta to CNF and enqueue the new clauses void enqueueCNF(const Theorem& theta); //! Recursive version of enqueueCNF() void enqueueCNFrec(const Theorem& theta); //! FIXME: write a comment void applyCNFRules(const Theorem& e); //! Cache a theorem phi <=> v by phi, where v is a literal. void addToCNFCache(const Theorem& thm); //! Find a theorem phi <=> v by phi, where v is a literal. /*! \return Null Theorem if not found. */ Theorem findInCNFCache(const Expr& e); //! Replaces ITE subexpressions in e with variables Theorem replaceITE(const Expr& e); protected: //! Return the current scope level (for convenience) int scopeLevel() { return d_core->getCM()->scopeLevel(); } public: //! Constructor SearchImplBase(TheoryCore* core); //! Destructor virtual ~SearchImplBase(); virtual void registerAtom(const Expr& e) { d_core->registerAtom(e, Theorem()); } virtual Theorem getImpliedLiteral() { return d_core->getImpliedLiteral(); } virtual void push() { d_core->getCM()->push(); } virtual void pop() { d_core->getCM()->pop(); } /////////////////////////////////////////////////////////////////////////// // checkValid() is the method that subclasses must implement. /////////////////////////////////////////////////////////////////////////// //! Checks the validity of a formula in the current context /*! The method that actually calls the SAT solver (implemented in a subclass). It should maintain d_assumptions (add all asserted splitters to it), and set d_lastValid and d_lastCounterExample appropriately before exiting. */ virtual QueryResult checkValidInternal(const Expr& e) = 0; //! Similar to checkValidInternal(), only returns Theorem(e) or Null virtual QueryResult checkValid(const Expr& e, Theorem& result); //! Reruns last check with e as an additional assumption virtual QueryResult restartInternal(const Expr& e) = 0; //! Reruns last check with e as an additional assumption virtual QueryResult restart(const Expr& e, Theorem& result); void returnFromCheck() { Theorem thm; restart(d_core->falseExpr(), thm); } virtual Theorem lastThm() { return d_lastValid; } /////////////////////////////////////////////////////////////////////////// // The following methods are provided by the base class, and in most // cases should be sufficient. However, they are virtual so that // subclasses can add functionality to them if needed. /////////////////////////////////////////////////////////////////////////// /*! @brief Generate and add a new assertion to the set of assertions in the current context. This should only be used by class VCL in assertFormula(). */ Theorem newUserAssumption(const Expr& e); //! Add a new internal asserion virtual Theorem newIntAssumption(const Expr& e); //! Helper for above function void newIntAssumption(const Theorem& thm); //! Get all assumptions made in this and all previous contexts. /*! \param assumptions should be an empty vector which will be filled \ with the assumptions */ void getUserAssumptions(std::vector& assumptions); void getInternalAssumptions(std::vector& assumptions); virtual void getAssumptions(std::vector& assumptions); //! Check if the formula has been assumed virtual bool isAssumption(const Expr& e); //! Add a new fact to the search engine from the core /*! It should be called by TheoryCore::assertFactCore(). */ void addFact(const Theorem& thm); //! Suggest a splitter to the SearchEngine /*! The higher is the priority, the sooner the SAT solver will split * on it. It can be positive or negative (default is 0). * * The set of suggested splitters is backtracking; that is, a * splitter is "forgotten" once the scope is backtracked. * * This method can be used either to change the priority * of existing splitters, or to introduce new splitters that DPs * consider relevant, even though they do not appear in existing * formulas. */ virtual void addSplitter(const Expr& e, int priority); virtual void getCounterExample(std::vector& assertions, bool inOrder = true); // The following two methods should be called only after a checkValid // which returns true. In any other case, they return Null values. //! Returns the proof term for the last proven query /*! It should be called only after a checkValid which returns true. In any other case, it returns Null. */ virtual Proof getProof(); /*! @brief Returns the set of assumptions used in the proof. It should be a subset of getAssumptions(). */ /*! It should be called only after a checkValid which returns true. In any other case, it returns Null. */ virtual const Assumptions& getAssumptionsUsed(); //! Process result of checkValid void processResult(const Theorem& res, const Expr& e); /* @} */ // end of group SE }; } #endif