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