CVC3

search_fast.h

Go to the documentation of this file.
00001 ///////////////////////////////////////////////////////////////////////////////
00002 /*!
00003  * \file search_fast.h
00004  *
00005  * Author: Mark Zavislak
00006  *
00007  * Created: Mon Jul 21 17:33:18 UTC 2003
00008  *
00009  * <hr>
00010  *
00011  * License to use, copy, modify, sell and/or distribute this software
00012  * and its documentation for any purpose is hereby granted without
00013  * royalty, subject to the terms and conditions defined in the \ref
00014  * LICENSE file provided with this distribution.
00015  *
00016  * <hr>
00017  *
00018  * A faster implementation of the proof search engine
00019  */
00020 ///////////////////////////////////////////////////////////////////////////////
00021 
00022 #ifndef _cvc3__include__search_fast_h_
00023 #define _cvc3__include__search_fast_h_
00024 
00025 #include <deque>
00026 #include <utility>
00027 #include "search_impl_base.h"
00028 #include "variable.h"
00029 #include "circuit.h"
00030 #include "statistics.h"
00031 #include <set>
00032 #include "smartcdo.h"
00033 
00034 namespace CVC3 {
00035 
00036   class VariableManager;
00037   class DecisionEngine;
00038 
00039 ////////////////////////////////////////////////////////////////////////////
00040 //////////// Definition of modules (groups) for doxygen ////////////////////
00041 ////////////////////////////////////////////////////////////////////////////
00042 
00043 /*!
00044  * \defgroup SE_Fast Fast Search Engine
00045  * \ingroup SE
00046  *
00047  * This module includes all the components of the fast search
00048  * engine.
00049  * @{
00050  */
00051 
00052   //! Implementation of a faster search engine, using newer techniques.
00053   /*!
00054 
00055   This search engine is engineered for greater speed.  It seeks to
00056   eliminate the use of recursion, and instead replace it with
00057   iterative procedures that have cleanly defined invariants.  This
00058   will hopefully not only eliminate bugs or inefficiencies that have
00059   been difficult to track down in the default version, but it should
00060   also make it easier to trace, read, and understand.  It strives to
00061   be in line with the most modern SAT techniques.
00062 
00063   There are three other significant changes.
00064 
00065   One, we want to improve the performance on heavily non-CNF problems.
00066   Unlike the older CVC, CVC3 does not expand problems into CNF and
00067   solve, but rather uses decision procedures to effect the same thing,
00068   but often much more slowly.  This search engine will leverage off
00069   knowledge gained from the DPs in the form of conflict clauses as
00070   much as possible.
00071 
00072   Two, the solver has traditionally had a difficult time getting
00073   started on non-CNF problems.  Modern satsolvers also have this
00074   problem, and so they employ "restarts" to try solving the problem
00075   again after gaining more knowledge about the problem at hand.  This
00076   allows a more accurate choice of splitters, and in the case of
00077   non-CNF problems, the solver can immediately leverage off CNF
00078   conflict clauses that were not initially available.
00079 
00080   Third, this code is specifically designed to deal with the new
00081   dependency tracking.  Lazy maps will be eliminated in favor implicit
00082   conflict graphs, reducing computation time in two different ways.
00083 
00084   */
00085 
00086   class SearchEngineFast : public SearchImplBase {
00087 
00088     friend class Circuit;
00089 
00090   /*! \addtogroup SE_Fast
00091    * @{
00092    */
00093 
00094     //! Name
00095     std::string d_name;
00096     //! Decision Engine
00097     DecisionEngine *d_decisionEngine;
00098     //! Total number of unit propagations
00099     StatCounter d_unitPropCount;
00100     //! Total number of circuit propagations
00101     StatCounter d_circuitPropCount;
00102     //! Total number of conflicts
00103     StatCounter d_conflictCount;
00104     //! Total number of conflict clauses generated (not all may be active)
00105     StatCounter d_conflictClauseCount;
00106 
00107     //! Backtrackable list of clauses.
00108     /*! New clauses may come into play
00109       from the decision procedures that are context dependent. */
00110     CDList<ClauseOwner> d_clauses;
00111 
00112     //! Backtrackable set of pending unprocessed literals.
00113     /*! These can be discovered at any scope level during conflict
00114       analysis. */
00115     CDMap<Expr,Theorem> d_unreportedLits;
00116     CDMap<Expr,bool> d_unreportedLitsHandled;
00117 
00118     //! Backtrackable list of non-literals (non-CNF formulas).
00119     /*! We treat nonliterals like clauses, because they are a superset
00120         of clauses.  We stick the real clauses into d_clauses, but all
00121         the rest have to be stored elsewhere. */
00122     CDList<SmartCDO<Theorem> > d_nonLiterals;
00123     CDMap<Expr,Theorem> d_nonLiteralsSaved; //!< prevent reprocessing
00124     //    CDMap<Expr,bool> d_nonLiteralSimplified; //!< Simplified non-literals
00125 
00126     //! Theorem which records simplification of the last query
00127     CDO<Theorem> d_simplifiedThm;
00128 
00129     //! Size of d_nonLiterals before most recent query
00130     CDO<unsigned> d_nonlitQueryStart;
00131     //! Size of d_nonLiterals after query (not including DP-generated non-literals)
00132     CDO<unsigned> d_nonlitQueryEnd;
00133     //! Size of d_clauses before most recent query
00134     CDO<unsigned> d_clausesQueryStart;
00135     //! Size of d_clauses after query
00136     CDO<unsigned> d_clausesQueryEnd;
00137 
00138     //! Array of conflict clauses: one deque for each outstanding query
00139     std::vector<std::deque<ClauseOwner>* > d_conflictClauseStack;
00140     //! Reference to top deque of conflict clauses
00141     std::deque<ClauseOwner>* d_conflictClauses;
00142 
00143     //! Helper class for managing conflict clauses
00144     /*! Conflict clauses should only get popped when the context in which a
00145      *  call to checkValid originates is popped.  This helper class checks
00146      *  every time there's a pop to see if the conflict clauses need to be
00147      *  restored.
00148      */
00149     friend class ConflictClauseManager;
00150     class ConflictClauseManager :public ContextNotifyObj {
00151       SearchEngineFast* d_se;
00152       std::vector<int> d_restorePoints;
00153     public:
00154       ConflictClauseManager(Context* context, SearchEngineFast* se)
00155         : ContextNotifyObj(context), d_se(se) {}
00156       void setRestorePoint();
00157       void notify();
00158     };
00159     ConflictClauseManager d_conflictClauseManager;
00160 
00161     //! Unprocessed unit conflict clauses
00162     /*! When we find unit conflict clauses, we are automatically going
00163         to jump back to the original scope.  Hopefully we won't find
00164         multiple ones, but you never know with those wacky decision
00165         procedures just spitting new information out.  These are then
00166         directly asserted then promptly forgotten about.  Chaff keeps
00167         them around (for correctness maybe), but we already have the
00168         proofs stored in the literals themselves. */
00169     std::vector<Clause> d_unitConflictClauses;
00170 
00171 
00172     //! Set of literals to be processed by bcp.
00173     /*! These are emptied out upon backtracking, because they can only
00174         be valid if they were already all processed without conflicts.
00175         Therefore, they don't need to be context dependent. */
00176     std::vector<Literal> d_literals;
00177     //! Set of asserted literals which may survive accross checkValid() calls
00178     /*!
00179      *  When a literal is asserted outside of checkValid() call, its
00180      *  value is remembered in a Literal database, but the literal
00181      *  queue for BCP is cleared.  We add literals to this set at the
00182      *  proper scope levels, and propagate them at the beginning of a
00183      *  checkValid() call.
00184      */
00185     CDMap<Expr,Literal> d_literalSet;
00186 
00187     //! Queue of derived facts to be sent to DPs
00188     /*! \sa addFact() and commitFacts() */
00189     std::vector<Theorem> d_factQueue;
00190     /*! @brief When true, use TheoryCore::enqueueFact() instead of
00191      * addFact() in commitFacts()
00192      */
00193     bool d_useEnqueueFact;
00194     //! True when checkSAT() is running
00195     /*! Used by addLiteralFact() to determine whether to BCP the
00196      *  literals immediately (outside of checkSAT()) or not.
00197      */
00198     bool d_inCheckSAT;
00199 
00200 
00201     //! Set of alive literals that shouldn't be garbage-collected
00202     /*! Unfortunately, I have a keep-alive issue.  I think literals
00203         actually have to hang around, so I assert them to a separate
00204         d_litsAlive CDList. */
00205     CDList<Literal> d_litsAlive;
00206 
00207     /*! @brief Mappings of literals to vectors of pointers to the
00208       corresponding watched literals.  */
00209     /*! A pointer is a pair (clause,i), where 'i' in {0,1} is the index
00210       of the watch pointer in the clause.
00211     */
00212     // ExprHashMap<std::vector<std::pair<Clause, int> > > d_wp;
00213 
00214     std::vector<Circuit*> d_circuits;
00215     ExprHashMap<std::vector<Circuit*> > d_circuitsByExpr;
00216 
00217     //! The scope of the last conflict
00218     /*! This is the true scope of the conflict, not necessarily the
00219       scope where the conflict was detected. */
00220     int d_lastConflictScope;
00221     //! The last conflict clause (for checkSAT()).  May be Null.
00222     /*! It records which conflict clause must be processed by BCP after
00223       backtracking from a conflict.  A conflict may generate several
00224       conflict clauses, but only one of them will cause a unit
00225       propagation.
00226     */
00227     Clause d_lastConflictClause;
00228     //! Theorem(FALSE) which generated a conflict
00229     Theorem d_conflictTheorem;
00230 
00231     /*! @brief Return a ref to the vector of watched literals.  If no
00232       such vector exists, create it. */
00233     std::vector<std::pair<Clause, int> >& wp(const Literal& literal);
00234 
00235     /*! @brief \return true if SAT, false otherwise.
00236      *
00237      * When false is returned, proof is saved in d_lastConflictTheorem */
00238     QueryResult checkSAT();
00239 
00240     //! Choose a splitter.
00241     /*! Preconditions: The current context is consistent.
00242      *
00243      * \return true if splitter available, and it asserts it through
00244      * newIntAssumption() after first pushing a new context.
00245      *
00246      * \return false if no splitters are available, which means the
00247      * context is SAT.
00248      *
00249      * Postconditions: A literal has been asserted through
00250      * newIntAssumption().
00251      */
00252     bool split();
00253 
00254     // Moved from the decision engine:
00255     //! Returns a splitter
00256     Expr findSplitter();
00257     //! Position of a literal with max score in d_litsByScores
00258     unsigned d_litsMaxScorePos;
00259     //! Vector of literals sorted by score
00260     std::vector<Literal> d_litsByScores;
00261     /*
00262     //! Mapping of literals to scores
00263     ExprHashMap<unsigned> d_litScores;
00264     //! Mapping of literals to their counters
00265     ExprHashMap<unsigned> d_litCounts;
00266     //! Mapping of literals to previous counters (what's that, anyway?)
00267     ExprHashMap<unsigned> d_litCountPrev;
00268     */
00269     //! Internal splitter counter for delaying updateLitScores()
00270     int d_splitterCount;
00271     //! Internal (decrementing) count of added splitters, to sort d_litByScores
00272     int d_litSortCount;
00273 
00274     //! Flag to switch on/off the berkmin heuristic
00275     const bool d_berkminFlag;
00276 
00277     //! Clear the list of asserted literals (d_literals)
00278     void clearLiterals();
00279 
00280     void updateLitScores(bool firstTime);
00281     //! Add the literals of a new clause to d_litsByScores
00282     void updateLitCounts(const Clause& c);
00283 
00284 
00285     //! Boolean constraint propagation.
00286     /*! Preconditions: On every run besides the first, the CNF clause
00287      *  database must not have any unit or unsat clauses, and there
00288      *  must be a literal queued up for processing.  The current
00289      *  context must be consistent.  Any and all assertions and
00290      *  assignments must actually be made within the bcp loop.  Other
00291      *  parts of the solver may queue new facts with addLiteralFact()
00292      *  and addNonLiteralFact().  bcp() will either process them, or
00293      *  it will find a conflict, in which case they will no longer be
00294      *  valid and will be dumped.  Any nonLiterals must already be
00295      *  simplified.
00296      *
00297      *  Description: BCP will systematically work through all the
00298      *  literals and nonliterals, using boolean constraint propagation
00299      *  by detecting unit clauses and using addLiteralFact() on the
00300      *  unit literal while also marking the clause as SAT.  Any
00301      *  clauses marked SAT are guaranteed to be SAT, but clauses not
00302      *  marked SAT are not guaranteed to be unsat.
00303      *
00304      * \return false if a conflict is found, true otherwise.
00305      *
00306      *  Postconditions: False indicates conflict.  If the conflict was
00307      *  discovered in CNF, we call the proof rule, then store that
00308      *  clause pointer so fixConflict() can skip over it during the
00309      *  search (when we finally change dependency tracking).
00310      *
00311      *  True indicates success.  All literals and nonLiterals have
00312      *  been processed without causing a conflict.  Processing
00313      *  nonliterals implies running simplify on them, immediately
00314      *  asserting any simplifications back to the core, and marking
00315      *  the original nonLiteral as simplified, to be ignored by all
00316      *  future (this context or deeper) splitters and bcp runs.
00317      *  Therefore, there will be no unsimplified nonliterals
00318      *  remaining.
00319      */
00320     bool bcp();
00321 
00322     //! Determines backtracking level and adds conflict clauses.
00323     /*! Preconditions: The current context is inconsistent.  If it
00324      *  resulted from a conflictRule() application, then the theorem
00325      *  is stored inside d_lastConflictTheorem.
00326      *
00327      *  If this was caused from bcp, we obtain the conflictRule()
00328      *  theorem from the d_lastConflictTheorem instance variable.
00329      *  From here we build conflict clauses and determine the correct
00330      *  backtracking level, at which point we actually backtrack
00331      *  there.  Finally, we also call addLiteralFact() on the "failure
00332      *  driven assertion" literal so that bcp has some place to begin
00333      *  (and it satisfies the bcp preconditions)
00334      *
00335      *  Postconditions: If True is returned: The current context is
00336      *  consistent, and a literal is queued up for bcp to process.  If
00337      *  False is returned: The context cannot be made consistent
00338      *  without backtracking past the original one, so the formula is
00339      *  unsat.
00340      */
00341     bool fixConflict();
00342     //! FIXME: document this
00343     void assertAssumptions();
00344     //! Queue up a fact to assert to the DPs later
00345     void enqueueFact(const Theorem& thm);
00346     //! Set the context inconsistent.  Takes Theorem(FALSE).
00347     void setInconsistent(const Theorem& thm);
00348     //! Commit all the enqueued facts to the DPs
00349     void commitFacts();
00350     //! Clear the local fact queue
00351     void clearFacts();
00352 
00353     /*! @name Processing a Conflict */
00354     //@{
00355     /*! @brief Take a conflict in the form of Literal, or
00356         Theorem, and generate all the necessary conflict clauses. */
00357     Theorem processConflict(const Literal& l);
00358     Theorem processConflict(const Theorem& thm);
00359     //@}
00360 
00361     //! Auxiliary function for unit propagation
00362     bool propagate(const Clause &c, int idx, bool& wpUpdated);
00363     //! Do the unit propagation for the clause
00364     void unitPropagation(const Clause &c, unsigned idx);
00365     //! Analyse the conflict, find the UIPs, etc.
00366     void analyzeUIPs(const Theorem &falseThm, int conflictScope);
00367 
00368     /////////////////////////////
00369     // New convenience methods //
00370     /////////////////////////////
00371 
00372     //! Go through all the clauses and check the watch pointers (for debugging)
00373     IF_DEBUG(void fullCheck();)
00374     //! Set up the watch pointers for the new clause
00375     void addNewClause(Clause &c);
00376     //! Process a new derived fact (auxiliary function)
00377     void recordFact(const Theorem& thm);
00378 
00379     //! First pass in conflict analysis; takes a theorem of FALSE
00380     void traceConflict(const Theorem& conflictThm);
00381     //! Private helper function for checkValid and restart
00382     QueryResult checkValidMain(const Expr& e2);
00383 
00384   public:
00385     //! The main Constructor
00386     SearchEngineFast(TheoryCore* core);
00387     //! Destructor
00388     virtual ~SearchEngineFast();
00389 
00390     const std::string& getName() { return d_name; }
00391     //! Implementation of the API call
00392     virtual QueryResult checkValidInternal(const Expr& e);
00393     virtual QueryResult restartInternal(const Expr& e);
00394     //! Redefine the counterexample generation.
00395     virtual void getCounterExample(std::vector<Expr>& assertions);
00396     //! Notify the search engine about a new literal fact.
00397     void addLiteralFact(const Theorem& thm);
00398     //! Notify the search engine about a new non-literal fact.
00399     void addNonLiteralFact(const Theorem& thm);
00400     /*! @brief Redefine newIntAssumption(): we need to add the new theorem
00401       to the appropriate Literal */
00402     virtual Theorem newIntAssumption(const Expr& e);
00403     virtual bool isAssumption(const Expr& e);
00404     void addSplitter(const Expr& e, int priority);
00405 
00406   /*! @} */ // end of addtogroup SE_Fast
00407 
00408     //! Return next clause whose satisfiability is unknown
00409     //virtual Clause nextClause();
00410     //! Return next non-clause which does not reduce to false
00411     //virtual Expr nextNonClause();
00412   };
00413 /*! @} */ // end of SE_Fast
00414 }
00415 
00416 
00417 #endif