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

Generated on Thu Apr 13 16:57:32 2006 for CVC Lite by  doxygen 1.4.4