diff --git a/abclib.dsp b/abclib.dsp index 500ffcaf0..1ab567cce 100644 --- a/abclib.dsp +++ b/abclib.dsp @@ -2911,6 +2911,10 @@ SOURCE=.\src\sat\cadical\cadical_averages.cpp # End Source File # Begin Source File +SOURCE=.\src\sat\cadical\cadical_backbone.cpp +# End Source File +# Begin Source File + SOURCE=.\src\sat\cadical\cadical_backtrack.cpp # End Source File # Begin Source File @@ -3243,6 +3247,14 @@ SOURCE=.\src\sat\cadical\cadical_walk.cpp # End Source File # Begin Source File +SOURCE=.\src\sat\cadical\cadical_walk_full_occs.cpp +# End Source File +# Begin Source File + +SOURCE=.\src\sat\cadical\cadical_warmup.cpp +# End Source File +# Begin Source File + SOURCE=.\src\sat\cadical\cadical_watch.cpp # End Source File # Begin Source File diff --git a/src/sat/cadical/cadical.hpp b/src/sat/cadical/cadical.hpp index 64a5d9ef7..7ceedcec4 100644 --- a/src/sat/cadical/cadical.hpp +++ b/src/sat/cadical/cadical.hpp @@ -7,6 +7,16 @@ #include #include +/*========================================================================*/ + +// We support semantic versioning (https://semver.org/), which means that we +// aim at not breaking API usage when increasing the minor or patch version, +// but assume API breaking changes when increasing the major version. + +#define CADICAL_MAJOR 2 // Major semantic version. +#define CADICAL_MINOR 2 // Minor semantic version. +#define CADICAL_PATCH 0 // Semantic patch version. + ABC_NAMESPACE_CXX_HEADER_START namespace CaDiCaL { @@ -46,7 +56,9 @@ enum Status { // // ------------------------------------------------------------------ // // Encode Problem and check without assumptions. // -// enum { TIE = 1, SHIRT = 2 }; +// int TIE = declare_one_more_variable (); +// int SHIRT = declare_one_more_variable (); +// CADICAL_assert (vars () >= 2); // // solver->add (-TIE), solver->add (SHIRT), solver->add (0); // solver->add (TIE), solver->add (SHIRT), solver->add (0); @@ -220,6 +232,7 @@ class Terminator; class ClauseIterator; class WitnessIterator; class ExternalPropagator; +class EquivalenceTracer; class Tracer; struct InternalTracer; class FileTracer; @@ -297,12 +310,22 @@ public: // int solve (); - // Get value (-lit=false, lit=true) of valid non-zero literal. + // Get the value of a valid non-zero literal. This follows the IPASIR + // semantics which says to return 'lit' if 'lit' is assigned to 'true' and + // '-lit' if 'lit' is assigned to false. This has the consequence that + // the returned literal is always assigned to 'true' and thus might be a + // bit confusing. To avoid the headache of these semantics (which we + // unfortunately should follow to be compatabile with IPASIR) the user can + // simply use positive variable indices instead of literals. Then the + // returned integer is negative if the variable is assigned to 'false' and + // positive it is assigned to 'true'. // // require (SATISFIED) // ensure (SATISFIED) // - int val (int lit); + int val ( + int lit, + bool use_default_value_for_declared_but_not_used_variable = true); // Try to flip the value of the given literal without falsifying the // formula. Returns 'true' if this was successful. Otherwise the model is @@ -361,7 +384,7 @@ public: void connect_terminator (Terminator *terminator); void disconnect_terminator (); - // Add call-back which allows to export learned clauses. + // Add call-back which allows to export_ learned clauses. // // require (VALID) // ensure (VALID) @@ -486,14 +509,17 @@ public: // Returns // - // 0 = UNKNOWN (unit propagation did not lead to a conflict nor to a - // complete assignment, or limit reached or interrupted - // through 'terminate') + // 0 = UNKNOWN // 10 = SATISFIABLE // 20 = UNSATISFIABLE - + // + // The 'UNKNOWN' result means that unit propagation did not lead to a + // conflict nor to a complete assignment, or limit reached or interrupted + // through 'terminate'. + // // require (READY) // ensure (INCONCLUSIVE | SATISFIED | UNSATISFIED) + // int propagate (); // @@ -502,6 +528,21 @@ public: // void implied (std::vector &implicants); + // We are not enforcing 'C++14' yet on all compilers/platforms (clang++ + // and MacOS in particular). The feature check for this warning is + // cumbersome to implement and conflicts with other checks for 'C++11'. + // After moving to 'C++14' or better 'C++17' we can add this check back + // but by then will probably have removed 'get_entrailed_literals' anyhow + // (in version '3.0.0'). + // +#if 0 + [[deprecated ("use the function implied instead with the same semantics " + "and arguments")]] +#endif + void get_entrailed_literals (std::vector &implicants) { + implied (implicants); + } + //------------------------------------------------------------------------ // This function determines a good splitting literal. The result can be // zero if the formula is proven to be satisfiable or unsatisfiable. This @@ -510,7 +551,7 @@ public: // returned but the state remains steady. // // require (READY) - // ensure (INCONCLUSIVE |SATISFIED|UNSATISFIED) + // ensure (INCONCLUSIVE | SATISFIED | UNSATISFIED) // int lookahead (void); @@ -565,12 +606,15 @@ public: // literal is used as an argument except for the functions 'val', 'fixed', // 'failed' and 'frozen'. However, the library internally keeps a maximum // variable index, which can be queried. + // // With factor (BVA) the solver might also add new variables. In that case // the user is required to use this to check which variables are currently - // free before adding new variables of their own. - // The alternative is to reserve variables in batches with - // 'reserve_difference'. Using 'reserve' in combination with any technique - // that could add variables (currently only factor) is not advised. + // free before adding new variables of their own. The alternative is to + // declare more variables in batches with 'declare_more_variables'. Using + // 'resize' in combination with any technique that could add variables + // (currently only factor) is not advised. After each application of + // `add`, `vars ()` will return an updated value, even if you did not + // import the entire clause yet. // // require (VALID | SOLVING) // ensure (VALID | SOLVING) @@ -583,21 +627,44 @@ public: // and has the same state transition and conditions as 'assume' etc. // // require (READY) - // ensure (STEADY ) + // ensure (STEADY) // - void reserve (int min_max_var); + void resize (int min_max_var); + + // We are not enforcing 'C++14' yet on all compilers/platforms (clang++ + // and MacOS in particular). The feature check for this warning is + // cumbersome to implement and conflicts with other checks for 'C++11'. + // After moving to 'C++14' or better 'C++17' we can add this check back + // but by then will have removed 'reserve' probably anyhow (in '3.0.0'). + // +#if 0 + [[deprecated ("use the function resize instead with the same semantics " + "and arguments.")]] +#endif + void reserve (int min_max_var) { resize (min_max_var); } // Increase the maximum variable index by a number of new variables. // initializes 'number_of_vars' new variables and protects them from // being used by the solver as extension variables (BVA). + // // It returns the new maximum variable index which is the highest - // variable name of the consecutive range of newly reserved variables. - // It has the same state transition and conditions as 'reserve' above. + // variable name of the consecutive range of newly delcared variables. + // It has the same state transition and conditions as 'resize' above. // // require (READY) - // ensure (STEADY ) + // ensure (STEADY) // - int reserve_difference (int number_of_vars); + int declare_more_variables (int number_of_additional_new_vars); + + // Increase the maximum variable index by one. This is a specialized + // version of declare_more_variables. + + int declare_one_more_variable (); + + // Get the value of some statistics or -1 if the statistics does not + // exist or is not support. Only requires the state to be initialized. + // + int64_t get_statistic_value (const char *const) const; #ifndef CADICAL_NTRACING //------------------------------------------------------------------------ @@ -694,19 +761,26 @@ public: // void optimize (int val); - // Specify search limits, where currently 'name' can be "conflicts", - // "decisions", "preprocessing", or "localsearch". The first two limits - // are unbounded by default. Thus using a negative limit for conflicts or - // decisions switches back to the default of unlimited search (for that - // particular limit). The preprocessing limit determines the number of - // preprocessing rounds, which is zero by default. Similarly, the local - // search limit determines the number of local search rounds (also zero by - // default). As with 'set', the return value denotes whether the limit - // 'name' is valid. These limits are only valid for the next 'solve' or - // 'simplify' call and reset to their default after 'solve' returns (as - // well as overwritten and reset during calls to 'simplify' and - // 'lookahead'). We actually also have an internal "terminate" limit - // which however should only be used for testing and debugging. + // Specify search limits, where currently 'name' can be + // + // "conflicts", + // "decisions", + // "preprocessing", or + // "localsearch". + // + // The first two limits are unbounded by default. Thus using a negative + // limit for conflicts or decisions switches back to the default of + // unlimited search (for that particular limit). The preprocessing limit + // determines the number of preprocessing rounds, which is zero by + // default. Similarly, the local search limit determines the number of + // local search rounds (zero by default). + // + // As with 'set', the return value denotes whether the limit 'name' is + // valid. These limits are only valid for the next 'solve' or 'simplify' + // call and reset to their default after 'solve' returns (as well as + // overwritten and reset during calls to 'simplify' and 'lookahead'). We + // actually also have an internal "terminate" limit which however should + // only be used for testing and debugging. // // require (READY) // ensure (READY) @@ -733,6 +807,7 @@ public: // similar to 'solve' with 'limits ("preprocessing", rounds)' except that // no CDCL nor local search, nor lucky phases are executed. The result // values are also the same: 0=UNKNOWN, 10=SATISFIABLE, 20=UNSATISFIABLE. + // // As 'solve' it resets current assumptions and limits before returning. // The numbers of rounds should not be negative. If the number of rounds // is zero only clauses are restored (if necessary) and top level unit @@ -865,18 +940,16 @@ public: void connect_proof_tracer (FileTracer *tracer, bool antecedents, bool finalize_clauses = false); - // Triggers the conclusion of incremental proofs. - // if the solver is SATISFIED it will trigger extend () - // and give the model to the proof tracer through conclude_sat () - // if the solver is UNSATISFIED it will trigger failing () - // which will learn new clauses as explained below: - // In case of failed assumptions will provide a core negated - // as a clause through the proof tracer interface. - // With a failing constraint these can be multiple clauses. - // Then it will trigger a conclude_unsat event with the id(s) - // of the newly learnt clauses or the id of the global conflict. - // In case the solver is in UNKNOWN, it will collect the currently - // entrailed literals and add them to the proof. + // Triggers the conclusion of incremental proofs. If the solver is + // 'SATISFIED' it will trigger 'extend ()' and give the model to the proof + // tracer through 'conclude_sat ()' if the solver is 'UNSATISFIED' it will + // trigger 'failing ()' which will learn new clauses as explained below: + // In case of failed assumptions will provide a core negated as a clause + // through the proof tracer interface. With a failing constraint these + // can be multiple clauses. Then it will trigger a 'conclude_unsat ()' + // event with the ids of the newly learnt clauses or the id of the global + // conflict. In case the solver is in 'UNKNOWN', it will collect the + // currently "entrailed" literals and add them to the proof. // // require (SATISFIED || UNSATISFIED || UNKNOWN) // ensure (SATISFIED || UNSATISFIED || UNKNOWN) @@ -895,20 +968,20 @@ public: //------------------------------------------------------------------------ - static void usage (); // print usage information for long options + static void usage (); // Print usage information for long options. - static void configurations (); // print configuration usage options + static void configurations (); // Print configuration usage options. // require (!DELETING) // ensure (!DELETING) // - void statistics (); // print statistics - void resources (); // print resource usage (time and memory) + void statistics (); // Print statistics. + void resources (); // Print resource usage (time and memory). // require (VALID) // ensure (VALID) // - void options (); // print current option and value list + void options (); // Print current option and value list. //------------------------------------------------------------------------ // Traverse irredundant clauses or the extension stack in reverse order. @@ -991,6 +1064,7 @@ private: // The solver is in the state ADDING if either the current clause or the // constraint (or both) is not yet terminated. + // bool adding_clause; bool adding_constraint; @@ -1002,7 +1076,7 @@ private: // public API of 'External' but hides everything else (except for the some // private functions). It is supposed to make it easier to understand the // API and use the solver through the API. - + // // This approach has the benefit of decoupling this header file from all // internal data structures, which is particularly useful if the rest of // the source is not available. For instance if only a CaDiCaL library is @@ -1046,9 +1120,9 @@ private: // The environment variable is read in the constructor and the trace is // opened for writing and then closed again in the destructor. // - // Alternatively one case use 'trace_api_calls'. Both + // Alternatively one case use 'trace_api_calls'. // - bool close_trace_api_file; // Close file if owned by solver it. + bool close_trace_api_file; // Close file if owned by solver. FILE *trace_api_file; // Also acts as flag that we are tracing. static bool tracing_api_through_environment; @@ -1057,6 +1131,7 @@ private: void trace_api_call (const char *) const; void trace_api_call (const char *, int) const; + void trace_api_call (const char *, int, int) const; void trace_api_call (const char *, const char *) const; void trace_api_call (const char *, const char *, int) const; #endif @@ -1094,9 +1169,8 @@ private: #endif #endif - // This gives warning messages for wrong 'printf' style format string - // usage. Apparently (on 'gcc 9' at least) the first argument is 'this' - // here. + // Gives warning messages for wrong 'printf' style format string usage. + // Apparently ('gcc 9' at least) the first argument is 'this' here. // // TODO: support for other compilers (beside 'gcc' and 'clang'). @@ -1113,12 +1187,12 @@ private: // require (VALID | DELETING) // ensure (VALID | DELETING) // - void section (const char *); // print section header - void message (const char *, ...) // ordinary message + void section (const char *); // Print section header. + void message (const char *, ...) // ordinary message. CADICAL_ATTRIBUTE_FORMAT (2, 3); - void message (); // empty line - only prefix - void error (const char *, ...) // produce error message + void message (); // Empty line - only prefix. + void error (const char *, ...) // Produce error message. CADICAL_ATTRIBUTE_FORMAT (2, 3); // Explicit verbose level ('section' and 'message' use '0'). @@ -1151,7 +1225,8 @@ private: // ensure (!INITIALIZING) // void dump_cnf (); - friend struct DumpCall; // Mobical calls 'dump_cnf' in 'DumpCall::execute' + friend struct DumpCall; // Mobical calls 'dump_cnf' in + // 'DumpCall::execute ()'. /*----------------------------------------------------------------------*/ @@ -1180,7 +1255,7 @@ public: virtual bool terminate () = 0; }; -// Connected learners which can be used to export learned clauses. +// Connected learners which can be used to export_ learned clauses. // The 'learning' can check the size of the learn clause and only if it // returns true then the individual literals of the learned clause are given // to the learn through 'learn' one by one terminated by a zero literal. @@ -1193,7 +1268,7 @@ public: }; // Connected listener gets notified whenever the truth value of a variable -// is fixed (for example during inprocessing or due to some derived unit +// is fixed (for example during inprocessing or due to derived unit // clauses). class FixedAssignmentListener { @@ -1212,9 +1287,10 @@ public: class ExternalPropagator { public: - bool is_lazy = false; // lazy propagator only checks complete assignments + bool is_lazy = false; // Lazy propagator only checks complete assignments. + bool are_reasons_forgettable = - false; // Reason external clauses can be deleted + false; // Reason external clauses can be deleted. virtual ~ExternalPropagator () {} @@ -1223,8 +1299,22 @@ public: // the call of propagator callbacks and when a driving clause is leading // to an assignment. // - // virtual void notify_assignment (int lit, bool is_fixed) = 0; virtual void notify_assignment (const std::vector &lits) = 0; + + // The notification for the assignement follow the standard trail + // used in SAT solvers. The assignment is a stack with (possibly + // empty) decisions. New assignments are pushed at the end of the + // stack. When backtracking, you get the information on how many + // decision (called levels in SAT solvers) you have to keep: + // new_level mean that all decision and decision and assignments + // before decision number new_level are kept, all other (at the end + // of stack) are removed. + // + // In particular, when backtracking to level '0', no decision is left and + // all assignment done before the first decision are kept. The number + // will always be lower than the number of decisions on the trail, so + // backtracking will always have an effect. + // virtual void notify_new_decision_level () = 0; virtual void notify_backtrack (size_t new_level) = 0; @@ -1236,24 +1326,24 @@ public: virtual bool cb_check_found_model (const std::vector &model) = 0; // Ask the external propagator for the next decision literal. If it - // returns 0, the solver makes its own choice. + // returns '0', the solver makes its own choice. // virtual int cb_decide () { return 0; }; // Ask the external propagator if there is an external propagation to make // under the current assignment. It returns either a literal to be - // propagated or 0, indicating that there is no external propagation under - // the current assignment. + // propagated or '0', indicating that there is no external propagation + // under the current assignment. // virtual int cb_propagate () { return 0; }; // Ask the external propagator for the reason clause of a previous - // external propagation step (done by cb_propagate). The clause must be - // added literal-by-literal closed with a 0. Further, the clause must + // external propagation step (done by 'cb_propagate ()'). The clause must + // be added literal-by-literal closed with a '0'. Further, the clause must // contain the propagated literal. // // The clause will be learned as an Irredundant Non-Forgettable Clause - // (see below at 'cb_has_external_clause' more details about it). + // (see below at 'cb_has_external_clause ()' more details about it). // virtual int cb_add_reason_clause_lit (int propagated_lit) { (void) propagated_lit; @@ -1263,22 +1353,25 @@ public: // The following two functions are used to add external clauses to the // solver during the CDCL loop. The external clause is added // literal-by-literal and learned by the solver as an irredundant - // (original) input clause. The clause can be arbitrary, but if it is - // root-satisfied or tautology, the solver will ignore it without learning - // it. Root-falsified literals are eagerly removed from the clause. - // Falsified clauses trigger conflict analysis, propagating clauses - // trigger propagation. In case chrono is 0, the solver backtracks to - // propagate the new literal on the right decision level, otherwise it - // potentially will be an out-of-order assignment on the current level. - // Unit clauses always (unless root-satisfied, see above) trigger - // backtracking (independently from the value of the chrono option and - // independently from being falsified or satisfied or unassigned) to level - // 0. Empty clause (or root falsified clause, see above) makes the problem - // unsat and stops the search immediately. A literal 0 must close the - // clause. + // (original) input clause. // - // The external propagator indicates that there is a clause to add. - // The parameter of the function allows the user to indicate that how + // The clause can be arbitrary, but if it is root-satisfied or tautology, + // the solver will ignore it without learning it. Root-falsified literals + // are eagerly removed from the clause. Falsified clauses trigger + // conflict analysis, propagating clauses trigger propagation. In case + // 'chrono' is '0', the solver backtracks to propagate the new literal on + // the correct decision level, as otherwise it potentially will be an + // out-of-order assignment on the current level. + // + // Unit clauses always (unless root-satisfied, see above) trigger + // backtracking (independently from the value of the 'chrono' option and + // independently from being falsified or satisfied or unassigned) to level + // '0'. Empty clause (or root falsified clause, see above) makes the + // problem 'UNSATISFIABLE' and stops the search immediately. A literal '0' + // must close the clause. + // + // The external propagator indicates that there is a clause to add. The + // parameter of the function allows the user to indicate that how // 'forgettable' is the external clause. Forgettable clauses are allowed // to be removed by the SAT solver during clause database reduction. // However, it is up to the solver to decide when actually the clause is @@ -1313,7 +1406,7 @@ public: // a unit literal is frozen. Falsified literals are skipped. If the solver // is inconsistent only the empty clause is traversed. // -// If 'clause' returns false traversal aborts early. +// If 'clause' returns 'false' traversal aborts early. class ClauseIterator { public: diff --git a/src/sat/cadical/cadicalSolver.c b/src/sat/cadical/cadicalSolver.c index 3a48344be..f27fe19f9 100644 --- a/src/sat/cadical/cadicalSolver.c +++ b/src/sat/cadical/cadicalSolver.c @@ -238,16 +238,17 @@ int cadical_solver_addvar(cadical_solver* s) { SeeAlso [] ***********************************************************************/ -void cadical_solver_setnvars(cadical_solver* s,int n) { - if ( n <= s->nVars ) - return; - if ( s->nVars == 0 ) - ccadical_reserve((CCaDiCaL*)s->p, n); - else { - assert( 0 ); - ccadical_reserve_difference((CCaDiCaL*)s->p, n - s->nVars); - } +void cadical_solver_setnvars(cadical_solver* s, int n) { + // make sure current number of variables are already fetched + // (also allow cases where addvar has been called) + assert(s->nVars >= ccadical_vars((CCaDiCaL*)s->p)); + // invalid to set fewer variables than current + assert(s->nVars <= n); + // set and resize if appropriate s->nVars = n; + if(ccadical_vars((CCaDiCaL*)s->p) == 0) { + ccadical_resize((CCaDiCaL*)s->p, n); + } } /**Function************************************************************* diff --git a/src/sat/cadical/cadical_analyze.cpp b/src/sat/cadical/cadical_analyze.cpp index 74b67fb06..d9351b80d 100644 --- a/src/sat/cadical/cadical_analyze.cpp +++ b/src/sat/cadical/cadical_analyze.cpp @@ -211,6 +211,7 @@ int Internal::recompute_glue (Clause *c) { int res = 0; const int64_t stamp = ++stats.recomputed; for (const auto &lit : *c) { + CADICAL_assert (val (lit)); int level = var (lit).level; CADICAL_assert (gtab[level] <= stamp); if (gtab[level] == stamp) @@ -535,7 +536,7 @@ Clause *Internal::new_driving_clause (const int glue, int &jump) { jump = var (clause[1]).level; res = new_learned_redundant_clause (glue); - res->used = 1 + (glue <= opts.reducetier2glue); + res->used = max_used; } LOG ("jump level %d", jump); @@ -912,7 +913,7 @@ void Internal::otfs_strengthen_clause (Clause *c, int lit, int new_size, mark_removed (lit); } mini_chain.clear (); - c->used = true; + c->used = max_used; LOG (c, "strengthened"); external->check_shrunken_clause (c); } @@ -1145,6 +1146,26 @@ void Internal::analyze () { uip = 0; while (!uip) { + if (!i) { + lazy_external_propagator_out_of_order_clause (uip); + if (unsat) + return; + else if (uip) { + open = 1; + break; + } else { + LOG (reason, "restarting the analysis on the new conflict"); + ++stats.conflicts; + reason = conflict; + resolvent_size = 0; + antecedent_size = 1; + resolved = 0; + open = 0; + analyze_reason (0, reason, open, resolvent_size, antecedent_size); + conflict_size = antecedent_size - 1; + CADICAL_assert (open > 1); + } + } CADICAL_assert (i > 0); const int lit = (*t)[--i]; if (!flags (lit).seen) @@ -1270,6 +1291,64 @@ void Internal::analyze () { recompute_tier (); } +// In the special case where the external propagator is lazy, the same +// invariants as OTFS break (but even more complicated). There are three +// possible cases: +// - the clause becomes empty (unsat must be answered) +// - the clause is a unit (backtrack and set the clause) +// - the clause is a new conflict on lower level and we restart the +// analysis +// +// TODO: we do not really need to keep the clause longer than the conflict +// analysis. +void Internal::lazy_external_propagator_out_of_order_clause (int &uip) { + CADICAL_assert (!opts.exteagerreasons); + CADICAL_assert (external_prop); + LOG (clause, "out-of-order conflict"); + if (clause.empty ()) { + LOG (lrat_chain, "lrat_chain:"); + LOG (clause, "clause:"); + LOG (unit_chain, "units:"); + if (lrat) { + LOG (unit_chain, "unit chain: "); + for (auto id : unit_chain) + lrat_chain.push_back (id); + unit_chain.clear (); + reverse (lrat_chain.begin (), lrat_chain.end ()); + } + LOG (lrat_chain, "lrat_chain:"); + learn_empty_clause (); + if (external->learner) + external->export_learned_empty_clause (); + conflict = 0; + } else if (clause.size () == 1) { + LOG ("found out-of-order unit"); + uip = -clause[0]; + CADICAL_assert (uip); + backtrack (var (uip).level); + CADICAL_assert (val (uip) > 0); + clause.clear (); + } else { + int jump; + const int glue = clause.size () - 1; + conflict = new_driving_clause (glue, jump); + UPDATE_AVERAGE (averages.current.level, jump); + backtrack (jump); + LOG (conflict, "new conflict"); + } + // Clean up. + // + clear_analyzed_literals (); + clear_unit_analyzed_literals (); + clear_analyzed_levels (); + clause.clear (); + + if (unsat) { + lrat_chain.clear (); + STOP (analyze); + } +} + // We wait reporting a learned unit until propagation of that unit is // completed. Otherwise the 'i' report gives the number of remaining // variables before propagating the unit (and hides the actual remaining diff --git a/src/sat/cadical/cadical_assume.cpp b/src/sat/cadical/cadical_assume.cpp index e7d5b79b7..d828bf4ab 100644 --- a/src/sat/cadical/cadical_assume.cpp +++ b/src/sat/cadical/cadical_assume.cpp @@ -11,7 +11,7 @@ namespace CaDiCaL { // adds an assumption literal onto the assumption stack. void Internal::assume (int lit) { - if (level && !opts.ilbassumptions) + if (level && !opts.ilb) backtrack (); else if (val (lit) < 0) backtrack (max (0, var (lit).level - 1)); @@ -552,9 +552,16 @@ struct sort_assumptions_smaller { // to the first place where the assumptions and the current trail differ. void Internal::sort_and_reuse_assumptions () { - CADICAL_assert (opts.ilbassumptions); - if (assumptions.empty ()) - return; + CADICAL_assert (opts.ilb >= 1); + if (assumptions.empty ()) { + if (opts.ilb == 1) { + LOG ("no assumptions, reusing nothing (ilb == 1)"); + backtrack (0); + } else { // reuse full trail + LOG ("no assumptions, reusing everything (ilb == 2)"); + return; + } + } MSORT (opts.radixsortlim, assumptions.begin (), assumptions.end (), sort_assumptions_positive_rank (this), sort_assumptions_smaller (this)); @@ -599,10 +606,12 @@ void Internal::sort_and_reuse_assumptions () { lit, alit); break; } + if (opts.ilb == 1 && + (size_t) target > assumptions.size ()) // reusing only assumptions + target = assumptions.size (); if (target < level) - backtrack (target); + backtrack_without_updating_phases (target); LOG ("assumptions allow for reuse of trail up to level %d", level); - // COVER (target > 1); if ((size_t) level > assumptions.size ()) stats.assumptionsreused += assumptions.size (); else diff --git a/src/sat/cadical/cadical_backbone.cpp b/src/sat/cadical/cadical_backbone.cpp new file mode 100644 index 000000000..d51841ec7 --- /dev/null +++ b/src/sat/cadical/cadical_backbone.cpp @@ -0,0 +1,637 @@ +#include "global.h" + +#include "internal.hpp" +#include "message.hpp" +#include "util.hpp" + +ABC_NAMESPACE_IMPL_START + +namespace CaDiCaL { + +inline void Internal::backbone_lrat_for_units (int lit, Clause *reason) { + if (!lrat) + return; + if (level) + return; // not decision level 0 + LOG ("backbone building chain for units"); + CADICAL_assert (lrat_chain.empty ()); + CADICAL_assert (reason); + for (auto &reason_lit : *reason) { + if (lit == reason_lit) + continue; + CADICAL_assert (val (reason_lit)); + if (!val (reason_lit)) + continue; + const int signed_reason_lit = val (reason_lit) * reason_lit; + int64_t id = unit_id (signed_reason_lit); + lrat_chain.push_back (id); + } + lrat_chain.push_back (reason->id); +} + +inline bool Internal::backbone_propagate (int64_t &ticks) { + require_mode (BACKBONE); + CADICAL_assert (!unsat); + START (propagate); + int64_t before = propagated2 = propagated; + for (;;) { + if (propagated2 != trail.size ()) { + const int lit = -trail[propagated2++]; + LOG ("backbone propagating %d over binary clauses", -lit); + Watches &ws = watches (lit); + ticks += + 1 + cache_lines (ws.size (), sizeof (const_watch_iterator *)); + for (const auto &w : ws) { + if (!w.binary ()) + continue; + const signed char b = val (w.blit); + if (b > 0) + continue; + if (b < 0) + conflict = w.clause; // but continue + else { + ticks++; + build_chain_for_units (w.blit, w.clause, 0); + backbone_assign (w.blit, w.clause); + lrat_chain.clear (); + } + } + } else if (!conflict && propagated != trail.size ()) { + const int lit = -trail[propagated++]; + LOG ("backbone propagating %d over large clauses", -lit); + Watches &ws = watches (lit); + const const_watch_iterator eow = ws.end (); + const_watch_iterator i = ws.begin (); + ticks += 1 + cache_lines (ws.size (), sizeof (*i)); + watch_iterator j = ws.begin (); + while (i != eow) { + const Watch w = *j++ = *i++; + if (w.binary ()) + continue; + if (val (w.blit) > 0) + continue; + ticks++; + if (w.clause->garbage) { + j--; + continue; + } + literal_iterator lits = w.clause->begin (); + const int other = lits[0] ^ lits[1] ^ lit; + const signed char u = val (other); + if (u > 0) + j[-1].blit = other; + else { + const int size = w.clause->size; + const const_literal_iterator end = lits + size; + const literal_iterator middle = lits + w.clause->pos; + literal_iterator k = middle; + signed char v = -1; + int r = 0; + while (k != end && (v = val (r = *k)) < 0) + k++; + if (v < 0) { + k = lits + 2; + CADICAL_assert (w.clause->pos <= size); + while (k != middle && (v = val (r = *k)) < 0) + k++; + } + w.clause->pos = k - lits; + CADICAL_assert (lits + 2 <= k), CADICAL_assert (k <= w.clause->end ()); + if (v > 0) + j[-1].blit = r; + else if (!v) { + LOG (w.clause, "unwatch %d in", r); + lits[0] = other; + lits[1] = r; + *k = lit; + ticks++; + watch_literal (r, lit, w.clause); + j--; + } else if (!u) { + ticks++; + CADICAL_assert (v < 0); + build_chain_for_units (other, w.clause, 0); + backbone_assign_any (other, w.clause); + lrat_chain.clear (); + } else { + if (w.clause == ignore) { + LOG ("ignoring conflict due to clause to vivify"); + continue; + } + CADICAL_assert (u < 0); + CADICAL_assert (v < 0); + conflict = w.clause; + break; + } + } + } + if (j != i) { + while (i != eow) + *j++ = *i++; + ws.resize (j - ws.begin ()); + } + } else + break; + } + int64_t delta = propagated2 - before; + stats.propagations.backbone += delta; + if (conflict) + LOG (conflict, "conflict"); + STOP (propagate); + return !conflict; +} + +inline void Internal::backbone_propagate2 (int64_t &ticks) { + require_mode (BACKBONE); + CADICAL_assert (propagated2 <= trail.size ()); + int64_t before = propagated2; + while (propagated2 != trail.size ()) { + const int lit = -trail[propagated2++]; + LOG ("probe propagating %d over binary clauses", -lit); + Watches &ws = watches (lit); + ticks += 1 + cache_lines (ws.size (), sizeof (const_watch_iterator *)); + for (const auto &w : ws) { + if (!w.binary ()) + break; + const signed char b = val (w.blit); + if (b > 0) + continue; + ticks++; + if (b < 0) { + conflict = w.clause; // no need to continue + break; + } else { + CADICAL_assert (lrat_chain.empty ()); + backbone_lrat_for_units (w.blit, w.clause); + backbone_assign (w.blit, w.clause); + lrat_chain.clear (); + } + } + } + + int64_t delta = propagated2 - before; + stats.propagations.backbone += delta; +} + +void Internal::schedule_backbone_cands (std::vector &candidates) { + + unsigned not_rescheduled = 0; + for (auto v : vars) { + const Flags f = flags (v); + if (!f.active ()) + continue; + if (f.backbone0) { + LOG ("scheduling backbone literal candidate %s", LOGLIT (v)); + candidates.push_back (v); + } else + ++not_rescheduled; + if (f.backbone1) { + LOG ("scheduling backbone literal candidate %s", LOGLIT (-v)); + candidates.push_back (-v); + } else + ++not_rescheduled; + } + + if (not_rescheduled) { + for (auto v : vars) { + const Flags f = flags (v); + if (!f.active ()) + continue; + if (!f.backbone0) { + LOG ("scheduling backbone literal candidate %s", LOGLIT (v)); + candidates.push_back (v); + } + if (!f.backbone1) { + LOG ("scheduling backbone literal candidate %s", LOGLIT (-v)); + candidates.push_back (-v); + } + } + } + CADICAL_assert (candidates.size () <= 2 * (size_t) max_var); + + VERBOSE (3, + "backbone schedule %zu backbone candidates in total %f " + "(rescheduled: %f%%)", + candidates.size (), percent (candidates.size (), 2 * max_var), + percent (not_rescheduled, 2 * max_var)); +} + +int Internal::backbone_analyze (Clause *, int64_t &ticks) { + CADICAL_assert (conflict); + CADICAL_assert (conflict->size == 2); + analyzed.push_back (std::abs (conflict->literals[0])); + flags (conflict->literals[0]).seen = true; + analyzed.push_back (std::abs (conflict->literals[1])); + flags (conflict->literals[1]).seen = true; + LOG (conflict, "analyzing conflict"); + if (lrat) + lrat_chain.push_back (conflict->id); + conflict = nullptr; + + for (auto t = trail.rbegin ();;) { + CADICAL_assert (t < trail.rend ()); + int lit = *t++; + LOG ("analyzing %s", LOGLIT (lit)); + if (!flags (lit).seen) + continue; + Clause *reason = var (lit).reason; + LOG (reason, "resolving with reason of %s", LOGLIT (lit)); + CADICAL_assert (reason), CADICAL_assert (reason != decision_reason); + ++ticks; + const int other = reason->literals[0] ^ reason->literals[1] ^ lit; + Flags &f_o = flags (other); + if (lrat) + lrat_chain.push_back (reason->id); + if (!f_o.seen) { + f_o.seen = true; + analyzed.push_back (other); + } else { + LOG ("backbone UIP %s", LOGLIT (other)); + for (auto lit : analyzed) + flags (lit).seen = false; + analyzed.clear (); + if (lrat) + reverse (begin (lrat_chain), end (lrat_chain)); + return other; + } + } +} + +inline void Internal::backbone_unit_reassign (int lit) { +#ifdef LOGGING + LOG ("reassigning %s to level 0", LOGLIT (lit)); + CADICAL_assert (val (lit) > 0); + CADICAL_assert (val (-lit) < 0); +#else + (void) lit; +#endif + return; +} + +inline void Internal::backbone_unit_assign (int lit) { + LOG ("assigning %s to level 0", LOGLIT (lit)); + require_mode (BACKBONE); + const int idx = vidx (lit); + CADICAL_assert (!vals[idx]); + Var &v = var (idx); + v.level = 0; // required to reuse decisions + v.trail = (int) trail.size (); // used in 'vivify_better_watch' + CADICAL_assert ((int) num_assigned < max_var); + num_assigned++; + v.reason = 0; // for conflict analysis + learn_unit_clause (lit); + lrat_chain.clear (); + const signed char tmp = sign (lit); + vals[idx] = tmp; + vals[-idx] = -tmp; + CADICAL_assert (val (lit) > 0); + CADICAL_assert (val (-lit) < 0); + trail.push_back (lit); + LOG ("backbone assign %d to level 0", lit); +} + +inline void Internal::backbone_assign_any (int lit, Clause *reason) { + require_mode (BACKBONE); + const int idx = vidx (lit); + CADICAL_assert (!vals[idx]); + CADICAL_assert (!flags (idx).eliminated () || !reason); + CADICAL_assert (reason == decision_reason || !reason || reason->size >= 2); + Var &v = var (idx); + v.level = level; // required to reuse decisions + v.trail = (int) trail.size (); // used in 'vivify_better_watch' + CADICAL_assert ((int) num_assigned < max_var); + num_assigned++; + v.reason = level ? reason : 0; // for conflict analysis + if (!level) + learn_unit_clause (lit); + const signed char tmp = sign (lit); + vals[idx] = tmp; + vals[-idx] = -tmp; + CADICAL_assert (val (lit) > 0); + CADICAL_assert (val (-lit) < 0); + trail.push_back (lit); + LOG (reason, "backbone assign %d", lit); +} + +inline void Internal::backbone_assign (int lit, Clause *reason) { + require_mode (BACKBONE); + const int idx = vidx (lit); + CADICAL_assert (!vals[idx]); + CADICAL_assert (!flags (idx).eliminated () || !reason); + CADICAL_assert (reason == decision_reason || !reason || reason->size == 2); + Var &v = var (idx); + v.level = level; // required to reuse decisions + v.trail = (int) trail.size (); // used in 'vivify_better_watch' + CADICAL_assert ((int) num_assigned < max_var); + num_assigned++; + v.reason = level ? reason : 0; // for conflict analysis + if (!level) + learn_unit_clause (lit); + const signed char tmp = sign (lit); + vals[idx] = tmp; + vals[-idx] = -tmp; + CADICAL_assert (val (lit) > 0); + CADICAL_assert (val (-lit) < 0); + trail.push_back (lit); + LOG (reason, "backbone assign %d", lit); +} + +void Internal::backbone_decision (int lit) { + require_mode (BACKBONE); + CADICAL_assert (propagated2 == trail.size ()); + new_trail_level (lit); + notify_decision (); + LOG ("search decide %d", lit); + backbone_assign (lit, decision_reason); +} + +unsigned Internal::compute_backbone_round (std::vector &candidates, + std::vector &units, + const int64_t ticks_limit, + int64_t &ticks, + unsigned inconsistent) { + CADICAL_assert (!conflict); + auto p = begin (candidates); + auto q = p; + const auto end = std::end (candidates); + size_t failed = 0; + ++stats.backbone.rounds; + + LOG (candidates, "candidates: "); + ticks += 1 + cache_lines (candidates.size (), + sizeof (std::vector::iterator *)); + while (p != end) { + CADICAL_assert (p < end); + CADICAL_assert (q <= p); + CADICAL_assert (!conflict); + const int probe = (*q = *p); + ++stats.backbone.probes; + + ++p, ++q; + const signed char v = val (probe); + if (v > 0) { + q--; + LOG ("removing satisfied backbone probe %s", LOGLIT (probe)); + if (probe < 0) + flags (probe).backbone1 = false; + else + flags (probe).backbone0 = false; + continue; + } + + if (v < 0) { + if (var (probe).level) + LOG ("skipping falsified backbone probe %s", LOGLIT (probe)); + else { + LOG ("removing root-level falsified backbone probe %s", + LOGLIT (probe)); + q--; + } + continue; + } + if (ticks >= ticks_limit) + break; + backbone_decision (probe); + backbone_propagate2 (ticks); + if (!conflict) { + LOG (candidates, + "propagating backbone probe %s successful; candidates:", + LOGLIT (probe)); + continue; + } + + ++failed; + ++stats.backbone.units; + int uip = backbone_analyze (conflict, ticks); + backtrack_without_updating_phases (level - 1); + backbone_unit_assign (uip); + ++stats.units; + CADICAL_assert (!conflict); + if (external->learner) + external->export_learned_unit_clause (uip); + + backbone_propagate2 (ticks); + if (conflict) { + LOG ("propagating backbone forced %s failed", LOGLIT (uip)); + inconsistent = uip; + // we have to give up on the current conflict + conflict = nullptr; + break; + } + units.push_back (uip); + } + while (p != end) + *q++ = *p++; + CADICAL_assert (q <= end); + candidates.resize (q - begin (candidates)); + LOG (candidates, "candidates: "); + + if (!inconsistent) { + LOG ("flushing satisfied probe candidates"); + auto p = begin (candidates); + auto q = p; + const auto end = std::end (candidates); + + while (p != end) { + const int probe = (*q++ = *p++); + const signed char v = val (probe); + if (v > 0) { + q--; + LOG ("removing satisfied backbone probe %s", LOGLIT (probe)); + if (probe < 0) + flags (probe).backbone1 = false; + else + flags (probe).backbone0 = false; + continue; + } + + if (v < 0) { + LOG ("keeping falsified probe %s", LOGLIT (probe)); + continue; + } + CADICAL_assert (!v); + LOG ("keeping unassigned probe %s", LOGLIT (probe)); + } + candidates.resize (q - begin (candidates)); + } + LOG (candidates, "candidates after !inconsistent: "); + if (level) + backtrack_without_updating_phases (); + if (!inconsistent && !units.empty ()) { + for (auto l : units) { + backbone_unit_reassign (l); + } + units.clear (); + if (!backbone_propagate (ticks)) { + LOG (conflict, "final repropagation yielded conflict"); + learn_empty_clause (); + } + } else { + if (!backbone_propagate (ticks)) { + learn_empty_clause (); + } + } + LOG (candidates, "candidates end of loop: "); + + LOG (candidates, "candidates end of backbone_round: "); + return failed; +} + +void Internal::keep_backbone_candidates ( + const std::vector &candidates) { + size_t remain = 0; + size_t prioritized = 0; + for (auto v : candidates) { + const Flags &f = flags (v); + if (!f.active ()) + continue; + ++remain; + if (v < 0) + prioritized += f.backbone1; + else + prioritized += f.backbone0; + } + CADICAL_assert (prioritized <= remain); + if (!remain) { +#ifndef CADICAL_NDEBUG + for (auto v : candidates) { + const Flags &f = flags (v); + if (!f.active ()) + continue; + CADICAL_assert (!f.backbone0); + CADICAL_assert (!f.backbone1); + } +#endif + return; + } + if (prioritized == remain) { + LOG ("keeping all remaining backbones"); + } else if (!prioritized) { + for (auto v : candidates) { + Flags &f = flags (v); + if (!f.active ()) + continue; + ++remain; + if (v < 0) { + CADICAL_assert (!f.backbone1); + f.backbone1 = true; + } else { + CADICAL_assert (!f.backbone0); + f.backbone0 = true; + } + } + } +} + +unsigned Internal::compute_backbone () { + size_t failed = 0; + + int64_t ticks = 0; + backbone_propagate2 (ticks); + CADICAL_assert (!conflict); + + std::vector candidates, units; + unsigned inconsistent = 0; + CADICAL_assert (!conflict); + + ++stats.backbone.phases; + schedule_backbone_cands (candidates); + + const size_t max_rounds = opts.backbonemaxrounds; + size_t round_limit = opts.backbonerounds; + round_limit *= stats.backbone.phases; + if (round_limit > max_rounds) + round_limit = max_rounds; + + SET_EFFORT_LIMIT (totalticks, backbone, false); + int64_t ticks_limit = totalticks - stats.ticks.backbone; + PHASE ("backbone", stats.backbone.phases, + "backbone limit of %" PRId64 " ticks", ticks_limit); + size_t rounds = 0; + for (; ++rounds;) { + if (rounds >= round_limit) { + LOG ("backround round limit %zu rounds", rounds); + break; + } + if (ticks >= ticks_limit) { + LOG ("backround round limit %" PRIu64 " ticks", ticks); + break; + } + VERBOSE (3, + "backbone round %zu of %zu with %" PRId64 + " ticks (%f %% done) with %zu failed so far", + rounds, max_rounds, ticks, percent (ticks, ticks_limit), + failed); + size_t new_failed = compute_backbone_round ( + candidates, units, ticks_limit, ticks, inconsistent); + failed += new_failed; + if (inconsistent) + break; + if (candidates.empty ()) + break; + if (unsat) + break; + } + + if (inconsistent && !unsat) { + LOG ("using forced unit %s by repropagating at level 0", + LOGLIT (inconsistent)); + backtrack_without_updating_phases (); + propagate (); + learn_empty_clause (); + } + if (unsat) { + PHASE ("backbone", stats.backbone.phases, + "inconsistent binary clauses"); + } else { + PHASE ("backbone", stats.backbone.phases, + "found %zu backbone literals %zu round in %" PRId64 " ticks", + failed, rounds, ticks); + } + + keep_backbone_candidates (candidates); + if (level) { + backtrack_without_updating_phases (); + if (!backbone_propagate (ticks)) { + learn_empty_clause (); + } + } + stats.ticks.backbone += ticks; + return failed; +} + +void Internal::binary_clauses_backbone () { + if (unsat) + return; + if (!opts.backbone) + return; + if (level) + backtrack_without_updating_phases (); + propagated2 = 0; // TODO: why? + if (!propagate ()) { + LOG ("propagation after connecting watches in inconsistency"); + learn_empty_clause (); + return; + } + + for (auto lit : lits) { + Watches &w = watches (lit); + std::stable_partition (begin (w), end (w), + [] (Watch w) { return w.binary (); }); + } + CADICAL_assert (propagated2 <= trail.size ()); + private_steps = true; + + CADICAL_assert (watching ()); + START_SIMPLIFIER (backbone, BACKBONE); + int failed = compute_backbone (); + CADICAL_assert (!level); + private_steps = false; + + report ('k', !failed); + STOP_SIMPLIFIER (backbone, BACKBONE); +} + +} // namespace CaDiCaL + +ABC_NAMESPACE_IMPL_END diff --git a/src/sat/cadical/cadical_backtrack.cpp b/src/sat/cadical/cadical_backtrack.cpp index b3c519ba2..8d182dc81 100644 --- a/src/sat/cadical/cadical_backtrack.cpp +++ b/src/sat/cadical/cadical_backtrack.cpp @@ -49,6 +49,9 @@ inline void Internal::unassign (int lit) { void Internal::update_target_and_best () { + if (opts.rephase == 2 && !stable) + return; + bool reset = (rephased && stats.conflicts > last.rephase.conflicts); if (reset) { @@ -131,7 +134,8 @@ void Internal::backtrack_without_updating_phases (int new_level) { // backtracking. It is possible to just keep out-of-order assigned // literals on the trail without breaking the solver (after some // modifications to 'analyze' - see 'opts.chrono' guarded code there). - CADICAL_assert (opts.chrono || external_prop || did_external_prop); + CADICAL_assert ((in_mode (BACKBONE)) || opts.chrono || external_prop || + did_external_prop); #ifdef LOGGING if (!v.level) LOG ("reassign %d @ 0 unit clause %d", lit, lit); diff --git a/src/sat/cadical/cadical_ccadical.cpp b/src/sat/cadical/cadical_ccadical.cpp index ccc616c80..3b61a0373 100644 --- a/src/sat/cadical/cadical_ccadical.cpp +++ b/src/sat/cadical/cadical_ccadical.cpp @@ -196,12 +196,24 @@ int ccadical_vars (CCaDiCaL *ptr) { return ((Wrapper *) ptr)->solver->vars (); } -int ccadical_reserve_difference (CCaDiCaL *ptr, int number_of_vars) { - return ((Wrapper *) ptr)->solver->reserve_difference (number_of_vars); +int ccadical_declare_more_variables (CCaDiCaL *ptr, int number_of_vars) { + return ((Wrapper *) ptr)->solver->declare_more_variables (number_of_vars); } -void ccadical_reserve(CCaDiCaL *ptr, int min_max_var) { - ((Wrapper *) ptr)->solver->reserve(min_max_var); +int ccadical_declare_one_more_variable (CCaDiCaL *ptr) { + return ((Wrapper *) ptr)->solver->declare_one_more_variable (); +} + +void ccadical_phase (CCaDiCaL *wrapper, int lit) { + ((Wrapper *) wrapper)->solver->phase (lit); +} + +void ccadical_unphase (CCaDiCaL *wrapper, int lit) { + ((Wrapper *) wrapper)->solver->unphase (lit); +} + +void ccadical_resize(CCaDiCaL *ptr, int min_max_var) { + ((Wrapper *) ptr)->solver->resize(min_max_var); } int ccadical_is_inconsistent(CCaDiCaL *ptr) { diff --git a/src/sat/cadical/cadical_checker.cpp b/src/sat/cadical/cadical_checker.cpp index 63a5f2b10..1081f937c 100644 --- a/src/sat/cadical/cadical_checker.cpp +++ b/src/sat/cadical/cadical_checker.cpp @@ -561,7 +561,8 @@ void Checker::add_original_clause (int64_t id, bool, const vector &c, STOP (checking); } -void Checker::add_derived_clause (int64_t id, bool, const vector &c, +void Checker::add_derived_clause (int64_t id, bool, int, + const vector &c, const vector &) { if (inconsistent) return; @@ -627,7 +628,7 @@ void Checker::delete_clause (int64_t id, bool, const vector &c) { void Checker::add_assumption_clause (int64_t id, const vector &c, const vector &chain) { - add_derived_clause (id, true, c, chain); + add_derived_clause (id, true, 0, c, chain); delete_clause (id, true, c); } diff --git a/src/sat/cadical/cadical_clause.cpp b/src/sat/cadical/cadical_clause.cpp index 1c84994a6..481cf4ee6 100644 --- a/src/sat/cadical/cadical_clause.cpp +++ b/src/sat/cadical/cadical_clause.cpp @@ -66,7 +66,7 @@ inline void Internal::mark_added (int lit, int size, bool redundant) { mark_ternary (lit); if (!redundant) mark_block (lit); - if (!redundant || size == 2) + if ((!redundant || size == 2)) mark_factor (lit); } @@ -151,6 +151,7 @@ Clause *Internal::new_clause (bool red, int glue) { void Internal::promote_clause (Clause *c, int new_glue) { CADICAL_assert (c->redundant); + CADICAL_assert (new_glue); const int tier1limit = tier1[false]; const int tier2limit = max (tier1limit, tier2[false]); if (!c->redundant) @@ -160,10 +161,10 @@ void Internal::promote_clause (Clause *c, int new_glue) { int old_glue = c->glue; if (new_glue >= old_glue) return; + c->used = max_used; if (old_glue > tier1limit && new_glue <= tier1limit) { LOG (c, "promoting with new glue %d to tier1", new_glue); stats.promoted1++; - c->used = max_used; } else if (old_glue > tier2limit && new_glue <= tier2limit) { LOG (c, "promoting with new glue %d to tier2", new_glue); stats.promoted2++; @@ -178,6 +179,7 @@ void Internal::promote_clause (Clause *c, int new_glue) { void Internal::promote_clause_glue_only (Clause *c, int new_glue) { CADICAL_assert (c->redundant); + CADICAL_assert (new_glue); if (c->hyper) return; int old_glue = c->glue; @@ -188,7 +190,6 @@ void Internal::promote_clause_glue_only (Clause *c, int new_glue) { if (new_glue <= tier1limit) { LOG (c, "promoting with new glue %d to tier1", new_glue); stats.promoted1++; - c->used = max_used; } else if (old_glue > tier2limit && new_glue <= tier2limit) { LOG (c, "promoting with new glue %d to tier2", new_glue); stats.promoted2++; @@ -582,13 +583,17 @@ Clause *Internal::new_hyper_ternary_resolved_clause (bool red) { return res; } -Clause *Internal::new_factor_clause () { +Clause *Internal::new_factor_clause (int witness) { external->check_learned_clause (); stats.factor_added++; stats.literals_factored += clause.size (); Clause *res = new_clause (false, 0); if (proof) { - proof->add_derived_clause (res, lrat_chain); + if (witness) + proof->add_derived_rat_clause (res, externalize (witness), + lrat_chain); + else + proof->add_derived_clause (res, lrat_chain); } CADICAL_assert (!watching ()); CADICAL_assert (occurring ()); @@ -644,6 +649,48 @@ Clause *Internal::new_resolved_irredundant_clause () { return res; } +void Internal::decay_clauses_upon_incremental_clauses () { + if (!opts.incdecay) + return; + if (!stats.searches) + return; + if (stats.conflicts < lim.incremental_decay) + return; + + PHASE ("decay", stats.incremental_decay, + "decaying clauses with next decaying at conflict %" PRId64 + "(after the next incremental call)", + lim.incremental_decay); + + for (auto c : clauses) { + if (c->garbage) + continue; + if (!c->redundant) + continue; + if (c->id >= last.incremental_decay.last_id) + continue; + switch (opts.incdecay) { + case 1: // my intuition + ++c->glue; + break; + case 2: // Armin's idea + if (c->glue < tier1[false]) + c->used = 1; + break; + case 3: + if (c->glue < tier1[false]) + c->used = 1; + ++c->glue; + break; + default: + break; + } + } + + lim.incremental_decay += stats.conflicts + opts.incdecayint; + ++stats.incremental_decay; + last.incremental_decay.last_id = clause_id; +} } // namespace CaDiCaL ABC_NAMESPACE_IMPL_END diff --git a/src/sat/cadical/cadical_compact.cpp b/src/sat/cadical/cadical_compact.cpp index 6747218cb..a46f333c3 100644 --- a/src/sat/cadical/cadical_compact.cpp +++ b/src/sat/cadical/cadical_compact.cpp @@ -354,7 +354,6 @@ void Internal::compact () { mapper.map_vector (phases.target); mapper.map_vector (phases.best); mapper.map_vector (phases.prev); - mapper.map_vector (phases.min); // Special code for 'frozentab'. // diff --git a/src/sat/cadical/cadical_congruence.cpp b/src/sat/cadical/cadical_congruence.cpp index dd5f30b7a..c31a769b5 100644 --- a/src/sat/cadical/cadical_congruence.cpp +++ b/src/sat/cadical/cadical_congruence.cpp @@ -2,6 +2,7 @@ #include "congruence.hpp" #include "internal.hpp" +#include "util.hpp" #include #include #include @@ -19,36 +20,68 @@ Closure::Closure (Internal *i) fresh_id (internal->clause_id) #endif { + dummy_search_gate = new Gate (); + dummy_search_gate->lhs = 0; + dummy_search_gate->garbage = false; } char &Closure::lazy_propagated (int lit) { return lazy_propagated_idx[internal->vidx (lit)]; } +std::string special_gate_str (int8_t f) { + switch (f) { + case NORMAL: + return ""; + case NO_PLUS_ELSE: + return "{no +e}"; + case NO_PLUS_THEN: + return "{no +t}"; + case NO_NEG_ELSE: + return "{no -e}"; + case NO_NEG_THEN: + return "{no -t}"; + case NO_ELSE: + return "{no e}"; + case NO_THEN: + return "{no t}"; + case COND_LHS: + return "{no -t +e}"; + case UCOND_LHS: + return "{no +t -e}"; + case DEGENERATED_AND: + return "{AND/bin}"; + case DEGENERATED_AND_LHS_FALSE: + return "{AND/bin;lhs=-1}"; + default: + CADICAL_assert (false); + return "BROKEN GATE"; + } +} void update_ite_flags (Gate *g) { - int8_t f = g->degenerated_ite; + int8_t f = g->degenerated_gate; const int lhs = g->lhs; - const int cond = g->rhs [0]; + const int cond = g->rhs[0]; const int then_lit = g->rhs[1]; const int else_lit = g->rhs[2]; if (lhs == cond) { - f |= Special_ITE_GATE::NO_NEG_THEN; - f |= Special_ITE_GATE::NO_PLUS_ELSE; + f |= Special_Gate::NO_NEG_THEN; + f |= Special_Gate::NO_PLUS_ELSE; } if (lhs == -cond) { - f |= Special_ITE_GATE::NO_PLUS_THEN; - f |= Special_ITE_GATE::NO_NEG_ELSE; + f |= Special_Gate::NO_PLUS_THEN; + f |= Special_Gate::NO_NEG_ELSE; } if (lhs == then_lit) { - f |= Special_ITE_GATE::NO_PLUS_THEN; - f |= Special_ITE_GATE::NO_NEG_THEN; + f |= Special_Gate::NO_PLUS_THEN; + f |= Special_Gate::NO_NEG_THEN; } if (lhs == else_lit) { - f |= Special_ITE_GATE::NO_PLUS_ELSE; - f |= Special_ITE_GATE::NO_NEG_ELSE; + f |= Special_Gate::NO_PLUS_ELSE; + f |= Special_Gate::NO_NEG_ELSE; } - g->degenerated_ite = f; + g->degenerated_gate = f; CADICAL_assert (lhs != -then_lit); CADICAL_assert (lhs != -else_lit); CADICAL_assert (cond != then_lit); @@ -59,35 +92,35 @@ void update_ite_flags (Gate *g) { void check_correct_ite_flags (const Gate *const g) { #ifndef CADICAL_NDEBUG - const int8_t f = g->degenerated_ite; + const int8_t f = g->degenerated_gate; const int lhs = g->lhs; - const int cond = g->rhs [0]; + const int cond = g->rhs[0]; const int then_lit = g->rhs[1]; const int else_lit = g->rhs[2]; CADICAL_assert (g->pos_lhs_ids.size () == 4); if (g->pos_lhs_ids[0].clause == nullptr) - CADICAL_assert ((f & Special_ITE_GATE::NO_PLUS_THEN)); + CADICAL_assert ((f & Special_Gate::NO_PLUS_THEN)); if (g->pos_lhs_ids[1].clause == nullptr) - CADICAL_assert (f & Special_ITE_GATE::NO_NEG_THEN); + CADICAL_assert (f & Special_Gate::NO_NEG_THEN); if (g->pos_lhs_ids[2].clause == nullptr) - CADICAL_assert (f & Special_ITE_GATE::NO_PLUS_ELSE); + CADICAL_assert (f & Special_Gate::NO_PLUS_ELSE); if (g->pos_lhs_ids[3].clause == nullptr) - CADICAL_assert (f & Special_ITE_GATE::NO_NEG_ELSE); + CADICAL_assert (f & Special_Gate::NO_NEG_ELSE); if (lhs == cond) { - CADICAL_assert (f & Special_ITE_GATE::NO_NEG_THEN); - CADICAL_assert (f & Special_ITE_GATE::NO_PLUS_ELSE); + CADICAL_assert (f & Special_Gate::NO_NEG_THEN); + CADICAL_assert (f & Special_Gate::NO_PLUS_ELSE); } if (lhs == -cond) { - CADICAL_assert (f & Special_ITE_GATE::NO_PLUS_THEN); - CADICAL_assert (f & Special_ITE_GATE::NO_NEG_ELSE); + CADICAL_assert (f & Special_Gate::NO_PLUS_THEN); + CADICAL_assert (f & Special_Gate::NO_NEG_ELSE); } if (lhs == then_lit) { - CADICAL_assert (f & Special_ITE_GATE::NO_PLUS_THEN); - CADICAL_assert (f & Special_ITE_GATE::NO_NEG_THEN); + CADICAL_assert (f & Special_Gate::NO_PLUS_THEN); + CADICAL_assert (f & Special_Gate::NO_NEG_THEN); } if (lhs == else_lit) { - CADICAL_assert (f & Special_ITE_GATE::NO_PLUS_ELSE); - CADICAL_assert (f & Special_ITE_GATE::NO_NEG_ELSE); + CADICAL_assert (f & Special_Gate::NO_PLUS_ELSE); + CADICAL_assert (f & Special_Gate::NO_NEG_ELSE); } CADICAL_assert (lhs != -then_lit); CADICAL_assert (lhs != -else_lit); @@ -96,19 +129,20 @@ void check_correct_ite_flags (const Gate *const g) { CADICAL_assert (cond != -then_lit); CADICAL_assert (cond != -else_lit); #else - (void)g; + (void) g; #endif } /*------------------------------------------------------------------------*/ -static size_t hash_lits (std::array &nonces, - const vector &lits) { - size_t hash = 0; - const auto end_nonces = end (nonces); - const auto begin_nonces = begin (nonces); - auto n = begin_nonces; - for (auto lit : lits) { +static inline size_t hash_lits (const std::array &nonces, + const vector &lits, Gate_Type tag) { + uint64_t hash = ((uint64_t) tag << 4) | ((uint64_t) tag >> 50); + const auto end_nonces = std::end (nonces); + const auto begin_nonces = std::begin (nonces); + auto n = begin_nonces + (uint64_t) tag; + CADICAL_assert (n < end_nonces); + for (int lit : lits) { hash += lit; hash *= *n++; hash = (hash << 4) | (hash >> 60); @@ -116,12 +150,11 @@ static size_t hash_lits (std::array &nonces, n = begin_nonces; } hash ^= hash >> 32; - return hash; + return (size_t) hash; } -size_t Hash::operator() (const Gate *const g) const { - CADICAL_assert (hash_lits (nonces, g->rhs) == g->hash); - return g->hash; +inline size_t Hash::operator() (const Gate *const g) const { + return hash_lits (nonces, g->rhs, g->tag); } bool gate_contains (Gate *g, int lit) { @@ -129,19 +162,27 @@ bool gate_contains (Gate *g, int lit) { } /*------------------------------------------------------------------------*/ struct compact_binary_rank { + CaDiCaL::Internal *internal; typedef uint64_t Type; + compact_binary_rank (Internal *i) : internal (i) {} uint64_t operator() (const CompactBinary &a) { - return ((uint64_t) a.lit1 << 32) + a.lit2; + return ((uint64_t) internal->vlit (a.lit1) << 32) + + internal->vlit (a.lit2); }; }; struct compact_binary_order { + CaDiCaL::Internal *internal; + compact_binary_order (Internal *i) : internal (i) {} bool operator() (const CompactBinary &a, const CompactBinary &b) { - return compact_binary_rank () (a) < compact_binary_rank () (b); + return compact_binary_rank (internal) (a) < + compact_binary_rank (internal) (b); }; }; bool Closure::find_binary (int lit, int other) const { + if (internal->vlit (lit) > internal->vlit (other)) + swap (lit, other); const auto offsize = offsetsize[internal->vlit (lit)]; // in C++17: [offset, size] = const auto offset = offsize.first; @@ -150,11 +191,12 @@ bool Closure::find_binary (int lit, int other) const { const auto end = std::begin (binaries) + size; CADICAL_assert (end <= std::end (binaries)); const CompactBinary target = CompactBinary (nullptr, 0, lit, other); - auto it = std::lower_bound (begin, end, target, compact_binary_order ()); + auto it = std::lower_bound (begin, end, target, + compact_binary_order (internal)); // search_binary only returns a bool bool found = (it != end && it->lit1 == lit && it->lit2 == other); if (found) { - LOG ("found binary [%zd] %d %d", it->id, lit, other); + LOG ("found binary [%" PRId64 "] %d %d", it->id, lit, other); if (internal->lrat) lrat_chain.push_back (it->id); } @@ -187,7 +229,7 @@ void Closure::extract_binaries () { } MSORT (internal->opts.radixsortlim, begin (binaries), end (binaries), - compact_binary_rank (), compact_binary_order ()); + compact_binary_rank (internal), compact_binary_order (internal)); { const size_t size = binaries.size (); @@ -207,6 +249,7 @@ void Closure::extract_binaries () { } size_t extracted = 0, already_present = 0, duplicated = 0; + (void) extracted, (void) already_present, (void) duplicated; const size_t size = internal->clauses.size (); for (size_t i = 0; i < size; ++i) { @@ -221,7 +264,8 @@ void Closure::extract_binaries () { const int *lits = d->literals; const int a = lits[0]; const int b = lits[1]; - const int c = lits[2]; // obfuscating d->literals[2] which triggers an error in pedandic mode + const int c = lits[2]; // obfuscating d->literals[2] which triggers an + // error in pedandic mode if (internal->val (a)) continue; if (internal->val (b)) @@ -256,7 +300,7 @@ void Closure::extract_binaries () { // kissat has code to remove duplicates, which we have already removed // before starting congruence MSORT (internal->opts.radixsortlim, begin (binaries), end (binaries), - compact_binary_rank (), compact_binary_order ()); + compact_binary_rank (internal), compact_binary_order (internal)); const size_t new_size = binaries.size (); { size_t i = 0; @@ -277,9 +321,13 @@ void Closure::extract_binaries () { } binaries.clear (); STOP (extractbinaries); - LOG ("extracted %zu binaries (plus %zu already present and %zu " - "duplicates)", - extracted, already_present, duplicated); + VERBOSE (2, + "[congruence-%" PRId64 + "] extracted %zu binaries (plus %zu already " + "present and %zu " + "duplicates)", + internal->stats.congruence.rounds, extracted, already_present, + duplicated); } /*------------------------------------------------------------------------*/ @@ -299,7 +347,7 @@ void Closure::unmark_all () { void Closure::set_mu1_reason (int lit, Clause *c) { CADICAL_assert (marked (lit) & 1); - LOG (c, "mu1 %d -> %zd", lit, c->id); + LOG (c, "mu1 %d -> %" PRId64, lit, c->id); mu1_ids[internal->vlit (lit)] = LitClausePair (lit, c); } @@ -307,7 +355,7 @@ void Closure::set_mu2_reason (int lit, Clause *c) { CADICAL_assert (marked (lit) & 2); if (!internal->lrat) return; - LOG (c, "mu2 %d -> %zd", lit, c->id); + LOG (c, "mu2 %d -> %" PRId64, lit, c->id); mu2_ids[internal->vlit (lit)] = LitClausePair (lit, c); } @@ -315,7 +363,7 @@ void Closure::set_mu4_reason (int lit, Clause *c) { CADICAL_assert (marked (lit) & 4); if (!internal->lrat) return; - LOG (c, "mu4 %d -> %zd", lit, c->id); + LOG (c, "mu4 %d -> %" PRId64, lit, c->id); mu4_ids[internal->vlit (lit)] = LitClausePair (lit, c); } @@ -448,6 +496,29 @@ int Closure::find_representative (int lit) { return res; } +int Closure::find_representative_and_compress_no_proofs (int lit) { + int res = lit; + int nxt = lit; + do { + res = nxt; + nxt = representative (nxt); + } while (nxt != res); + + representative (lit) = res; + representative (-lit) = -res; + + return res; +} + +int Closure::find_representative_already_compressed (int lit) { + int res = lit; + res = representative (lit); + + CADICAL_assert (representative (res) == res); + + return res; +} + int Closure::find_representative_and_compress (int lit, bool update_eager) { LOG ("finding representative of %d", lit); int res = lit; @@ -684,17 +755,21 @@ void Closure::index_gate (Gate *g) { CADICAL_assert (!g->indexed); CADICAL_assert (!internal->unsat); CADICAL_assert (g->arity () > 1); - CADICAL_assert (g->hash == hash_lits (nonces, g->rhs)); LOG (g, "adding to hash table"); table.insert (g); g->indexed = true; + CADICAL_assert (table.find (g) != table.end ()); } void Closure::produce_rewritten_clause_lrat_and_clean ( - std::vector &litIds, int except_lhs, bool remove_units) { + std::vector &litIds, int except_lhs, bool remove_units, + bool allow_tautology_in_input) { CADICAL_assert (internal->lrat_chain.empty ()); for (auto &litId : litIds) { - CADICAL_assert (litId.clause); + CADICAL_assert (litId.clause || allow_tautology_in_input); + (void) allow_tautology_in_input; + if (!litId.clause) + continue; litId.clause = produce_rewritten_clause_lrat (litId.clause, except_lhs, remove_units); litId.current_lit = find_eager_representative (litId.current_lit); @@ -705,6 +780,20 @@ void Closure::produce_rewritten_clause_lrat_and_clean ( end (litIds)); } +void Closure::produce_rewritten_clause_lrat_and_clean ( + my_dummy_optional &litIds, int except_lhs, bool remove_units) { + CADICAL_assert (internal->lrat_chain.empty ()); + if (litIds ()) { + auto &litId = litIds.content; + CADICAL_assert (litId.clause); + litId.clause = produce_rewritten_clause_lrat (litId.clause, except_lhs, + remove_units); + litId.current_lit = find_eager_representative (litId.current_lit); + if (!litId.clause) + litIds.reset (); + } +} + void Closure::produce_rewritten_clause_lrat_and_clean ( std::vector &litIds, int except_lhs, size_t &old_position1, size_t &old_position2, bool remove_units) { @@ -713,6 +802,14 @@ void Closure::produce_rewritten_clause_lrat_and_clean ( size_t j = 0; for (size_t i = 0; i < litIds.size (); ++i) { CADICAL_assert (j <= i); + if (!litIds[i].clause) { + LOG ("empty clause %zd", i); + if (i == old_position1) + old_position1 = -1; + if (i == old_position2) + old_position2 = -1; + continue; + } litIds[j].clause = produce_rewritten_clause_lrat ( litIds[i].clause, except_lhs, remove_units); litIds[j].current_lit = @@ -757,15 +854,11 @@ void Closure::compute_rewritten_clause_lrat_simple (Clause *c, int except) { continue; } if (internal->val (lit) < 0) { -#if 1 LOG ("found unit %d, removing it", -lit); LRAT_ID id = internal->unit_id (-lit); lrat_chain.push_back (id); changed = true; continue; -#else - LOG ("found unit %d, but ignoring it", -lit); -#endif } if (internal->val (lit) > 0) { LOG ("found positive unit, so clause is subsumed by unit"); @@ -820,7 +913,11 @@ Clause *Closure::new_tmp_clause (std::vector &clause) { LOG (clause, "learn new tmp clause"); CADICAL_assert (clause.size () >= 2); - internal->external->check_learned_clause (); + if (internal->external->solution) { + std::swap (clause, internal->clause); + internal->external->check_learned_clause (); + std::swap (clause, internal->clause); + } CADICAL_assert (internal->clause.size () <= (size_t) INT_MAX); const int size = (int) clause.size (); @@ -912,7 +1009,8 @@ void Closure::produce_rewritten_clause_lrat ( } Clause *Closure::produce_rewritten_clause_lrat (Clause *c, int except_lhs, - bool remove_units, bool fail_on_unit) { + bool remove_units, + bool fail_on_unit) { CADICAL_assert (internal->clause.empty ()); CADICAL_assert (internal->lrat_chain.empty ()); auto tmp_lrat (std::move (lrat_chain)); @@ -1012,7 +1110,7 @@ Clause *Closure::produce_rewritten_clause_lrat (Clause *c, int except_lhs, void Closure::push_id_on_chain (std::vector &chain, Rewrite rewrite, int lit) { - LOG ("adding reason %zd for rewriting %d marked", + LOG ("adding reason %" PRId64 " for rewriting %d marked", lit == rewrite.src ? rewrite.id1 : rewrite.id2, lit); chain.push_back (lit == rewrite.src ? rewrite.id1 : rewrite.id2); } @@ -1132,11 +1230,19 @@ void Closure::push_id_on_chain (std::vector &chain, Clause *c) { LOG (lrat_chain, "chain"); } +void Closure::push_id_on_chain (std::vector &chain, + const my_dummy_optional &c) { + CADICAL_assert (c ()); + auto litId = c.content; + chain.push_back (litId.clause->id); + LOG (lrat_chain, "chain"); +} + void Closure::push_id_on_chain (std::vector &chain, const std::vector &reasons) { for (auto litId : reasons) { - LOG (litId.clause, "found lrat in gate %d from %zd", litId.current_lit, - litId.clause->id); + LOG (litId.clause, "found lrat in gate %d from %" PRId64, + litId.current_lit, litId.clause->id); push_id_on_chain (chain, litId.clause); } LOG (lrat_chain, "chain from %zd reasons", reasons.size ()); @@ -1155,9 +1261,19 @@ void Closure::learn_congruence_unit_when_lhs_set (Gate *g, int src, case Gate_Type::And_Gate: LOG (lrat_chain, "lrat"); LOG (lrat_chain, "lrat"); - for (auto litId : g->neg_lhs_ids) - push_id_and_rewriting_lrat_unit ( - litId.clause, Rewrite (src, dst, id1, id2), lrat_chain); + if (g->neg_lhs_ids ()) + push_id_and_rewriting_lrat_unit (g->neg_lhs_ids.content.clause, + Rewrite (src, dst, id1, id2), + lrat_chain); + else { + CADICAL_assert (g->degenerated_gate == Special_Gate::DEGENERATED_AND || + g->degenerated_gate == + Special_Gate::DEGENERATED_AND_LHS_FALSE); + CADICAL_assert (g->pos_lhs_ids.size () == 1); + push_id_and_rewriting_lrat_unit (g->pos_lhs_ids[0].clause, + Rewrite (src, dst, id1, id2), + lrat_chain); + } LOG (lrat_chain, "lrat"); break; default: @@ -1167,10 +1283,15 @@ void Closure::learn_congruence_unit_when_lhs_set (Gate *g, int src, // Something very important here: as we are producing a unit, we cannot // simplify or rewrite the clauses as this will produce units. +// +// For special cases, you have to be careful as the reasons are not +// eagerly reduced. Therefore, we need to filter to keep only the reasons we +// are interested (usually the ones where we have a clash). void Closure::learn_congruence_unit_falsifies_lrat_chain ( Gate *g, int src, int dst, int clashing, int falsified, int unit) { if (!internal->lrat) return; + CADICAL_assert (unit); CADICAL_assert (!g->pos_lhs_ids.empty ()); CADICAL_assert (internal->analyzed.empty ()); CADICAL_assert (lrat_chain.empty ()); @@ -1185,14 +1306,19 @@ void Closure::learn_congruence_unit_falsifies_lrat_chain ( if (clashing == -g->lhs) { for (auto litId : g->pos_lhs_ids) { LOG (litId.clause, - "found lrat in gate %d from %zd (looking for %d)", + "found lrat in gate %d from %" PRId64 " (looking for %d)", litId.current_lit, litId.clause->id, falsified); if (litId.current_lit == clashing) { push_id_and_rewriting_lrat_unit ( litId.clause, Rewrite (), proof_chain, true, Rewrite (), - g->degenerated_and_neg || g->degenerated_and_pos ? 0 : -g->lhs); + g->degenerated_gate == Special_Gate::DEGENERATED_AND || + g->degenerated_gate == + Special_Gate::DEGENERATED_AND_LHS_FALSE + ? 0 + : -g->lhs); } } + CADICAL_assert (!proof_chain.empty ()); } else { // Example: 3 = (-1&2) and 2=1 // The proof consists in taking the binary clause with the rewrites @@ -1201,7 +1327,9 @@ void Closure::learn_congruence_unit_falsifies_lrat_chain ( // 9: -2v1 // 6: 3v1 // The chain cannot start by 9 - if (g->degenerated_and_neg || g->degenerated_and_pos) { + if (g->degenerated_gate == Special_Gate::DEGENERATED_AND || + g->degenerated_gate == + Special_Gate::DEGENERATED_AND_LHS_FALSE) { LOG ("%d %d %d", src, dst, g->lhs); if (src == g->lhs || dst == g->lhs) { LOG ("degenerated AND gate with dst=lhs"); @@ -1215,24 +1343,36 @@ void Closure::learn_congruence_unit_falsifies_lrat_chain ( LOG (proof_chain, "produced lrat chain so far"); } } - CADICAL_assert (!proof_chain.empty ()); + CADICAL_assert (!proof_chain.empty ()); } else { - LOG ("degenerated AND gate with conflict without LHS"); + LOG ("degenerated AND gate with conflict without LHS for %s", + LOGLIT (unit)); for (const auto &litId : g->pos_lhs_ids) { LOG (litId.clause, "definition clause %d ->", litId.current_lit); + const bool insert_after = + std::find (begin (*litId.clause), end (*litId.clause), + unit) == end (*litId.clause); push_id_and_rewriting_lrat_unit (litId.clause, Rewrite (), - proof_chain, false, - Rewrite (), -g->lhs); + proof_chain, insert_after, + Rewrite (), g->lhs); LOG (proof_chain, "produced lrat chain so far"); } } } else { LOG ("normal AND gate"); for (const auto &litId : g->pos_lhs_ids) { - push_id_and_rewriting_lrat_unit (litId.clause, Rewrite (), - proof_chain, false, Rewrite (), - g->degenerated_and_neg || g->degenerated_and_pos ? 0 : -g->lhs); + const bool insert_after = + std::find (begin (*litId.clause), end (*litId.clause), + unit) == end (*litId.clause); + push_id_and_rewriting_lrat_unit ( + litId.clause, Rewrite (), proof_chain, insert_after, + Rewrite (), + g->degenerated_gate == Special_Gate::DEGENERATED_AND || + g->degenerated_gate == + Special_Gate::DEGENERATED_AND_LHS_FALSE + ? 0 + : -g->lhs); LOG (proof_chain, "produced lrat chain so far"); } } @@ -1245,7 +1385,7 @@ void Closure::learn_congruence_unit_falsifies_lrat_chain ( // rewriting) for (auto litId : g->pos_lhs_ids) { LOG (litId.clause, - "found lrat in gate %d from %zd (looking for %d)", + "found lrat in gate %d from %" PRId64 " (looking for %d)", litId.current_lit, litId.clause->id, falsified); if (litId.current_lit == falsified || (litId.current_lit == src && dst == falsified)) { @@ -1257,9 +1397,9 @@ void Closure::learn_congruence_unit_falsifies_lrat_chain ( } else { CADICAL_assert (unit); // Example is 1 = 2&3 where 2 and 3 are false - for (auto litId : g->neg_lhs_ids) { - push_id_and_rewriting_lrat_unit (litId.clause, Rewrite (), - proof_chain); + if (g->neg_lhs_ids ()) { + push_id_and_rewriting_lrat_unit (g->neg_lhs_ids.content.clause, + Rewrite (), proof_chain); } LOG (proof_chain, "produced lrat chain"); break; @@ -1288,16 +1428,18 @@ bool Closure::fully_propagate () { return false; } -bool Closure::learn_congruence_unit (int lit, bool delay_propagation, bool force_propagation) { +bool Closure::learn_congruence_unit (int lit, bool delay_propagation, + bool force_propagation) { if (internal->unsat) return false; + LOG (lrat_chain, "assigning due to LRAT chain"); const signed char val_lit = internal->val (lit); if (val_lit > 0) { LOG ("already set lit %d", lit); if (internal->lrat) lrat_chain.clear (); if (force_propagation) - return fully_propagate(); + return fully_propagate (); return true; } LOG ("adding unit %s", LOGLIT (lit)); @@ -1307,6 +1449,7 @@ bool Closure::learn_congruence_unit (int lit, bool delay_propagation, bool force if (internal->lrat) { CADICAL_assert (internal->lrat_chain.empty ()); LRAT_ID id = internal->unit_id (-lit); + LOG ("for literal %s adding unit %" PRId64, LOGLIT (-lit), id); internal->lrat_chain.push_back (id); for (auto id : lrat_chain) internal->lrat_chain.push_back (id); @@ -1317,19 +1460,21 @@ bool Closure::learn_congruence_unit (int lit, bool delay_propagation, bool force } LOG (lrat_chain, "assigning due to LRAT chain"); + CADICAL_assert (internal->lrat_chain.empty ()); swap (lrat_chain, internal->lrat_chain); internal->assign_unit (lit); CADICAL_assert (lrat_chain.empty ()); CADICAL_assert (internal->lrat_chain.empty ()); if (delay_propagation) return false; - else return fully_propagate (); + else + return fully_propagate (); } // for merging the literals there are many cases // TODO: LRAT does not work if the LHS is not in normal form and if the // representative is also in the gate. -bool Closure::merge_literals_lrat ( +bool Closure::merge_literals ( Gate *g, Gate *h, int lit, int other, const std::vector &extra_reasons_lit, const std::vector &extra_reasons_ulit) { @@ -1337,9 +1482,7 @@ bool Closure::merge_literals_lrat ( CADICAL_assert (g->lhs == lit); CADICAL_assert (g == h || h->lhs == other); (void) g, (void) h; - LOG ("merging literals %s and %s", LOGLIT(lit), LOGLIT(other)); - // TODO: this should not update_eager but still calculate the LRAT chain - // below! + LOG ("merging literals %s and %s", LOGLIT (lit), LOGLIT (other)); const int repr_lit = find_representative_and_compress (lit, false); const int repr_other = find_representative_and_compress (other, false); find_representative_and_compress (-lit, false); @@ -1365,6 +1508,48 @@ bool Closure::merge_literals_lrat ( } } + // This is a special case where we actually cannot produce an LRAT proof + // 2 = (3 & 4) + // false = (3 & 4) + if (g->degenerated_gate == Special_Gate::DEGENERATED_AND_LHS_FALSE || + h->degenerated_gate == Special_Gate::DEGENERATED_AND_LHS_FALSE) { + LOG ("merging special AND case with both side < 0"); + CADICAL_assert (internal->val (g->lhs) < 0 || internal->val (h->lhs) < 0); + CADICAL_assert (internal->val (g->lhs) != internal->val (h->lhs)); + lrat_chain = + extra_reasons_lit.empty () ? extra_reasons_ulit : extra_reasons_lit; + const int unass = internal->val (g->lhs) < 0 ? h->lhs : g->lhs; + learn_congruence_unit (-unass); + return false; + } + + LOG ("special case %d %d %d %d", + g->degenerated_gate == Special_Gate::DEGENERATED_AND, + internal->val (h->lhs) < 0, + h->degenerated_gate == Special_Gate::DEGENERATED_AND, + internal->val (g->lhs) < 0); + if ((g->degenerated_gate == Special_Gate::DEGENERATED_AND_LHS_FALSE && + internal->val (h->lhs) < 0) || + (h->degenerated_gate == Special_Gate::DEGENERATED_AND_LHS_FALSE && + internal->val (g->lhs) < 0)) { + LOG ("merging special AND case"); + CADICAL_assert (internal->val (g->lhs) < 0 || internal->val (h->lhs) < 0); + CADICAL_assert (internal->val (g->lhs) != internal->val (h->lhs)); + lrat_chain = + extra_reasons_lit.empty () ? extra_reasons_ulit : extra_reasons_lit; + const int unass = internal->val (g->lhs) < 0 ? h->lhs : g->lhs; + learn_congruence_unit (-unass); + return false; + } + + return really_merge_literals (lit, other, repr_lit, repr_other, + extra_reasons_lit, extra_reasons_ulit); +} + +bool Closure::really_merge_literals ( + int lit, int other, int repr_lit, int repr_other, + const std::vector &extra_reasons_lit, + const std::vector &extra_reasons_ulit) { // For LRAT we need to distinguish more cases for a more regular // reconstruction. // @@ -1383,23 +1568,39 @@ bool Closure::merge_literals_lrat ( // (either false or the equivalence). The steps can also not be merged // because repr_lit can appear in the gate and hence in the resolution // chain. + const int val_lit = internal->val (lit); + const int val_other = internal->val (other); + int smaller_repr = repr_lit; int larger_repr = repr_other; int smaller = lit; int larger = other; + int val_smaller = val_lit; + int val_larger = val_other; const std::vector *smaller_chain = &extra_reasons_ulit; const std::vector *larger_chain = &extra_reasons_lit; + if (val_lit && val_lit == val_other) { + LOG ("not merging lits %d and %d assigned to same value", lit, other); + if (internal->lrat) + lrat_chain.clear (); + return false; + } + if (repr_lit == repr_other) { + LOG ("already merged %s and %s", LOGLIT (lit), LOGLIT (other)); + return false; + } if (abs (smaller_repr) > abs (larger_repr)) { swap (smaller_repr, larger_repr); swap (smaller, larger); swap (smaller_chain, larger_chain); + swap (val_smaller, val_larger); } CADICAL_assert (find_representative (smaller_repr) == smaller_repr); CADICAL_assert (find_representative (larger_repr) == larger_repr); if (lit == -other) { - CADICAL_assert (chain.empty ()); + CADICAL_assert (lrat_chain.empty ()); LOG ("merging clashing %d and %d", lit, other); if (internal->proof) { if (internal->lrat) { @@ -1421,9 +1622,10 @@ bool Closure::merge_literals_lrat ( return false; } - LOG ("merging %d and %d first and then the equivalences of %d and %d " + LOG ("merging %s and %s first and then the equivalences of %s and %s " "with LRAT", - lit, other, repr_lit, repr_other); + LOGLIT (lit), LOGLIT (other), LOGLIT (repr_lit), + LOGLIT (repr_other)); Clause *eq1_tmp = nullptr; if (internal->lrat) { lrat_chain = *smaller_chain; @@ -1447,7 +1649,12 @@ bool Closure::merge_literals_lrat ( if (repr_lit == -repr_other) { // now derive empty clause Rewrite rew1, rew2; + Clause *eq1 = eq1_tmp; // == -larger, smaller + Clause *eq2 = eq2_tmp; if (internal->lrat) { + if (val_larger < 0) + swap (eq1, eq2); + // no need to calculate push_id_and_rewriting_lrat here because all // the job is done by the arguments already rew1 = Rewrite (lit == repr_lit ? 0 : lit, repr_lit, @@ -1456,21 +1663,26 @@ bool Closure::merge_literals_lrat ( rew2 = Rewrite (other == repr_other ? 0 : other, repr_other, other == repr_other ? 0 : representative_id (other), other == repr_other ? 0 : representative_id (-other)); - push_id_and_rewriting_lrat_unit (eq1_tmp, rew1, lrat_chain, true, - rew2); + push_id_and_rewriting_lrat_unit (eq1, rew1, lrat_chain, true, rew2); swap (lrat_chain, internal->lrat_chain); } - internal->assign_unit (-larger_repr); - if (internal->lrat) { - internal->lrat_chain.clear (); + CADICAL_assert (val_larger == internal->val (larger_repr)); + if (!val_larger) { + // not assigned, first assign one + internal->assign_unit (-larger_repr); + if (internal->lrat) { + internal->lrat_chain.clear (); - if (larger != larger_repr) - push_lrat_unit (-larger_repr); - push_id_and_rewriting_lrat_unit ( - eq2_tmp, rew1, lrat_chain, true, rew2, - larger != larger_repr ? larger_repr : 0); - LOG (lrat_chain, "lrat chain"); - swap (lrat_chain, internal->lrat_chain); + if (larger != larger_repr) + push_lrat_unit (-larger_repr); + // no need to calculate push_id_and_rewriting_lrat here because all + // the job is done by the arguments already + push_id_and_rewriting_lrat_unit (eq2, rew1, lrat_chain, true, rew2, + larger != larger_repr ? larger_repr + : 0); + LOG (lrat_chain, "lrat chain"); + swap (lrat_chain, internal->lrat_chain); + } } internal->learn_empty_clause (); if (internal->lrat) @@ -1480,22 +1692,37 @@ bool Closure::merge_literals_lrat ( if (val_lit) { if (val_lit == val_other) { - LOG ("not merging lits %d and %d assigned to same value", lit, other); + LOG ("not merging lits %s and %s assigned to same value", + LOGLIT (lit), LOGLIT (other)); if (internal->lrat) lrat_chain.clear (); return false; } if (val_lit == -val_other) { - LOG ("merging lits %d and %d assigned to inconsistent value", lit, - other); + LOG ("merging lits %s and %s assigned to inconsistent value", + LOGLIT (lit), LOGLIT (other)); CADICAL_assert (lrat_chain.empty ()); if (internal->lrat) { - Clause *c = val_lit ? eq2_tmp : eq1_tmp; - int pos = val_lit ? other : lit; - int neg = val_lit ? -lit : -other; + Clause *c; + // eq1 == -larger, smaller + // eq2 == larger, -smaller + if (lit == smaller) { + if (val_lit < 0) + c = eq1_tmp; + else + c = eq2_tmp; + } else { + if (val_lit < 0) + c = eq2_tmp; + else + c = eq1_tmp; + } + int pos = val_lit < 0 ? other : lit; + int neg = val_lit < 0 ? -lit : -other; push_lrat_unit (pos); push_lrat_unit (neg); push_id_on_chain (lrat_chain, c); + swap (internal->lrat_chain, lrat_chain); } internal->learn_empty_clause (); if (internal->lrat) @@ -1504,7 +1731,8 @@ bool Closure::merge_literals_lrat ( } CADICAL_assert (!val_other); - LOG ("merging assigned %d and unassigned %d", lit, other); + LOG ("merging assigned %s and unassigned %s", LOGLIT (lit), + LOGLIT (other)); CADICAL_assert (lrat_chain.empty ()); const int unit = (val_lit < 0) ? -other : other; if (internal->lrat) { @@ -1530,21 +1758,24 @@ bool Closure::merge_literals_lrat ( } if (!val_lit && val_other) { - LOG ("merging assigned %d and unassigned %d", lit, other); + LOG ("merging unassigned %s and assigned %s", LOGLIT (lit), + LOGLIT (other)); CADICAL_assert (lrat_chain.empty ()); const int unit = (val_other < 0) ? -lit : lit; if (internal->lrat) { Clause *c; - if (lit == smaller) { - if (val_lit < 0) - c = eq1_tmp; + // eq1 == -larger, smaller + // eq2 == larger, -smaller + if (lit == smaller) { // opposite of case above + if (val_other < 0) + c = eq2_tmp; // other == larger ==> eq2 == other , unit else - c = eq2_tmp; + c = eq1_tmp; // other == larger ==> eq1 == -other , unit } else { - if (val_lit < 0) - c = eq2_tmp; + if (val_other < 0) + c = eq1_tmp; // lit == larger ==> eq1 == unit, other else - c = eq1_tmp; + c = eq2_tmp; // lit == larger ==> eq2 == unit, -other } push_id_and_rewriting_lrat_unit (c, Rewrite (), lrat_chain, true, Rewrite (), lit, unit); @@ -1633,7 +1864,7 @@ bool Closure::merge_literals_lrat ( // Variant when there are no gates // TODO: this is exactly the same as the other function without the gates. // Kill the arguments! -bool Closure::merge_literals_lrat ( +bool Closure::merge_literals ( int lit, int other, const std::vector &extra_reasons_lit, const std::vector &extra_reasons_ulit) { CADICAL_assert (!internal->unsat); @@ -1650,320 +1881,11 @@ bool Closure::merge_literals_lrat ( if (repr_lit == repr_other) { LOG ("already merged %s and %s", LOGLIT (lit), LOGLIT (other)); - if (internal->lrat) - lrat_chain.clear (); return false; } - const int val_lit = internal->val (lit); - const int val_other = internal->val (other); - - // For LRAT we need to distinguish more cases for a more regular - // reconstruction. - // - // 1. if lit = -other, then we learn lit and -lit to derive false - // - // 2. otherwise, we learn the new clauses lit = -other (which are two real - // clauses). - // - // 2a. if repr_lit = -repr_other, we learn the units repr_lit and - // -repr_lit to derive false - // - // 2b. otherwise, we learn the equivalences repr_lit = -repr_other - // (which are two real clauses) - // - // Without LRAT this is easier, as we directly learn the conclusion - // (either false or the equivalence). The steps can also not be merged - // because repr_lit can appear in the gate and hence in the resolution - // chain. - int smaller_repr = repr_lit; - int larger_repr = repr_other; - int val_smaller = val_lit; - int val_larger = val_other; - int smaller = lit; - int larger = other; - const std::vector *smaller_chain = &extra_reasons_ulit; - const std::vector *larger_chain = &extra_reasons_lit; - - if (abs (smaller_repr) > abs (larger_repr)) { - swap (smaller_repr, larger_repr); - swap (smaller, larger); - swap (smaller_chain, larger_chain); - swap (val_smaller, val_larger); - } - - CADICAL_assert (find_representative (smaller_repr) == smaller_repr); - CADICAL_assert (find_representative (larger_repr) == larger_repr); - if (lit == -other) { - LOG ("merging clashing %d and %d", lit, other); - if (!val_smaller) { - if (internal->lrat) - internal->lrat_chain = *smaller_chain; - internal->assign_unit (smaller); - if (internal->lrat) - internal->lrat_chain.clear (); - - push_lrat_unit (smaller); - if (internal->lrat) { - CADICAL_assert (internal->lrat_chain.empty ()); - swap (internal->lrat_chain, lrat_chain); - for (auto id : *larger_chain) - internal->lrat_chain.push_back (id); - LOG (internal->lrat_chain, "lrat chain"); - } - internal->learn_empty_clause (); - return false; - } else { - if (internal->lrat) - internal->lrat_chain = - (val_smaller < 0 ? *smaller_chain : *larger_chain); - if (internal->lrat) - internal->lrat_chain.push_back ( - internal->unit_id (val_smaller > 0 ? smaller : -smaller)); - internal->learn_empty_clause (); - return false; - } - } - - if (val_lit && val_lit == val_other) { - LOG ("not merging lits %d and %d assigned to same value", lit, other); - return false; - } - - if (val_lit && val_lit == -val_other) { - if (internal->lrat) { - internal->lrat_chain.push_back ( - internal->unit_id (val_smaller < 0 ? -smaller : smaller)); - internal->lrat_chain.push_back ( - internal->unit_id (val_larger < 0 ? -larger : larger)); - for (auto id : (val_smaller < 0 ? *smaller_chain : *larger_chain)) { - internal->lrat_chain.push_back(id); - } - } - internal->learn_empty_clause (); - return false; - } - - LOG ("merging %d and %d first and then the equivalences of %d and %d " - "with LRAT", - lit, other, repr_lit, repr_other); - Clause *eq1_tmp = nullptr; - if (internal->lrat) { - lrat_chain = *smaller_chain; - eq1_tmp = add_tmp_binary_clause (-larger, smaller); - CADICAL_assert (eq1_tmp); - } - CADICAL_assert (!internal->lrat || eq1_tmp); - - Clause *eq2_tmp = nullptr; - if (internal->lrat) { - lrat_chain = *larger_chain; - LOG (lrat_chain, "lrat chain"); - // the order in the clause is important for the - // repr_lit == -repr_other to get the right chain - eq2_tmp = add_tmp_binary_clause (larger, -smaller); - CADICAL_assert (eq2_tmp); - } - - CADICAL_assert (!internal->lrat || eq2_tmp); - if (internal->lrat) - lrat_chain.clear (); - - if (repr_lit == -repr_other) { - // now derive empty clause - Rewrite rew1, rew2; - if (internal->lrat) { - // no need to calculate push_id_and_rewriting_lrat here because all - // the job is done by the arguments already - rew1 = Rewrite (lit == repr_lit ? 0 : lit, repr_lit, - lit == repr_lit ? 0 : representative_id (lit), - lit == repr_lit ? 0 : representative_id (-lit)); - rew2 = Rewrite (other == repr_other ? 0 : other, repr_other, - other == repr_other ? 0 : representative_id (other), - other == repr_other ? 0 : representative_id (-other)); - push_id_and_rewriting_lrat_unit (eq1_tmp, rew1, lrat_chain, true, - rew2); - swap (lrat_chain, internal->lrat_chain); - } - CADICAL_assert (val_larger == internal->val (larger_repr)); - if (!val_larger) { - // not assigned, first assign one - internal->assign_unit (-larger_repr); - if (internal->lrat) { - internal->lrat_chain.clear (); - - if (larger != larger_repr) - push_lrat_unit (-larger_repr); - // no need to calculate push_id_and_rewriting_lrat here because all - // the job is done by the arguments already - push_id_and_rewriting_lrat_unit ( - eq2_tmp, rew1, lrat_chain, true, rew2, - larger != larger_repr ? larger_repr : 0); - LOG (lrat_chain, "lrat chain"); - swap (lrat_chain, internal->lrat_chain); - } - } else { - // otherwise, no need to - if (internal->lrat) - lrat_chain.push_back (internal->unit_id (larger_repr)); - } - internal->learn_empty_clause (); - if (internal->lrat) - internal->lrat_chain.clear (); - return false; - } - - if (val_lit) { - if (val_lit == val_other) { - LOG ("not merging lits %d and %d assigned to same value", lit, other); - if (internal->lrat) - lrat_chain.clear (); - return false; - } - if (val_lit == -val_other) { - LOG ("merging lits %d and %d assigned to inconsistent value", lit, - other); - CADICAL_assert (lrat_chain.empty ()); - if (internal->lrat) { - Clause *c = val_lit ? eq2_tmp : eq1_tmp; - int pos = val_lit ? other : lit; - int neg = val_lit ? -lit : -other; - push_lrat_unit (pos); - push_lrat_unit (neg); - push_id_on_chain (lrat_chain, c); - } - internal->learn_empty_clause (); - if (internal->lrat) - lrat_chain.clear (); - return false; - } - - CADICAL_assert (!val_other); - LOG ("merging assigned %d and unassigned %d", lit, other); - CADICAL_assert (lrat_chain.empty ()); - const int unit = (val_lit < 0) ? -other : other; - if (internal->lrat) { - Clause *c; - if (lit == smaller) { - if (val_lit < 0) - c = eq1_tmp; - else - c = eq2_tmp; - } else { - if (val_lit < 0) - c = eq2_tmp; - else - c = eq1_tmp; - } - push_id_and_rewriting_lrat_unit (c, Rewrite (), lrat_chain, true, - Rewrite (), unit); - } - learn_congruence_unit (unit); - if (internal->lrat) - lrat_chain.clear (); - return false; - } - - if (!val_lit && val_other) { - LOG ("merging assigned %d and unassigned %d", lit, other); - CADICAL_assert (lrat_chain.empty ()); - const int unit = (val_other < 0) ? -lit : lit; - if (internal->lrat) { - Clause *c; - if (lit == smaller) { - CADICAL_assert (other == larger); - if (val_other > 0) - c = eq1_tmp; - else - c = eq2_tmp; - } else { - if (val_other > 0) - c = eq2_tmp; - else - c = eq1_tmp; - } - push_id_and_rewriting_lrat_unit (c, Rewrite (), lrat_chain, true, - Rewrite (), lit, unit); - } - learn_congruence_unit (unit); - if (internal->lrat) - lrat_chain.clear (); - return false; - } - - Clause *eq1_repr, *eq2_repr; - if (smaller_repr != smaller || larger != larger_repr) { - if (internal->lrat) { - lrat_chain.clear (); - Rewrite rew1 = Rewrite ( - smaller_repr != smaller ? smaller : 0, - smaller_repr != smaller ? smaller_repr : 0, - smaller_repr != smaller ? representative_id (smaller) : 0, - smaller_repr != smaller ? representative_id (-smaller) : 0); - Rewrite rew2 = - Rewrite (larger_repr != larger ? larger : 0, - larger_repr != larger ? larger_repr : 0, - larger_repr != larger ? representative_id (larger) : 0, - larger_repr != larger ? representative_id (-larger) : 0); - push_id_and_rewriting_lrat_full (eq1_tmp, rew1, lrat_chain, true, - rew2); - } - eq1_repr = learn_binary_tmp_or_full_clause (-larger_repr, smaller_repr); - } else { - if (internal->lrat) - eq1_repr = maybe_promote_tmp_binary_clause (eq1_tmp); - else - eq1_repr = maybe_add_binary_clause (-larger_repr, smaller_repr); - } - - if (internal->lrat) { - lrat_chain.clear (); - } - - if (smaller_repr != smaller || larger != larger_repr) { - - if (internal->lrat) { - lrat_chain.clear (); - // eq2 = larger, -smaller - Rewrite rew1 = Rewrite ( - smaller_repr != smaller ? smaller : 0, - smaller_repr != smaller ? smaller_repr : 0, - smaller_repr != smaller ? representative_id (smaller) : 0, - smaller_repr != smaller ? representative_id (-smaller) : 0); - Rewrite rew2 = - Rewrite (larger_repr != larger ? larger : 0, - larger_repr != larger ? larger_repr : 0, - larger_repr != larger ? representative_id (larger) : 0, - larger_repr != larger ? representative_id (-larger) : 0); - push_id_and_rewriting_lrat_full (eq2_tmp, rew1, lrat_chain, true, - rew2); - } - eq2_repr = learn_binary_tmp_or_full_clause (larger_repr, -smaller_repr); - } else { - if (internal->lrat) - eq2_repr = maybe_promote_tmp_binary_clause (eq2_tmp); - else - eq2_repr = maybe_add_binary_clause (larger_repr, -smaller_repr); - } - lrat_chain.clear (); - - if (internal->lrat) { - representative_id (larger_repr) = eq1_repr->id; - CADICAL_assert (std::find (begin (*eq1_repr), end (*eq1_repr), -larger_repr) != - end (*eq1_repr)); - representative_id (-larger_repr) = eq2_repr->id; - check_not_tmp_binary_clause (eq1_repr); - check_not_tmp_binary_clause (eq2_repr); - CADICAL_assert (std::find (begin (*eq2_repr), end (*eq2_repr), larger_repr) != - end (*eq2_repr)); - } - LOG ("updating %d -> %d", larger_repr, smaller_repr); - representative (larger_repr) = smaller_repr; - representative (-larger_repr) = -smaller_repr; - schedule_literal (larger_repr); - ++internal->stats.congruence.congruent; - CADICAL_assert (lrat_chain.empty ()); - return true; + return really_merge_literals (lit, other, repr_lit, repr_other, + extra_reasons_lit, extra_reasons_ulit); } inline void Closure::promote_clause (Clause *c) { @@ -2238,8 +2160,8 @@ void Closure::init_closure () { } units = internal->propagated; Random rand (internal->stats.congruence.rounds); - for (auto &n : nonces) { - n = 1 | rand.next (); + for (uint64_t &n : nonces) { + n = static_cast (1) | rand.next (); } #ifdef LOGGING fresh_id = internal->clause_id; @@ -2270,8 +2192,21 @@ void Closure::init_and_gate_extraction () { void Closure::check_and_gate_implied (Gate *g) { CADICAL_assert (internal->clause.empty ()); CADICAL_assert (g->tag == Gate_Type::And_Gate); - if (internal->lrat) + if (!internal->opts.check) return; + if (internal->lrat) { + CADICAL_assert (g->degenerated_gate != Special_Gate::NORMAL || + g->neg_lhs_ids ()); +#ifndef CADICAL_NDEBUG + for (auto c : g->pos_lhs_ids) { + CADICAL_assert (std::find_if ( + begin (*c.clause), end (*c.clause), [this, c] (int lit) { + return find_eager_representative (lit) == c.current_lit; + }) != end (*c.clause)); + } +#endif + return; + } LOG (g, "checking implied"); const int lhs = g->lhs; const int not_lhs = -lhs; @@ -2351,13 +2286,11 @@ void Closure::shrink_and_gate (Gate *g, int falsifies, int clashing) { if (falsifies) { g->rhs.resize (1); g->rhs[0] = falsifies; - g->hash = hash_lits (nonces, g->rhs); } else if (clashing) { LOG (g, "gate before clashing on %d", clashing); g->rhs.resize (2); g->rhs[0] = clashing; g->rhs[1] = -clashing; - g->hash = hash_lits (nonces, g->rhs); LOG (g, "gate after clashing on %d", clashing); } g->shrunken = true; @@ -2368,23 +2301,30 @@ void Closure::update_and_gate_unit_build_lrat_chain ( std::vector &extra_reasons_lit, std::vector &extra_reasons_ulit) { LOG ("generate chain for gate boiling down to unit"); - if (g->neg_lhs_ids.size () != 1) { + if (g->lhs == g->rhs[0]) // do not try to produce LRAT chains, we will not + // need them anyway + return; - if (g->degenerated_and_neg || g->degenerated_and_pos) { + if (!g->neg_lhs_ids ()) { + + if (g->degenerated_gate == Special_Gate::DEGENERATED_AND || + g->degenerated_gate == Special_Gate::DEGENERATED_AND_LHS_FALSE) { // can happen for 4 = AND 3 4 (degenerated with only the clause -4 3) // with a rewriting 4 -> 1 (unchanged clause) // and later 1 -> 3 (unchanged clause) // but you do not know anymore from the gate that it is degenerated CADICAL_assert (g->pos_lhs_ids.size () == 1); push_id_and_rewriting_lrat_unit (g->pos_lhs_ids[0].clause, Rewrite (), - extra_reasons_ulit, true, Rewrite ()); + extra_reasons_ulit, true, + Rewrite ()); return; } CADICAL_assert (g->lhs == g->rhs[0] || (g->lhs == src && g->rhs[0] == dst)); - CADICAL_assert (g->pos_lhs_ids.size () <= 1); // either degenerated or empty A = A + CADICAL_assert (g->pos_lhs_ids.size () <= + 1); // either degenerated or empty A = A return; } - CADICAL_assert (g->neg_lhs_ids.size () == 1); + CADICAL_assert (g->neg_lhs_ids ()); CADICAL_assert (!g->pos_lhs_ids.empty ()); const int repr_lit = find_representative (g->lhs); @@ -2394,9 +2334,9 @@ void Closure::update_and_gate_unit_build_lrat_chain ( return; } - push_id_and_rewriting_lrat_unit (g->neg_lhs_ids[0].clause, Rewrite (), - extra_reasons_ulit, true, Rewrite (), - g->lhs, -dst); + push_id_and_rewriting_lrat_unit (g->neg_lhs_ids.content.clause, + Rewrite (), extra_reasons_ulit, true, + Rewrite (), g->lhs, -dst); LOG (extra_reasons_ulit, "lrat chain for negative side"); lrat_chain.clear (); @@ -2419,10 +2359,12 @@ void Closure::update_and_gate_build_lrat_chain ( if (find_representative (g->lhs) == find_representative (h->lhs)) return; // set to same value, don't do anything - if (internal->val (g->lhs) && internal->val (g->lhs) == internal->val (h->lhs)) + if (internal->val (g->lhs) && + internal->val (g->lhs) == internal->val (h->lhs)) return; const bool g_tautology = gate_contains (g, g->lhs); const bool h_tautology = gate_contains (h, h->lhs); + if (g_tautology && h_tautology) { LOG ("both gates are a tautology"); // special case: actually we have an equivalence due to binary clauses @@ -2443,7 +2385,7 @@ void Closure::update_and_gate_build_lrat_chain ( for (auto &litId : h->pos_lhs_ids) { if (litId.current_lit == g->lhs) { CADICAL_assert (extra_reasons_ulit.empty ()); - CADICAL_assert (litId.clause); + CADICAL_assert (litId.clause); LOG (litId.clause, "binary clause to push into the reason"); litId.clause = produce_rewritten_clause_lrat (litId.clause, h->lhs, remove_units); @@ -2455,6 +2397,54 @@ void Closure::update_and_gate_build_lrat_chain ( CADICAL_assert (extra_reasons_ulit.size () == 1); return; } + + const bool produce_unit_due_to_both_degenerated = + (g_tautology && internal->val (h->lhs) < 0 && + h->degenerated_gate == Special_Gate::DEGENERATED_AND_LHS_FALSE) || + (h_tautology && internal->val (g->lhs) < 0 && + g->degenerated_gate == Special_Gate::DEGENERATED_AND_LHS_FALSE); + + if (produce_unit_due_to_both_degenerated) { + LOG ("one gate is a tautology and the other is a unit: producing " + "reason for unit"); + Gate *tauto = (g_tautology ? g : h); + Gate *other = (g_tautology ? h : g); + CADICAL_assert (tauto->degenerated_gate & Special_Gate::DEGENERATED_AND); + LOG (tauto, "starting lrat from tauto gate"); + for (auto &litId : tauto->pos_lhs_ids) { + litId.clause = produce_rewritten_clause_lrat ( + litId.clause, tauto->lhs, remove_units); + if (litId.clause) + extra_reasons_lit.push_back (litId.clause->id); + } + LOG (tauto, "now creating lrat with other gate"); + // if tauto can also be a tautology: neg_lhs_ids + // otherwise the clause is in pos_lhs_ids + for (auto &litId : other->pos_lhs_ids) { + CADICAL_assert (extra_reasons_ulit.empty ()); + CADICAL_assert (litId.clause); + LOG (litId.clause, "binary clause to push into the reason"); + litId.clause = + produce_rewritten_clause_lrat (litId.clause, 0, remove_units); + if (litId.clause) + extra_reasons_lit.push_back (litId.clause->id); + } + if (other->neg_lhs_ids ()) { + auto &litId = other->neg_lhs_ids.content; + if (litId.current_lit == tauto->lhs || + litId.current_lit == -tauto->lhs || true) { + CADICAL_assert (extra_reasons_ulit.empty ()); + CADICAL_assert (litId.clause); + LOG (litId.clause, "binary clause to push into the reason"); + litId.clause = + produce_rewritten_clause_lrat (litId.clause, 0, remove_units); + if (litId.clause) + extra_reasons_lit.push_back (litId.clause->id); + } + } + return; + } + if (g_tautology || h_tautology) { // special case: actually we have an equivalence due to binary clauses // and some of the clauses from the gate are actually tautologies @@ -2473,7 +2463,7 @@ void Closure::update_and_gate_build_lrat_chain ( // one direction: the binary clause already exists for (auto &litId : other->pos_lhs_ids) { if (litId.current_lit == tauto->lhs) { - CADICAL_assert (litId.clause); + CADICAL_assert (litId.clause); CADICAL_assert (extra_reasons_tauto.empty ()); LOG (litId.clause, "binary clause to push into the reason"); litId.clause = produce_rewritten_clause_lrat ( @@ -2493,32 +2483,69 @@ void Closure::update_and_gate_build_lrat_chain ( litId.current_lit, tauto->lhs); if (litId.current_lit != tauto->lhs) { LOG (litId.clause, "binary clause to push into the reason"); - CADICAL_assert (litId.clause); + CADICAL_assert (litId.clause); litId.clause = produce_rewritten_clause_lrat ( litId.clause, tauto->lhs, remove_units); if (!litId.clause) // degenerated but does not know yet - continue; - CADICAL_assert (litId.clause); + continue; + CADICAL_assert (litId.clause); extra_reasons_other.push_back (litId.clause->id); } - tauto->pos_lhs_ids.erase (std::remove_if (begin (tauto->pos_lhs_ids), - end (tauto->pos_lhs_ids), - [] (const LitClausePair &p) { - return !p.clause; - }), - end (tauto->pos_lhs_ids)); + tauto->pos_lhs_ids.erase ( + std::remove_if ( + begin (tauto->pos_lhs_ids), end (tauto->pos_lhs_ids), + [] (const LitClausePair &p) { return !p.clause; }), + end (tauto->pos_lhs_ids)); } CADICAL_assert (!extra_reasons_other.empty ()); produce_rewritten_clause_lrat_and_clean (other->neg_lhs_ids, other->lhs, remove_units); - push_id_on_chain (extra_reasons_other, other->neg_lhs_ids); + CADICAL_assert (other->neg_lhs_ids ()); + push_id_on_chain (extra_reasons_other, + other->neg_lhs_ids.content.clause); CADICAL_assert (!extra_reasons_tauto.empty ()); CADICAL_assert (!extra_reasons_other.empty ()); return; } + + const bool g_degenerated = + (g->degenerated_gate == Special_Gate::DEGENERATED_AND || + g->degenerated_gate == Special_Gate::DEGENERATED_AND_LHS_FALSE); + const bool h_degenerated = + (h->degenerated_gate == Special_Gate::DEGENERATED_AND || + h->degenerated_gate == Special_Gate::DEGENERATED_AND_LHS_FALSE); + + CADICAL_assert (!(g_degenerated && h_degenerated)); + if (g_degenerated || h_degenerated) { + Gate *tauto = (g_degenerated ? g : h); + Gate *other = (g_degenerated ? h : g); + LOG (tauto, "one gate is degenerated"); + CADICAL_assert (tauto != other); + CADICAL_assert (tauto == h || tauto == g); + + auto &extra_reasons_tauto = + (!g_tautology ? extra_reasons_lit : extra_reasons_ulit); + + // start with the other clauses for LRAT + produce_rewritten_clause_lrat_and_clean (other->pos_lhs_ids, + other->lhs); + for (auto &litId : other->pos_lhs_ids) { + LOG (litId.clause, "pushing clause from other"); + push_id_on_chain (extra_reasons_tauto, litId.clause); + } + // one direction: the binary clause already exists + produce_rewritten_clause_lrat_and_clean (tauto->pos_lhs_ids, + other->lhs); + for (auto &litId : tauto->pos_lhs_ids) { + LOG (litId.clause, "pushing clause from tauto"); + push_id_on_chain (extra_reasons_tauto, litId.clause); + } + LOG (extra_reasons_tauto, "lrat chain for positive side"); + return; + } + // default: resolve all clauses // first rewrite - // TODO: do we really need dest as second exclusion? produce_rewritten_clause_lrat_and_clean (h->pos_lhs_ids, -h->lhs, remove_units); CADICAL_assert (internal->clause.empty ()); @@ -2533,7 +2560,7 @@ void Closure::update_and_gate_build_lrat_chain ( CADICAL_assert (internal->clause.empty ()); push_id_on_chain (extra_reasons_ulit, h->pos_lhs_ids); - push_id_on_chain (extra_reasons_ulit, g->neg_lhs_ids[0].clause); + push_id_on_chain (extra_reasons_ulit, g->neg_lhs_ids); lrat_chain.clear (); LOG (extra_reasons_ulit, "lrat chain for negative side"); @@ -2549,18 +2576,36 @@ void Closure::update_and_gate_build_lrat_chain ( void Closure::update_and_gate (Gate *g, GatesTable::iterator it, int src, int dst, LRAT_ID id1, LRAT_ID id2, int falsifies, int clashing) { - LOG (g, "update and gate of arity %ld", g->arity ()); + LOG (g, "update and gate of arity %zu", g->arity ()); CADICAL_assert (lrat_chain.empty ()); bool garbage = true; + if (g->arity () == 1 && internal->val (g->lhs) && + internal->val (g->lhs) == internal->val (g->rhs[0])) { + g->garbage = true; + return; + } if (falsifies || clashing) { - if (internal->lrat) - learn_congruence_unit_falsifies_lrat_chain (g, src, dst, clashing, - falsifies, 0); int unit = -g->lhs; if (unit == src) unit = dst; else if (unit == -src) unit = -dst; + if (internal->lrat) + learn_congruence_unit_falsifies_lrat_chain (g, src, dst, clashing, + falsifies, unit); + int other = -unit; + if (g->degenerated_gate == Special_Gate::DEGENERATED_AND || + g->degenerated_gate == Special_Gate::DEGENERATED_AND_LHS_FALSE) { + other = find_eager_representative_and_compress (-unit); + if (internal->lrat && -unit != other) { + CADICAL_assert (internal->lrat_chain.empty ()); + internal->lrat_chain.push_back (eager_representative_id (-unit)); + for (auto id : lrat_chain) + internal->lrat_chain.push_back (id); + lrat_chain.clear (); + swap (internal->lrat_chain, lrat_chain); + } + } learn_congruence_unit (unit); if (internal->lrat) lrat_chain.clear (); @@ -2586,8 +2631,8 @@ void Closure::update_and_gate (Gate *g, GatesTable::iterator it, int src, update_and_gate_unit_build_lrat_chain (g, src, id1, id2, g->rhs[0], extra_reasons_lit, extra_reasons_ulit); - if (merge_literals_lrat (g, g, g->lhs, g->rhs[0], extra_reasons_lit, - extra_reasons_ulit)) { + if (merge_literals (g, g, g->lhs, g->rhs[0], extra_reasons_lit, + extra_reasons_ulit)) { ++internal->stats.congruence.unaries; ++internal->stats.congruence.unary_and; } @@ -2604,15 +2649,15 @@ void Closure::update_and_gate (Gate *g, GatesTable::iterator it, int src, if (internal->lrat) update_and_gate_build_lrat_chain (g, h, extra_reasons_lit2, extra_reasons_ulit2); - if (merge_literals_lrat (g, h, g->lhs, h->lhs, extra_reasons_lit2, - extra_reasons_ulit2)) + if (merge_literals (g, h, g->lhs, h->lhs, extra_reasons_lit2, + extra_reasons_ulit2)) ++internal->stats.congruence.ands; } else { if (g->indexed) { + CADICAL_assert (it != table.end ()); LOG (g, "removing from table"); (void) table.erase (it); } - g->hash = hash_lits (nonces, g->rhs); LOG (g, "inserting gate into table"); CADICAL_assert (table.count (g) == 0); table.insert (g); @@ -2632,7 +2677,6 @@ void Closure::update_xor_gate (Gate *g, GatesTable::iterator git) { CADICAL_assert (!internal->unsat && chain.empty ()); LOG (g, "updating"); bool garbage = true; - // TODO Florian LRAT for learn_congruence_unit CADICAL_assert (g->arity () == 0 || internal->clause.empty ()); CADICAL_assert (clause.empty ()); if (g->arity () == 0) { @@ -2653,7 +2697,7 @@ void Closure::update_xor_gate (Gate *g, GatesTable::iterator git) { for (auto id : lrat_chain) internal->lrat_chain.push_back (id); lrat_chain.clear (); - internal->learn_empty_clause(); + internal->learn_empty_clause (); } else learn_congruence_unit (-g->lhs); @@ -2681,9 +2725,8 @@ void Closure::update_xor_gate (Gate *g, GatesTable::iterator git) { lrat_chain.push_back (g->pos_lhs_ids[1].clause->id); } learn_congruence_unit (-g->rhs[0]); - } else if (merge_literals_lrat ( - g->lhs, g->rhs[0], reasons_implication, - reasons_back)) { // TODO Florian merge with LRAT + } else if (merge_literals (g->lhs, g->rhs[0], reasons_implication, + reasons_back)) { ++internal->stats.congruence.unaries; ++internal->stats.congruence.unary_and; } @@ -2695,15 +2738,15 @@ void Closure::update_xor_gate (Gate *g, GatesTable::iterator git) { std::vector reasons_implication, reasons_back; add_xor_matching_proof_chain (g, g->lhs, h->pos_lhs_ids, h->lhs, reasons_implication, reasons_back); - if (merge_literals_lrat (g->lhs, h->lhs, reasons_implication, - reasons_back)) + if (merge_literals (g, h, g->lhs, h->lhs, reasons_implication, + reasons_back)) { ++internal->stats.congruence.xors; + } delete_proof_chain (); } else { if (g->indexed) { remove_gate (git); } - g->hash = hash_lits (nonces, g->rhs); LOG (g, "reinserting in table"); table.insert (g); g->indexed = true; @@ -2726,6 +2769,16 @@ void Closure::simplify_and_gate (Gate *g) { int falsifies = 0; std::vector::iterator it = begin (g->rhs); bool ulhs_in_rhs = false; + if (g->degenerated_gate != Special_Gate::NORMAL) { + const int old_lhs = g->lhs; + g->lhs = find_eager_representative (g->lhs); + for (auto &c : g->pos_lhs_ids) { + if (c.current_lit == old_lhs) + c.current_lit = g->lhs; + if (c.current_lit == -old_lhs) + c.current_lit = -g->lhs; + } + } for (auto lit : g->rhs) { const signed char v = internal->val (lit); if (v > 0) { @@ -2739,24 +2792,24 @@ void Closure::simplify_and_gate (Gate *g) { ulhs_in_rhs = true; *it++ = lit; if (lit == g->lhs) - g->degenerated_and_pos = true; + g->degenerated_gate = DEGENERATED_AND; if (lit == -g->lhs) - g->degenerated_and_neg = true; + g->degenerated_gate = DEGENERATED_AND; } if (internal->lrat) { // updating reasons size_t i = 0, size = g->pos_lhs_ids.size (); for (size_t j = 0; j < size; ++j) { - LOG ("looking at %d [%ld %ld]", g->pos_lhs_ids[j].current_lit, i, j); + LOG ("looking at %d [%zu %zu]", g->pos_lhs_ids[j].current_lit, i, j); g->pos_lhs_ids[i] = g->pos_lhs_ids[j]; - if (!g->degenerated_and_pos && + if (!g->degenerated_gate && internal->val (g->pos_lhs_ids[i].current_lit) && g->pos_lhs_ids[i].current_lit != falsifies) continue; - LOG ("keeping %d [%ld %ld]", g->pos_lhs_ids[i].current_lit, i, j); + LOG ("keeping %d [%zu %zu]", g->pos_lhs_ids[i].current_lit, i, j); ++i; } - LOG ("resizing to %ld", i); + LOG ("resizing to %zu", i); g->pos_lhs_ids.resize (i); } @@ -2767,7 +2820,6 @@ void Closure::simplify_and_gate (Gate *g) { g->shrunken = true; g->rhs.resize (it - std::begin (g->rhs)); - g->hash = hash_lits (nonces, g->rhs); LOG (g, "shrunken"); shrink_and_gate (g, falsifies); @@ -2823,7 +2875,7 @@ bool Closure::simplify_gates (int lit) { /*------------------------------------------------------------------------*/ // AND gates -Gate *Closure::find_and_lits (const vector &rhs, Gate *except) { +Gate *Closure::find_and_lits (vector &rhs, Gate *except) { CADICAL_assert (is_sorted (begin (rhs), end (rhs), sort_literals_by_var_smaller (internal))); return find_gate_lits (rhs, Gate_Type::And_Gate, except); @@ -2832,42 +2884,46 @@ Gate *Closure::find_and_lits (const vector &rhs, Gate *except) { // search for the gate in the hash-table. We cannot use find, as we might // be changing a gate, so there might be 2 gates with the same LHS (the one // we are changing and the other we are looking for) -Gate *Closure::find_gate_lits (const vector &rhs, Gate_Type typ, +Gate *Closure::find_gate_lits (vector &rhs, Gate_Type typ, Gate *except) { - Gate *g = new Gate; - g->tag = typ; - g->rhs = rhs; - g->hash = hash_lits (nonces, g->rhs); - g->lhs = 0; - g->garbage = false; #ifdef LOGGING - g->id = 0; + dummy_search_gate->id = 0; #endif - const auto &its = table.equal_range (g); Gate *h = nullptr; - for (auto it = its.first; it != its.second; ++it) { - LOG ((*it), "checking gate in the table"); - if (*it == except) - continue; - CADICAL_assert ((*it)->lhs != g->lhs); - if ((*it)->tag != g->tag) - continue; - if ((*it)->rhs != g->rhs) - continue; - h = *it; - break; + dummy_search_gate->tag = typ; + std::swap (dummy_search_gate->rhs, rhs); + CADICAL_assert (dummy_search_gate->lhs == 0); + CADICAL_assert (dummy_search_gate->garbage == false); + + if ((!except || !except->indexed)) { + auto git = table.find (dummy_search_gate); + if (git != std::end (table)) + h = *git; + CADICAL_assert (!except || h != except); + } else { + const auto &its = table.equal_range (dummy_search_gate); + for (auto it = its.first; it != its.second; ++it) { + LOG ((*it), "checking gate in the table"); + if (*it == except) + continue; + if ((*it)->tag != typ) + continue; + if ((*it)->rhs != dummy_search_gate->rhs) + continue; + h = *it; + break; + } } + std::swap (dummy_search_gate->rhs, rhs); if (h) { - LOG (g, "searching"); + LOG (dummy_search_gate, "searching"); LOG (h, "already existing"); - delete g; return h; } else { - LOG (g->rhs, "gate not found in table"); - delete g; + LOG (dummy_search_gate->rhs, "gate not found in table"); return nullptr; } } @@ -2891,7 +2947,7 @@ Gate *Closure::new_and_gate (Clause *base_clause, int lhs) { g->lhs = lhs; g->tag = Gate_Type::And_Gate; if (internal->lrat) { - g->neg_lhs_ids.push_back (LitClausePair (lhs, base_clause)); + g->neg_lhs_ids = LitClausePair (lhs, base_clause); for (auto i : lrat_chain_and_gate) g->pos_lhs_ids.push_back (i); #ifdef LOGGING @@ -2901,9 +2957,10 @@ Gate *Closure::new_and_gate (Clause *base_clause, int lhs) { [] (const LitClausePair &x) { return x.clause->id; }); LOG (result, "lrat chain positive (%d):", lhs); result.clear (); - transform (begin (g->neg_lhs_ids), end (g->neg_lhs_ids), - back_inserter (result), - [] (const LitClausePair &x) { return x.clause->id; }); + if (g->neg_lhs_ids ()) { + auto c = g->neg_lhs_ids.content; + result.push_back (c.clause->id); + } LOG (result, "lrat chain negative (%d):", lhs); #endif } @@ -2916,8 +2973,8 @@ Gate *Closure::new_and_gate (Clause *base_clause, int lhs) { if (internal->lrat) merge_and_gate_lrat_produce_lrat (g, h, reasons_lrat_src, reasons_lrat_usrc); - if (merge_literals_lrat (g, h, lhs, h->lhs, reasons_lrat_src, - reasons_lrat_usrc)) { + if (merge_literals (g, h, lhs, h->lhs, reasons_lrat_src, + reasons_lrat_usrc)) { LOG ("found merged literals"); ++internal->stats.congruence.ands; } @@ -2930,7 +2987,6 @@ Gate *Closure::new_and_gate (Clause *base_clause, int lhs) { g->garbage = false; g->indexed = true; g->shrunken = false; - g->hash = hash_lits (nonces, g->rhs); table.insert (g); ++internal->stats.congruence.gates; @@ -2942,6 +2998,7 @@ Gate *Closure::new_and_gate (Clause *base_clause, int lhs) { connect_goccs (g, lit); } } + ++internal->stats.congruence.and_gates; return g; } @@ -3072,7 +3129,7 @@ void Closure::check_not_tmp_binary_clause (Clause *c) { #else (void) c; #endif -}; +} Clause *Closure::maybe_promote_tmp_binary_clause (Clause *c) { CADICAL_assert (internal->lrat); @@ -3089,7 +3146,7 @@ Clause *Closure::maybe_promote_tmp_binary_clause (Clause *c) { Clause *res = add_binary_clause (c->literals[0], c->literals[1]); LOG (res, "promoted to"); return res; -}; +} Clause *Closure::add_tmp_binary_clause (int a, int b) { CADICAL_assert (internal->clause.empty ()); @@ -3127,9 +3184,9 @@ Gate *Closure::find_remaining_and_gate (Clause *base_clause, int lhs) { return nullptr; } - LOG ("trying to find AND gate with remaining LHS %d", (lhs)); - LOG ("negated LHS %d occurs times in %zd binary clauses", (not_lhs), - internal->noccs (-lhs)); + LOG ("trying to find AND gate with remaining LHS %s", LOGLIT (lhs)); + LOG ("negated LHS %s occurs times in %" PRId64 " binary clauses", + LOGLIT (not_lhs), internal->noccs (-lhs)); const size_t arity = lits.size () - 1; size_t matched = 0; @@ -3150,7 +3207,7 @@ Gate *Closure::find_remaining_and_gate (Clause *base_clause, int lhs) { ++matched; if (!(mark & 2)) { lrat_chain_and_gate.push_back (LitClausePair (other, w.clause)); - LOG ("pushing %d -> %zd", other, w.clause->id); + LOG ("pushing %d -> %" PRId64, other, w.clause->id); continue; } LOG ("marking %d mu4", other); @@ -3279,7 +3336,7 @@ void Closure::extract_and_gates_with_base_clause (Clause *c) { const size_t arity = size - 1; if (max_negbincount < arity) { LOG (c, - "all literals have less than %lu negated occurrences" + "all literals have less than %zu negated occurrences" "thus skipping", arity); if (internal->lrat) @@ -3305,7 +3362,7 @@ void Closure::extract_and_gates_with_base_clause (Clause *c) { } const size_t reduced_size = clause_size - reduced; CADICAL_assert (reduced_size); - LOG (c, "trying as base arity %lu AND gate", arity); + LOG (c, "trying as base arity %zu AND gate", arity); CADICAL_assert (begin (lits) + reduced_size <= end (lits)); MSORT (internal->opts.radixsortlim, begin (lits), begin (lits) + reduced_size, @@ -3321,8 +3378,9 @@ void Closure::extract_and_gates_with_base_clause (Clause *c) { if (c->garbage) break; const int lhs = lits[i]; - LOG ("trying LHS candidate literal %d with %ld negated occurrences", - (lhs), internal->noccs (-lhs)); + LOG ("trying LHS candidate literal %s with %" PRId64 + " negated occurrences", + LOGLIT (lhs), internal->noccs (-lhs)); if (first) { first = false; @@ -3347,7 +3405,7 @@ void Closure::extract_and_gates_with_base_clause (Clause *c) { } lrat_chain_and_gate.clear (); if (extracted) - LOG (c, "extracted %u with arity %lu AND base", extracted, arity); + LOG (c, "extracted %u with arity %zu AND base", extracted, arity); } void Closure::reset_and_gate_extraction () { @@ -3360,12 +3418,16 @@ void Closure::extract_and_gates () { if (!internal->opts.congruenceand) return; START (extractands); + marks.resize (internal->max_var * 2 + 3); init_and_gate_extraction (); - +#ifndef CADICAL_QUIET + const int64_t gates_before = internal->stats.congruence.and_gates; +#endif const size_t size = internal->clauses.size (); for (size_t i = 0; i < size && !internal->terminated_asynchronously (); - ++i) { // we can learn new binary clauses, but no for loop + ++i) { + // we can learn new binary clauses, but so no for loop CADICAL_assert (lrat_chain.empty ()); Clause *c = internal->clauses[i]; if (c->garbage) @@ -3380,6 +3442,11 @@ void Closure::extract_and_gates () { CADICAL_assert (lrat_chain.empty ()); } + VERBOSE (2, + "[congruence-%" PRId64 "] " + "found %" PRIu64 " AND gates", + internal->stats.congruence.rounds, + internal->stats.congruence.and_gates - gates_before); reset_and_gate_extraction (); STOP (extractands); } @@ -3415,6 +3482,8 @@ void inc_lits (vector &lits) { void Closure::check_ternary (int a, int b, int c) { CADICAL_assert (internal->clause.empty ()); + if (!internal->opts.check) + return; if (internal->lrat) return; auto &clause = internal->clause; @@ -3424,7 +3493,7 @@ void Closure::check_ternary (int a, int b, int c) { clause.push_back (c); internal->external->check_learned_clause (); if (internal->proof) { - const LRAT_ID id = internal->clause_id++; + const LRAT_ID id = ++internal->clause_id; internal->proof->add_derived_clause (id, false, clause, {}); internal->proof->delete_clause (id, false, clause); } @@ -3434,6 +3503,8 @@ void Closure::check_ternary (int a, int b, int c) { void Closure::check_binary_implied (int a, int b) { CADICAL_assert (internal->clause.empty ()); + if (!internal->opts.check) + return; if (internal->lrat) return; auto &clause = internal->clause; @@ -3445,6 +3516,8 @@ void Closure::check_binary_implied (int a, int b) { } void Closure::check_implied () { + if (!internal->opts.check) + return; if (internal->lrat) return; internal->external->check_learned_clause (); @@ -3481,8 +3554,6 @@ void Closure::add_xor_shrinking_proof_chain (Gate *g, int pivot) { LOG (pair.clause, "key %d", pair.current_lit); } #endif - // TODO Florian adjust indices of first depending on order... - // for (unsigned i = 0; i != end; ++i) { while (i && parity != parity_lits (clause)) inc_lits (clause); @@ -3524,6 +3595,8 @@ void Closure::check_xor_gate_implied (Gate const *const g) { if (internal->lrat) { return; } + if (!internal->opts.check) + return; const int lhs = g->lhs; LOG (g, "checking implied"); auto &clause = internal->clause; @@ -3542,7 +3615,7 @@ void Closure::check_xor_gate_implied (Gate const *const g) { inc_lits (clause); internal->external->check_learned_clause (); if (internal->proof) { - internal->proof->add_derived_clause (internal->clause_id, false, + internal->proof->add_derived_clause (++internal->clause_id, false, clause, {}); internal->proof->delete_clause (internal->clause_id, false, clause); } @@ -3551,7 +3624,7 @@ void Closure::check_xor_gate_implied (Gate const *const g) { clause.clear (); } -Gate *Closure::find_xor_lits (const vector &rhs) { +Gate *Closure::find_xor_lits (vector &rhs) { CADICAL_assert (is_sorted (begin (rhs), end (rhs), sort_literals_by_var_smaller (internal))); return find_gate_lits (rhs, Gate_Type::XOr_Gate); @@ -3579,19 +3652,16 @@ bool Closure::normalize_ite_lits_gate (Gate *g) { CADICAL_assert (g->pos_lhs_ids.size () == 4); std::swap (g->pos_lhs_ids[0], g->pos_lhs_ids[2]); std::swap (g->pos_lhs_ids[1], g->pos_lhs_ids[3]); - const int8_t flag = g->degenerated_ite; + const int8_t flag = g->degenerated_gate; const int8_t plus_then = flag & NO_PLUS_THEN; const int8_t neg_then = flag & NO_NEG_THEN; const int8_t plus_else = flag & NO_PLUS_ELSE; const int8_t neg_else = flag & NO_NEG_ELSE; - g->degenerated_ite = (plus_then ? Special_ITE_GATE::NO_PLUS_ELSE - : Special_ITE_GATE::NORMAL) | - (neg_then ? Special_ITE_GATE::NO_NEG_ELSE - : Special_ITE_GATE::NORMAL) | - (plus_else ? Special_ITE_GATE::NO_PLUS_THEN - : Special_ITE_GATE::NORMAL) | - (neg_else ? Special_ITE_GATE::NO_NEG_THEN - : Special_ITE_GATE::NORMAL); + g->degenerated_gate = + (plus_then ? Special_Gate::NO_PLUS_ELSE : Special_Gate::NORMAL) | + (neg_then ? Special_Gate::NO_NEG_ELSE : Special_Gate::NORMAL) | + (plus_else ? Special_Gate::NO_PLUS_THEN : Special_Gate::NORMAL) | + (neg_else ? Special_Gate::NO_NEG_THEN : Special_Gate::NORMAL); CADICAL_assert (g->pos_lhs_ids[0].current_lit == rhs[1]); CADICAL_assert (g->pos_lhs_ids[2].current_lit == rhs[2]); if (internal->lrat) @@ -3609,19 +3679,16 @@ bool Closure::normalize_ite_lits_gate (Gate *g) { CADICAL_assert (g->pos_lhs_ids.size () == 4); std::swap (g->pos_lhs_ids[0], g->pos_lhs_ids[1]); std::swap (g->pos_lhs_ids[2], g->pos_lhs_ids[3]); - const int8_t flag = g->degenerated_ite; + const int8_t flag = g->degenerated_gate; const int8_t plus_then = flag & NO_PLUS_THEN; const int8_t neg_then = flag & NO_NEG_THEN; const int8_t plus_else = flag & NO_PLUS_ELSE; const int8_t neg_else = flag & NO_NEG_ELSE; - g->degenerated_ite = (plus_then ? Special_ITE_GATE::NO_NEG_THEN - : Special_ITE_GATE::NORMAL) | - (neg_then ? Special_ITE_GATE::NO_PLUS_THEN - : Special_ITE_GATE::NORMAL) | - (plus_else ? Special_ITE_GATE::NO_NEG_ELSE - : Special_ITE_GATE::NORMAL) | - (neg_else ? Special_ITE_GATE::NO_PLUS_ELSE - : Special_ITE_GATE::NORMAL); + g->degenerated_gate = + (plus_then ? Special_Gate::NO_NEG_THEN : Special_Gate::NORMAL) | + (neg_then ? Special_Gate::NO_PLUS_THEN : Special_Gate::NORMAL) | + (plus_else ? Special_Gate::NO_NEG_ELSE : Special_Gate::NORMAL) | + (neg_else ? Special_Gate::NO_PLUS_ELSE : Special_Gate::NORMAL); CADICAL_assert (g->pos_lhs_ids[0].current_lit == rhs[1]); CADICAL_assert (g->pos_lhs_ids[2].current_lit == rhs[2]); // incorrect as we have not negated the LHS yet! @@ -3635,7 +3702,7 @@ bool Closure::normalize_ite_lits_gate (Gate *g) { #ifndef CADICAL_NDEBUG bool is_tautological_ite_gate (Gate *g) { CADICAL_assert (g->tag == Gate_Type::ITE_Gate); - CADICAL_assert (g->rhs.size () == 3); + CADICAL_assert (g->arity () == 3); const int cond_lit = g->rhs[0]; const int then_lit = g->rhs[1]; const int else_lit = g->rhs[2]; @@ -3713,6 +3780,7 @@ LRAT_ID Closure::simplify_and_add_to_proof_chain (vector &unsimplified, } } else { id = check_and_add_to_proof_chain (clause); + CADICAL_assert (id > 0); add_clause_to_chain (clause, id); } } else { @@ -3765,12 +3833,14 @@ void Closure::simplify_and_sort_xor_lrat_clauses ( void Closure::add_xor_matching_proof_chain ( Gate *g, int lhs1, const vector &clauses2, int lhs2, vector &to_lrat, vector &back_lrat) { - if (lhs1 == lhs2) - return; if (!internal->proof) return; + if (lhs1 == lhs2) + return; CADICAL_assert (unsimplified.empty ()); - unsimplified = g->rhs; + unsimplified.reserve (g->arity ()); + for (auto lit : g->rhs) + unsimplified.push_back (lit); vector first; vector second; if (internal->lrat) { @@ -3785,7 +3855,7 @@ void Closure::add_xor_matching_proof_chain ( for (auto pair : first) { bool first = pair.current_lit & 1; int rest = pair.current_lit >> 1; - rest &= ~(1 << (g->rhs.size () - 1)); + rest &= ~(1 << (g->arity () - 1)); if (first == (lhs1 > 0)) { first_ids.push_back (LitIdPair (rest, pair.clause->id)); } else { @@ -3796,7 +3866,7 @@ void Closure::add_xor_matching_proof_chain ( for (auto pair : second) { bool first = pair.current_lit & 1; int rest = pair.current_lit >> 1; - rest &= ~(1 << (g->rhs.size () - 1)); + rest &= ~(1 << (g->arity () - 1)); if (first == (lhs2 < 0)) { first_ids.push_back (LitIdPair (rest, pair.clause->id)); } else { @@ -3804,7 +3874,6 @@ void Closure::add_xor_matching_proof_chain ( } LOG (pair.clause, "key %d", pair.current_lit); } - // TODO Florian: resort and ids after every round do { vector first_tmp; vector second_tmp; @@ -3873,7 +3942,7 @@ void Closure::add_xor_matching_proof_chain ( Gate *Closure::new_xor_gate (const vector &glauses, int lhs) { rhs.clear (); - + rhs.reserve (lits.size ()); for (auto lit : lits) { if (lhs != lit && -lhs != lit) { CADICAL_assert (lit > 0); @@ -3888,8 +3957,7 @@ Gate *Closure::new_xor_gate (const vector &glauses, std::vector reasons_implication, reasons_back; add_xor_matching_proof_chain (g, g->lhs, glauses, lhs, reasons_implication, reasons_back); - if (merge_literals_lrat (g->lhs, lhs, reasons_implication, - reasons_back)) { + if (merge_literals (g->lhs, lhs, reasons_implication, reasons_back)) { ++internal->stats.congruence.xors; } delete_proof_chain (); @@ -3902,9 +3970,9 @@ Gate *Closure::new_xor_gate (const vector &glauses, g->garbage = false; g->indexed = true; g->shrunken = false; - g->hash = hash_lits (nonces, g->rhs); - for (auto pair : glauses) - g->pos_lhs_ids.push_back (pair); + if (internal->lrat) + for (auto pair : glauses) + g->pos_lhs_ids.push_back (pair); table.insert (g); ++internal->stats.congruence.gates; #ifdef LOGGING @@ -3916,8 +3984,10 @@ Gate *Closure::new_xor_gate (const vector &glauses, connect_goccs (g, lit); } } + ++internal->stats.congruence.xor_gates; return g; } + uint32_t Closure::number_from_xor_reason_reversed (const std::vector &rhs) { uint32_t n = 0; @@ -4026,7 +4096,7 @@ void Closure::init_xor_gate_extraction (std::vector &candidates) { CONTINUE_COUNTING_NEXT_CLAUSE:; } - LOG ("considering %zd out of %zd", candidates.size (), + LOG ("considering %zu out of %" PRId64, candidates.size (), internal->irredundant ()); const unsigned rounds = internal->opts.congruencexorcounts; #ifdef LOGGING @@ -4072,7 +4142,7 @@ void Closure::init_xor_gate_extraction (std::vector &candidates) { if (!removed) break; - LOG ("after round %d, %zd (%ld %%) remain", round, candidates.size (), + LOG ("after round %d, %zd (%zu %%) remain", round, candidates.size (), candidates.size () / (1 + original_size) * 100); } @@ -4080,6 +4150,10 @@ void Closure::init_xor_gate_extraction (std::vector &candidates) { for (auto lit : *c) internal->occs (lit).push_back (c); } + + VERBOSE ( + 2, "connected %zu large clauses %.0f%%", candidates.size (), + percent (candidates.size (), internal->stats.current.irredundant)); } Clause *Closure::find_large_xor_side_clause (std::vector &lits) { @@ -4107,7 +4181,7 @@ Clause *Closure::find_large_xor_side_clause (std::vector &lits) { CADICAL_assert (least_occurring_literal); LOG ("searching XOR side clause watched by %d#%u", least_occurring_literal, count_least_occurring); - LOG ("searching for size %ld", size_lits); + LOG ("searching for size %zu", size_lits); for (auto c : internal->occs (least_occurring_literal)) { LOG (c, "checking"); CADICAL_assert (c->size != 2); // TODO kissat has break @@ -4252,6 +4326,7 @@ void Closure::extract_xor_gates_with_base_clause (Clause *c) { inc_lits (lits); LOG (lits, "found all needed %u matching clauses:", found); CADICAL_assert (found == 1u << arity); + (void) found; if (negated) { auto p = begin (lits); int lit; @@ -4279,6 +4354,9 @@ void Closure::extract_xor_gates () { if (!internal->opts.congruencexor) return; START (extractxors); +#ifndef CADICAL_QUIET + const int64_t gates_before = internal->stats.congruence.xor_gates; +#endif LOG ("starting extracting XOR"); std::vector candidates = {}; init_xor_gate_extraction (candidates); @@ -4289,6 +4367,9 @@ void Closure::extract_xor_gates () { continue; extract_xor_gates_with_base_clause (c); } + VERBOSE (2, "[congruence-%" PRId64 "] found %" PRId64 " XOR gates", + internal->stats.congruence.rounds, + internal->stats.congruence.xor_gates - gates_before); reset_xor_gate_extraction (); STOP (extractxors); } @@ -4300,11 +4381,11 @@ void Closure::find_units () { RESTART: if (!internal->flags (v).active ()) continue; - for (int sgn = -1; sgn < 1; sgn += 2) { + for (int sgn = -1; sgn <= 1; sgn += 2) { int lit = v * sgn; for (auto w : internal->watches (lit)) { if (!w.binary ()) - continue; // todo check that binaries first + break; const int other = w.blit; if (marked (-other)) { LOG (w.clause, "binary clause %d %d and %d %d give unit %d", lit, @@ -4332,6 +4413,7 @@ void Closure::find_units () { CADICAL_assert (internal->analyzed.empty ()); } LOG ("found %zd units", units); + (void) units; } void Closure::find_equivalences () { @@ -4394,7 +4476,6 @@ void Closure::find_equivalences () { lit, other, internal->lrat ? marked_mu1 (-other).clause : nullptr, w.clause)) { - ++internal->stats.congruence.congruent; } unmark_all (); if (internal->unsat) @@ -4407,7 +4488,11 @@ void Closure::find_equivalences () { unmark_all (); } CADICAL_assert (internal->analyzed.empty ()); - LOG ("found %zd equivalences", schedule.size ()); + // force removal of memory + mu1_ids.clear (); + shrink_vector (mu1_ids); + VERBOSE (2, "[congruence-%" PRId64 "] found %zd equivalences", + internal->stats.congruence.rounds, schedule.size ()); } /*------------------------------------------------------------------------*/ @@ -4428,22 +4513,29 @@ void Closure::rewrite_and_gate (Gate *g, int dst, int src, LRAT_ID id1, CADICAL_assert (src); CADICAL_assert (dst); CADICAL_assert (internal->val (src) == internal->val (dst)); - GatesTable::iterator git = (g->indexed ? table.find (g) : end (table)); LOG (g, "rewriting %d into %d in", src, dst); + GatesTable::iterator git = (g->indexed ? table.find (g) : end (table)); + CADICAL_assert (!g->indexed || git != table.end ()); int clashing = 0, falsifies = 0; unsigned dst_count = 0, not_dst_count = 0; auto q = begin (g->rhs); + if (g->degenerated_gate != Special_Gate::NORMAL) { + g->lhs = find_eager_representative (g->lhs); + LOG (g, "updating LHS to"); + } for (int &lit : g->rhs) { - if (lit == src) - lit = dst; - if (lit == -g->lhs) { + if (lit == -g->lhs || (lit == src && dst == -g->lhs)) { LOG ("found negated LHS literal %d", lit); clashing = lit; - g->degenerated_and_neg = true; + g->degenerated_gate = DEGENERATED_AND; break; } + if (lit == src) + lit = dst; if (lit == g->lhs) - g->degenerated_and_pos = true; + g->degenerated_gate = Special_Gate::DEGENERATED_AND; + if (-lit == g->lhs) + g->degenerated_gate = Special_Gate::DEGENERATED_AND; const signed char val = internal->val (lit); if (val > 0) { continue; @@ -4456,7 +4548,7 @@ void Closure::rewrite_and_gate (Gate *g, int dst, int src, LRAT_ID id1, if (lit == dst) { if (not_dst_count) { LOG ("clashing literals %d and %d", (-dst), (dst)); - clashing = -dst; + clashing = dst; break; } if (dst_count++) @@ -4476,6 +4568,11 @@ void Closure::rewrite_and_gate (Gate *g, int dst, int src, LRAT_ID id1, } LOG (lrat_chain, "lrat chain after rewriting"); + if (q != end (g->rhs)) { + g->rhs.resize (q - begin (g->rhs)); + g->shrunken = true; + } + if (internal->lrat) { // updating reasons in the chain. #ifdef LOGGING for (auto litId : g->pos_lhs_ids) { @@ -4484,30 +4581,40 @@ void Closure::rewrite_and_gate (Gate *g, int dst, int src, LRAT_ID id1, #endif // We remove all assigned literals except the falsified literal such // that we can produce an LRAT chain + // + // Obviously there are exceptions that make everything more complicated, + // in particular when we do not need the gate anymore, like in: 3 := And + // 1@0+=1 3 Then CADICAL_assertion will fail (like the `CADICAL_assert (i)`) but it does + // not matter. size_t i = 0, size = g->pos_lhs_ids.size (); bool found = false; CADICAL_assert (!falsifies || !clashing); const int orig_falsifies = falsifies == dst ? src : falsifies; const int orig_clashing = - clashing == -dst ? -src : (clashing == dst ? src : clashing); + clashing == -dst ? -src : (clashing == dst ? src : clashing); int keep_clashing = clashing; LOG ("keeping chain for falsifies: %d aka %d and clashing: %d aka %d", falsifies, orig_falsifies, clashing, orig_clashing); + // We do not need all the clauses. Therefore, we keep only the + // ones required by clashing. for (size_t j = 0; j < size; ++j) { LOG (g->pos_lhs_ids[j].clause, "looking at %d [%zd %zd] with val %d", g->pos_lhs_ids[j].current_lit, i, j, internal->val (g->pos_lhs_ids[i].current_lit)); g->pos_lhs_ids[i] = g->pos_lhs_ids[j]; - if (keep_clashing && g->pos_lhs_ids[i].current_lit != orig_clashing && - g->pos_lhs_ids[i].current_lit != -orig_clashing && - g->pos_lhs_ids[i].current_lit != keep_clashing && - g->pos_lhs_ids[i].current_lit != -keep_clashing) + int &curr = g->pos_lhs_ids[i].current_lit; + // if the gate is not normal, we rewrite the LHS. Therefore, we + // also have to update the literal here. + if (g->degenerated_gate != Special_Gate::NORMAL && + find_eager_representative (curr) == g->lhs) + curr = find_eager_representative (curr); + if (keep_clashing && curr != orig_clashing && + curr != -orig_clashing && curr != keep_clashing && + curr != -keep_clashing) continue; - if (internal->val (g->pos_lhs_ids[i].current_lit) && - g->pos_lhs_ids[i].current_lit != src && - g->pos_lhs_ids[i].current_lit != orig_falsifies) + if (internal->val (curr) && curr != src && curr != orig_falsifies) continue; - if (g->pos_lhs_ids[i].current_lit == dst) { + if (curr == dst) { if (!found) found = true; else @@ -4526,21 +4633,18 @@ void Closure::rewrite_and_gate (Gate *g, int dst, int src, LRAT_ID id1, ++i; } LOG ("resizing to %zd", i); - CADICAL_assert (i); + CADICAL_assert (i || (g->arity () == 1 && g->rhs[0] == g->lhs)); g->pos_lhs_ids.resize (i); } - if (q != end (g->rhs)) { - g->rhs.resize (q - begin (g->rhs)); - g->shrunken = true; - } CADICAL_assert (dst_count <= 2); CADICAL_assert (not_dst_count <= 1); std::vector reasons_lrat_src, reasons_lrat_usrc; shrink_and_gate (g, falsifies, clashing); LOG (g, "rewritten as"); - CADICAL_assert (!internal->lrat || !g->pos_lhs_ids.empty ()); + CADICAL_assert (!internal->lrat || !g->pos_lhs_ids.empty () || + (g->arity () == 1 && g->rhs[0] == g->lhs)); // check_and_gate_implied (g); update_and_gate (g, git, src, dst, id1, id2, falsifies, clashing); ++internal->stats.congruence.rewritten_ands; @@ -4609,7 +4713,7 @@ void Closure::rewrite_xor_gate (Gate *g, int dst, int src) { GatesTable::iterator git = (g->indexed ? table.find (g) : end (table)); size_t j = 0, dst_count = 0; bool original_dst_negated = (dst < 0); - dst = abs (dst); + dst = internal->vidx (dst); unsigned negate = original_dst_negated; const size_t size = g->rhs.size (); for (size_t i = 0; i < size; ++i) { @@ -4646,14 +4750,10 @@ void Closure::rewrite_xor_gate (Gate *g, int dst, int src) { g->shrunken = true; CADICAL_assert (is_sorted (begin (g->rhs), end (g->rhs), sort_literals_by_var_smaller (internal))); - g->hash = hash_lits (nonces, g->rhs); } else if (j != size) { g->shrunken = true; g->rhs.resize (j); sort_literals_by_var (g->rhs); - g->hash = hash_lits ( - nonces, - g->rhs); // all but one (the dst) is sorted correctly actually } else { CADICAL_assert (j == size); sort_literals_by_var (g->rhs); @@ -4674,7 +4774,7 @@ void Closure::rewrite_xor_gate (Gate *g, int dst, int src) { } check_xor_gate_implied (g); - // TODO stats + ++internal->stats.congruence.rewritten_xors; } // update to produce proofs @@ -4685,7 +4785,7 @@ void Closure::simplify_xor_gate (Gate *g) { check_xor_gate_implied (g); unsigned negate = 0; GatesTable::iterator git = (g->indexed ? table.find (g) : end (table)); - const size_t size = g->rhs.size (); + const size_t size = g->arity (); size_t j = 0; for (size_t i = 0; i < size; ++i) { int lit = g->rhs[i]; @@ -4707,9 +4807,6 @@ void Closure::simplify_xor_gate (Gate *g) { g->rhs.resize (j); CADICAL_assert (is_sorted (begin (g->rhs), end (g->rhs), sort_literals_by_var_smaller (internal))); - g->hash = hash_lits (nonces, g->rhs); - } else { - CADICAL_assert (g->hash == hash_lits (nonces, g->rhs)); } check_xor_gate_implied (g); @@ -4797,10 +4894,6 @@ size_t Closure::propagate_units_and_equivalences () { CADICAL_assert (g->tag == Gate_Type::ITE_Gate || g->tag == Gate_Type::XOr_Gate || !gate_contains (g, -g->lhs)); - // TODO: this would be nice to have! - // CADICAL_assert (g->tag != Gate_Type::ITE_Gate || (g->rhs.size() == 3 - // && g->rhs[1] != -g->lhs && g->rhs[2] != -g->lhs)); - // CADICAL_assert (table.count(g) == 1); for (auto lit : g->rhs) { CADICAL_assert (!internal->val (lit)); CADICAL_assert (representative (lit) == lit); @@ -4857,7 +4950,7 @@ void Closure::reset_closure () { for (auto c : extra_clauses) { CADICAL_assert (!c->garbage); internal->proof->delete_clause (c); - delete c; + delete[] c; } extra_clauses.clear (); } else { @@ -4869,40 +4962,12 @@ void Closure::reset_extraction () { full_watching = true; if (!internal->unsat && !internal->propagate ()) { internal->learn_empty_clause (); + return; } -#if 0 - // remove delete watched clauses from the watch list - for (auto v : internal->vars) { - for (auto sgn = -1; sgn <= 1; sgn += 2) { - const int lit = v * sgn; - auto &watchers = internal->watches (lit); - const size_t size = watchers.size (); - size_t j = 0; - for (size_t i = 0; i != size; ++i) { - const auto w = watchers[i]; - watchers[j] = watchers[i]; - if (!w.clause->garbage) - ++j; - } - watchers.resize(j); - } - } - // watch the remaining non-watched clauses - for (auto c : new_unwatched_binary_clauses) - internal->watch_clause (c); - new_unwatched_binary_clauses.clear(); - for (auto c : internal->clauses) { - if (c->garbage) - continue; - if (c->size != 2) - internal->watch_clause (c); - } -#else // simpler implementation new_unwatched_binary_clauses.clear (); internal->clear_watches (); internal->connect_watches (); -#endif } void Closure::forward_subsume_matching_clauses () { @@ -4910,34 +4975,42 @@ void Closure::forward_subsume_matching_clauses () { reset_closure (); std::vector matchable; matchable.resize (internal->max_var + 1); +#ifndef CADICAL_QUIET size_t count_matchable = 0; - +#endif for (auto idx : internal->vars) { - if (!internal->flags (idx).active ()) + if (!internal->flags (idx).active ()) { + (void) find_representative_and_compress_no_proofs (idx); continue; + } const int lit = idx; - const int repr = find_representative (lit); + const int repr = find_representative_and_compress_no_proofs (lit); if (lit == repr) continue; - const int repr_idx = abs (repr); + const int repr_idx = internal->vidx (repr); if (!matchable[idx]) { LOG ("matchable %d", idx); matchable[idx] = true; - count_matchable++; +#ifndef CADICAL_QUIET + ++count_matchable; +#endif } if (!matchable[repr_idx]) { LOG ("matchable %d", repr_idx); matchable[repr_idx] = true; - count_matchable++; +#ifndef CADICAL_QUIET + ++count_matchable; +#endif } } - LOG ("found %.0f%%", - (double) count_matchable / - (double) (internal->max_var ? internal->max_var : 1)); - std::vector candidates; + VERBOSE (3, "congruence found %zu matchable variables %.0f%%", + count_matchable, percent (count_matchable, internal->max_var)); + std::vector candidates; auto &analyzed = internal->analyzed; + size_t potential = 0; + (void) potential; for (auto *c : internal->clauses) { if (c->garbage) @@ -4946,6 +5019,7 @@ void Closure::forward_subsume_matching_clauses () { continue; if (c->size == 2) continue; + ++potential; CADICAL_assert (analyzed.empty ()); bool contains_matchable = false; for (auto lit : *c) { @@ -4963,7 +5037,7 @@ void Closure::forward_subsume_matching_clauses () { contains_matchable = true; } - const int repr = find_representative (lit); + const int repr = find_representative_and_compress_no_proofs (lit); CADICAL_assert (!internal->val (repr)); if (marked (repr)) continue; @@ -4990,19 +5064,30 @@ void Closure::forward_subsume_matching_clauses () { candidates.push_back (c); } + VERBOSE (3, + "[congruence-%" PRId64 "] considering %zu " + "matchable subsumption candidates out of %zu %.0f%%", + internal->stats.congruence.rounds, candidates.size (), potential, + percent (candidates.size (), potential)); + rsort (begin (candidates), end (candidates), smaller_clause_size_rank ()); size_t tried = 0, subsumed = 0; + (void) tried, (void) subsumed; internal->init_occs (); for (auto c : candidates) { - CADICAL_assert (c->size != 2); - // TODO if terminated + CADICAL_assert (c.size != 2); + if (internal->terminated_asynchronously ()) + break; ++tried; - if (find_subsuming_clause (c)) { + if (find_subsuming_clause (c.clause)) { ++subsumed; } } - LOG ("[congruence] subsumed %.0f%%", - (double) subsumed / (double) (tried ? tried : 1)); + VERBOSE (3, + "[congruence-%" PRId64 "] subsumed %zu " + "clauses out of %zu tried %.0f%% clauses", + internal->stats.congruence.rounds, subsumed, tried, + percent (subsumed, tried)); STOP (congruencematching); } @@ -5046,7 +5131,7 @@ bool Closure::find_subsuming_clause (Clause *subsumed) { Clause *subsuming = nullptr; for (auto lit : *subsumed) { CADICAL_assert (internal->val (lit) <= 0); - const int repr_lit = find_representative (lit); + const int repr_lit = find_representative_already_compressed (lit); const signed char repr_val = internal->val (repr_lit); CADICAL_assert (repr_val <= 0); if (repr_val < 0) @@ -5061,14 +5146,14 @@ bool Closure::find_subsuming_clause (Clause *subsumed) { LOG (subsumed, "trying to forward subsume"); for (auto lit : *subsumed) { - const int repr_lit = find_representative (lit); - const size_t count = internal->occs (lit).size (); + const int repr_lit = find_representative_already_compressed (lit); + const size_t count = internal->occs (repr_lit).size (); CADICAL_assert (count <= UINT_MAX); if (count < count_least_occurring) { count_least_occurring = count; least_occuring_lit = repr_lit; } - for (auto d : internal->occs (lit)) { + for (auto d : internal->occs (repr_lit)) { CADICAL_assert (!d->garbage); CADICAL_assert (subsumed != d); if (!subsumed->redundant && d->redundant) @@ -5078,7 +5163,8 @@ bool Closure::find_subsuming_clause (Clause *subsumed) { if (v < 0) continue; CADICAL_assert (!v); - const int repr_other = find_representative (other); + const int repr_other = + find_representative_already_compressed (other); if (!marked (repr_other)) goto CONTINUE_WITH_NEXT_CLAUSE; LOG ("subsuming due to %d -> %d", other, repr_other); @@ -5093,7 +5179,7 @@ bool Closure::find_subsuming_clause (Clause *subsumed) { FOUND_SUBSUMING: for (auto lit : *subsumed) { - const int repr_lit = find_representative (lit); + const int repr_lit = find_representative_already_compressed (lit); const signed char v = internal->val (lit); if (!v) marked (repr_lit) = 0; @@ -5126,28 +5212,30 @@ void Closure::produce_ite_merge_then_else_reasons ( return; check_correct_ite_flags (g); // no merge is happening actually - CADICAL_assert (g->rhs[1] == find_eager_representative(g->rhs[1]) || g->rhs[2] == find_eager_representative(g->rhs[2])); - if (find_eager_representative (g->lhs) == g->rhs[1] || find_eager_representative (g->lhs) == g->rhs[2]) + CADICAL_assert (g->rhs[1] == find_eager_representative (g->rhs[1]) || + g->rhs[2] == find_eager_representative (g->rhs[2])); + if (find_eager_representative (g->lhs) == g->rhs[1] || + find_eager_representative (g->lhs) == g->rhs[2]) return; if ((g->rhs[1] == src && g->lhs == dst && g->rhs[2] == g->lhs) || (g->rhs[2] == src && g->lhs == dst && g->rhs[1] == g->lhs) || (g->rhs[1] == -src && g->lhs == -dst && g->rhs[2] == g->lhs) || (g->rhs[2] == -src && g->lhs == -dst && g->rhs[1] == g->lhs)) return; - check_ite_lrat_reasons (g, false); - CADICAL_assert (g->rhs.size () == 3); + check_ite_lrat_reasons (g); + CADICAL_assert (g->arity () == 3); CADICAL_assert (src == g->rhs[1] || src == g->rhs[2]); CADICAL_assert (dst == g->rhs[1] || dst == g->rhs[2]); - const int8_t flag = g->degenerated_ite; + const int8_t flag = g->degenerated_gate; CADICAL_assert (!ite_flags_no_then_clauses (flag)); // e = lhs: already merged CADICAL_assert (!ite_flags_no_else_clauses (flag)); // t = lhs: already merged produce_rewritten_clause_lrat (g->pos_lhs_ids, g->lhs, false); if (ite_flags_neg_cond_lhs (flag)) { LOG ("degenerated case with lhs = -cond"); - LOG (g->pos_lhs_ids[0].clause, "1:"); - LOG (g->pos_lhs_ids[1].clause, "2:"); - reasons_back.push_back (g->pos_lhs_ids[0].clause->id); - reasons_implication.push_back (g->pos_lhs_ids[1].clause->id); + LOG (g->pos_lhs_ids[1].clause, "1:"); + LOG (g->pos_lhs_ids[2].clause, "2:"); + reasons_back.push_back (g->pos_lhs_ids[1].clause->id); + reasons_implication.push_back (g->pos_lhs_ids[2].clause->id); return; } if (ite_flags_cond_lhs (flag)) { @@ -5170,21 +5258,28 @@ void Closure::rewrite_ite_gate_update_lrat_reasons (Gate *g, int src, return; LOG (g, "updating lrat from"); for (auto &litId : g->pos_lhs_ids) { - CADICAL_assert (litId.clause); + CADICAL_assert (litId.clause || g->degenerated_gate != Special_Gate::NORMAL); if (litId.current_lit == src) litId.current_lit = dst; if (litId.current_lit == -src) litId.current_lit = -dst; } - check_ite_lrat_reasons (g, false); + check_ite_lrat_reasons (g); } +// Transforms an ITE gate to an AND gate +// +// In essence the transformation is simple for LRAT: two long clause need to +// be resolved to get a binary and one long clause stays the long ternary +// clause for the resulting gate. There are two things to be careful: we +// have to rewrite the LHS too, because if it appears on the RHS too, it +// will not get rewritten and the proof will not work. bool Closure::rewrite_ite_gate_to_and ( Gate *g, int src, int dst, size_t idx1, size_t idx2, int cond_lit_to_learn_if_degenerated) { CADICAL_assert (internal->lrat_chain.empty ()); CADICAL_assert (!g->garbage); - LOG (g, "rewriting to proper AND"); + LOG (g, "rewriting to proper AND gate, namely"); if (internal->val (g->lhs) > 0) { { const int lit = g->rhs[0]; @@ -5234,12 +5329,59 @@ bool Closure::rewrite_ite_gate_to_and ( } return true; } + if (src == g->lhs) + g->lhs = dst; + else if (src == -g->lhs) + g->lhs = -dst; if (!internal->lrat) return false; - LOG ("updating flags"); - g->degenerated_and_neg = (g->rhs[1] == -g->lhs || g->rhs[0] == -g->lhs); - g->degenerated_and_pos = (g->rhs[0] == g->lhs || g->rhs[1] == g->lhs); - CADICAL_assert (g->rhs.size () == 3); + LOG (g, "updating flags for"); + + if (g->rhs[1] == -g->lhs || g->rhs[0] == -g->lhs) + g->degenerated_gate = Special_Gate::DEGENERATED_AND; + + if (g->degenerated_gate == Special_Gate::DEGENERATED_AND || + g->degenerated_gate == Special_Gate::DEGENERATED_AND_LHS_FALSE) { + LOG ("degenerated AND gate"); + CADICAL_assert (g->rhs[1] == -g->lhs || g->rhs[0] == -g->lhs); + if (g->rhs[1] == -g->lhs) { + if (g->rhs[0] == -g->lhs) { + // -2 := ITE 1 2 2: we need + // we need 2 3 + LitClausePair &p1 = g->pos_lhs_ids[2]; + LitClausePair &p2 = g->pos_lhs_ids[3]; + p1.clause = produce_rewritten_clause_lrat (p1.clause); + p2.clause = produce_rewritten_clause_lrat (p2.clause); + CADICAL_assert (p1.clause); + CADICAL_assert (p2.clause); + lrat_chain.push_back (p1.clause->id); + lrat_chain.push_back (p2.clause->id); + // push_id_and_rewriting_lrat_unit (g->pos_lhs_ids[0].clause, + // Rewrite (), lrat_chain); + } else { + // -1 := ITE -4 1 -1 we need 3 + // -2 := ITE 1 2 2 we need 2 + LOG ("g->rhs[0] != -g->lhs"); + const int idx = (g->lhs == g->rhs[2]) ? 3 : 2; + push_id_and_rewriting_lrat_unit (g->pos_lhs_ids[idx].clause, + Rewrite (), lrat_chain); + } + learn_congruence_unit (-g->lhs); + return true; + } + if (g->rhs[0] == -g->lhs) { + LOG ("g->rhs[0] == -g->lhs"); + Clause *c = + g->lhs > 0 ? g->pos_lhs_ids[1].clause : g->pos_lhs_ids[0].clause; + push_id_and_rewriting_lrat_unit (c, Rewrite (), lrat_chain); + learn_congruence_unit (-g->lhs); + return true; + } + } + if (g->rhs[0] == g->lhs || g->rhs[1] == g->lhs) + g->degenerated_gate = Special_Gate::DEGENERATED_AND; + + CADICAL_assert (g->arity () == 3); CADICAL_assert (g->pos_lhs_ids.size () == 4); CADICAL_assert (idx1 < g->pos_lhs_ids.size ()); CADICAL_assert (idx2 < g->pos_lhs_ids.size ()); @@ -5286,7 +5428,7 @@ bool Closure::rewrite_ite_gate_to_and ( [] (LitClausePair l) { return l.clause->size == 3; }); CADICAL_assert (long_clause != end (g->pos_lhs_ids)); LOG (long_clause->clause, "new long clause"); - g->neg_lhs_ids.push_back (*long_clause); + g->neg_lhs_ids = *long_clause; g->pos_lhs_ids.erase (long_clause); CADICAL_assert (g->pos_lhs_ids.size () == 1); @@ -5318,6 +5460,8 @@ void Closure::produce_ite_merge_lhs_then_else_reasons ( std::vector &reasons_back, std::vector &reasons_unit, bool rewritting_then, bool &learn_units) { + if (!internal->proof) + return; const size_t idx1 = rewritting_then ? 0 : 2; const size_t idx2 = idx1 + 1; const size_t other_idx1 = rewritting_then ? 2 : 0; @@ -5328,10 +5472,7 @@ void Closure::produce_ite_merge_lhs_then_else_reasons ( const int repr_cond_lit = find_eager_representative (g->rhs[0]); const int repr_lit_to_merge = find_eager_representative (lit_to_merge); const int repr_other_lit = find_eager_representative (other_lit); - const int repr_lhs = find_eager_representative(g->lhs); - if (!internal->proof) - return; - + const int repr_lhs = find_eager_representative (g->lhs); LOG ("cond: %d, merging %d and rewriting to %d", cond_lit, lit_to_merge, other_lit); @@ -5342,7 +5483,8 @@ void Closure::produce_ite_merge_lhs_then_else_reasons ( if (repr_lhs == -repr_other_lit) { LOG ("special case: %s=%s, checking if other: %s %s", LOGLIT (g->lhs), LOGLIT (-lit_to_merge), LOGLIT (cond_lit), LOGLIT (other_lit)); - CADICAL_assert (repr_lit_to_merge != -repr_lhs); // should have been rewritten before + CADICAL_assert (repr_lit_to_merge != + -repr_lhs); // should have been rewritten before if (rewritting_then && repr_cond_lit == repr_lhs) { LOG ("t=-lhs/c=lhs"); @@ -5460,7 +5602,6 @@ void Closure::produce_ite_merge_lhs_then_else_reasons ( } if (other_lit == repr_lhs) { - LOG ("TODO FIX ME t=-lhs/e=lhs"); learn_units = true; // in the other direction we are merging a literal with itself g->pos_lhs_ids[idx1].clause = produce_rewritten_clause_lrat ( @@ -5531,7 +5672,7 @@ void Closure::rewrite_ite_gate (Gate *g, int dst, int src) { return; LOG (g, "rewriting %d by %d in", src, dst); CADICAL_assert (!g->shrunken); - CADICAL_assert (g->rhs.size () == 3); + CADICAL_assert (g->arity () == 3); CADICAL_assert (!internal->lrat || g->pos_lhs_ids.size () == 4); auto &rhs = g->rhs; const int lhs = g->lhs; @@ -5579,15 +5720,15 @@ void Closure::rewrite_ite_gate (Gate *g, int dst, int src) { // !then_lit ? then_lit : else_lit // !then_lit & then_lit | then_lit & else_lit // then_lit & else_lit - rhs[0] = else_lit; - CADICAL_assert (rhs[1] == then_lit); + rhs[0] = then_lit; + rhs[1] = else_lit; garbage = rewrite_ite_gate_to_and (g, src, dst, 0, 2, -dst); } else if (dst == else_lit) { // else_list ? then_lit : else_lit // else_list & then_lit | !else_list & else_lit // else_list & then_lit - rhs[0] = else_lit; - CADICAL_assert (rhs[1] == then_lit); + rhs[0] = then_lit; + rhs[1] = else_lit; garbage = rewrite_ite_gate_to_and (g, src, dst, 2, 0, dst); } else if (not_dst == else_lit) { // !else_list ? then_lit : else_lit @@ -5627,11 +5768,13 @@ void Closure::rewrite_ite_gate (Gate *g, int dst, int src) { if (-else_lit == lhs) { if (internal->lrat) lrat_chain = reasons_implication; - learn_congruence_unit (cond == -lhs ? -else_lit : else_lit, false, true); - } else fully_propagate (); + learn_congruence_unit (cond == -lhs ? -else_lit : else_lit, false, + true); + } else + fully_propagate (); } else { - if (merge_literals_lrat (g->lhs, else_lit, reasons_implication, - reasons_back)) { + if (merge_literals (g->lhs, else_lit, reasons_implication, + reasons_back)) { ++internal->stats.congruence.unaries; ++internal->stats.congruence.unary_ites; } @@ -5669,8 +5812,8 @@ void Closure::rewrite_ite_gate (Gate *g, int dst, int src) { std::vector reasons_implication, reasons_back; produce_ite_merge_then_else_reasons (g, src, dst, reasons_implication, reasons_back); - if (merge_literals_lrat (lhs, else_lit, reasons_implication, - reasons_back)) { + if (merge_literals (lhs, else_lit, reasons_implication, + reasons_back)) { ++internal->stats.congruence.unaries; ++internal->stats.congruence.unary_ites; } @@ -5691,7 +5834,8 @@ void Closure::rewrite_ite_gate (Gate *g, int dst, int src) { learn_congruence_unit (-else_lit); garbage = true; } else { - LOG ("changing to xor"); + LOG ("changing to xor %s = %s ^ %s", LOGLIT (g->lhs), LOGLIT (cond), + LOGLIT (else_lit)); new_tag = Gate_Type::XOr_Gate; CADICAL_assert (rhs[0] == cond); rhs[1] = else_lit; @@ -5703,7 +5847,7 @@ void Closure::rewrite_ite_gate (Gate *g, int dst, int src) { } #endif produce_rewritten_clause_lrat_and_clean (g->pos_lhs_ids, g->lhs, - true); + !true, true); #ifdef LOGGING for (auto litId : g->pos_lhs_ids) { LOG (litId.clause, "%d ->", litId.current_lit); @@ -5730,14 +5874,17 @@ void Closure::rewrite_ite_gate (Gate *g, int dst, int src) { lrat_chain = reasons_unit; learn_congruence_unit (cond, true); if (then_lit != lhs) { - LOG ("special case, learning %d",cond == -lhs ? -then_lit : then_lit); + LOG ("special case, learning %d", + cond == -lhs ? -then_lit : then_lit); if (internal->lrat) lrat_chain = reasons_implication; - learn_congruence_unit (cond == -lhs ? -then_lit : then_lit, false, true); - } else fully_propagate (); + learn_congruence_unit (cond == -lhs ? -then_lit : then_lit, false, + true); + } else + fully_propagate (); } else { - if (merge_literals_lrat (lhs, then_lit, reasons_implication, - reasons_back)) { + if (merge_literals (lhs, then_lit, reasons_implication, + reasons_back)) { ++internal->stats.congruence.unaries; ++internal->stats.congruence.unary_ites; } @@ -5775,8 +5922,8 @@ void Closure::rewrite_ite_gate (Gate *g, int dst, int src) { std::vector reasons_implication, reasons_back; produce_ite_merge_then_else_reasons (g, src, dst, reasons_implication, reasons_back); - if (merge_literals_lrat (lhs, then_lit, reasons_implication, - reasons_back)) { + if (merge_literals (lhs, then_lit, reasons_implication, + reasons_back)) { ++internal->stats.congruence.unaries; ++internal->stats.congruence.unary_ites; } @@ -5785,10 +5932,15 @@ void Closure::rewrite_ite_gate (Gate *g, int dst, int src) { // cond ? then_lit : !then_lit // cond & then_lit | !cond & !then_lit // !(cond ^ then_lit) + // + // For the clause 11 = 11 ? 9 : 8, we have empty clauses. The flag is + // only useful for debugging + const bool allow_empty_lrat_clause = + g->degenerated_gate != Special_Gate::NORMAL; if (lhs == cond) { - produce_rewritten_clause_lrat_and_clean (g->pos_lhs_ids, not_lhs, - false); if (internal->lrat) { + produce_rewritten_clause_lrat_and_clean ( + g->pos_lhs_ids, not_lhs, false, allow_empty_lrat_clause); CADICAL_assert (g->pos_lhs_ids.size () == 2); lrat_chain.push_back (g->pos_lhs_ids[0].clause->id); lrat_chain.push_back (g->pos_lhs_ids[1].clause->id); @@ -5796,9 +5948,9 @@ void Closure::rewrite_ite_gate (Gate *g, int dst, int src) { learn_congruence_unit (then_lit); garbage = true; } else if (not_lhs == cond) { - produce_rewritten_clause_lrat_and_clean (g->pos_lhs_ids, not_lhs, - false); if (internal->lrat) { + produce_rewritten_clause_lrat_and_clean ( + g->pos_lhs_ids, not_lhs, false, allow_empty_lrat_clause); CADICAL_assert (g->pos_lhs_ids.size () == 2); lrat_chain.push_back (g->pos_lhs_ids[0].clause->id); lrat_chain.push_back (g->pos_lhs_ids[1].clause->id); @@ -5806,9 +5958,9 @@ void Closure::rewrite_ite_gate (Gate *g, int dst, int src) { learn_congruence_unit (-then_lit); garbage = true; } else if (not_lhs == then_lit) { - produce_rewritten_clause_lrat_and_clean (g->pos_lhs_ids, not_lhs, - false); if (internal->lrat) { + produce_rewritten_clause_lrat_and_clean ( + g->pos_lhs_ids, not_lhs, false, allow_empty_lrat_clause); CADICAL_assert (g->pos_lhs_ids.size () == 2); lrat_chain.push_back (g->pos_lhs_ids[0].clause->id); lrat_chain.push_back (g->pos_lhs_ids[1].clause->id); @@ -5848,9 +6000,8 @@ void Closure::rewrite_ite_gate (Gate *g, int dst, int src) { if (negate_lhs) g->lhs = -g->lhs; } - if (internal->vlit (rhs[0]) > - internal->vlit ( - rhs[1])) { // unlike kissat, we need to do it after negating + // unlike kissat, we need to do it after negating + if (internal->vlit (rhs[0]) > internal->vlit (rhs[1])) { std::swap (rhs[0], rhs[1]); CADICAL_assert (new_tag != Gate_Type::ITE_Gate); } @@ -5916,26 +6067,35 @@ void Closure::rewrite_ite_gate (Gate *g, int dst, int src) { LOG (litId.clause, "%d ->", litId.current_lit); } #endif - produce_rewritten_clause_lrat_and_clean (g->pos_lhs_ids, g->lhs); - CADICAL_assert (!internal->lrat || g->pos_lhs_ids.size () == 2); - Clause *c1 = nullptr, *c2 = nullptr; - if (internal->lrat) { - CADICAL_assert (g->pos_lhs_ids[0].clause); - bool rhs_as_src_first = - g->pos_lhs_ids[0].clause->literals[0] == g->lhs || - g->pos_lhs_ids[0].clause->literals[1] == g->lhs; - c1 = (rhs_as_src_first ? g->pos_lhs_ids[0].clause - : g->pos_lhs_ids[1].clause); - c2 = (rhs_as_src_first ? g->pos_lhs_ids[1].clause - : g->pos_lhs_ids[0].clause); - c1 = maybe_promote_tmp_binary_clause (c1); - c2 = maybe_promote_tmp_binary_clause (c2); - } else { - maybe_add_binary_clause (-g->lhs, g->rhs[0]); - maybe_add_binary_clause (g->lhs, -g->rhs[0]); + produce_rewritten_clause_lrat_and_clean (g->pos_lhs_ids, + g->lhs); + CADICAL_assert ( + !internal->lrat || g->pos_lhs_ids.size () == 2 || + (g->arity () == 1 && find_representative (g->lhs) == + find_representative (g->rhs[0]))); + if (g->arity () == 1) // can happen when there are units in the + // gate that are not simplified yet + garbage = true; + else { + Clause *c1 = nullptr, *c2 = nullptr; + if (internal->lrat) { + CADICAL_assert (g->pos_lhs_ids[0].clause); + bool rhs_as_src_first = + g->pos_lhs_ids[0].clause->literals[0] == g->lhs || + g->pos_lhs_ids[0].clause->literals[1] == g->lhs; + c1 = (rhs_as_src_first ? g->pos_lhs_ids[0].clause + : g->pos_lhs_ids[1].clause); + c2 = (rhs_as_src_first ? g->pos_lhs_ids[1].clause + : g->pos_lhs_ids[0].clause); + c1 = maybe_promote_tmp_binary_clause (c1); + c2 = maybe_promote_tmp_binary_clause (c2); + } else { + maybe_add_binary_clause (-g->lhs, g->rhs[0]); + maybe_add_binary_clause (g->lhs, -g->rhs[0]); + } + merge_literals_equivalence (g->lhs, g->rhs[0], c1, c2); + garbage = true; } - merge_literals_equivalence (g->lhs, g->rhs[0], c1, c2); - garbage = true; } } if (!garbage) { @@ -5947,7 +6107,6 @@ void Closure::rewrite_ite_gate (Gate *g, int dst, int src) { } if (!garbage) { - g->hash = hash_lits (nonces, g->rhs); LOG (g, "rewritten"); if (internal->lrat) { @@ -5971,8 +6130,7 @@ void Closure::rewrite_ite_gate (Gate *g, int dst, int src) { #endif CADICAL_assert (g->pos_lhs_ids.size () == 2 || gate_contains (g, g->lhs)); - CADICAL_assert (g->neg_lhs_ids.size () == 1 || - gate_contains (g, g->lhs)); + CADICAL_assert (g->neg_lhs_ids () || gate_contains (g, g->lhs)); CADICAL_assert (g->arity () == 2); #ifndef CADICAL_NDEBUG std::for_each ( @@ -6004,8 +6162,8 @@ void Closure::rewrite_ite_gate (Gate *g, int dst, int src) { add_xor_matching_proof_chain (g, g->lhs, h->pos_lhs_ids, h->lhs, reasons_implication, reasons_back); - if (merge_literals_lrat (g->lhs, h->lhs, reasons_implication, - reasons_back)) + if (merge_literals (g->lhs, h->lhs, reasons_implication, + reasons_back)) ++internal->stats.congruence.xors; } else { add_ite_turned_and_binary_clauses (g); @@ -6013,8 +6171,8 @@ void Closure::rewrite_ite_gate (Gate *g, int dst, int src) { if (internal->lrat) merge_and_gate_lrat_produce_lrat (g, h, reasons_implication, reasons_back, false); - if (merge_literals_lrat (g->lhs, h->lhs, reasons_implication, - reasons_back)) + if (merge_literals (g, h, g->lhs, h->lhs, reasons_implication, + reasons_back)) ++internal->stats.congruence.ands; } delete_proof_chain (); @@ -6036,7 +6194,7 @@ void Closure::rewrite_ite_gate (Gate *g, int dst, int src) { } else { LOG (g, "rewritten"); if (internal->lrat) - update_ite_flags (g), check_correct_ite_flags(g); + update_ite_flags (g), check_correct_ite_flags (g); CADICAL_assert (rhs[0] != rhs[1]); CADICAL_assert (rhs[0] != rhs[2]); CADICAL_assert (rhs[1] != rhs[2]); @@ -6044,7 +6202,7 @@ void Closure::rewrite_ite_gate (Gate *g, int dst, int src) { CADICAL_assert (rhs[0] != -(rhs[2])); CADICAL_assert (rhs[1] != -(rhs[2])); check_ite_gate_implied (g); - check_ite_lrat_reasons (g, false); + check_ite_lrat_reasons (g); bool negate_lhs; Gate *h = find_ite_gate (g, negate_lhs); CADICAL_assert (lhs == g->lhs); @@ -6053,20 +6211,23 @@ void Closure::rewrite_ite_gate (Gate *g, int dst, int src) { g->lhs = -lhs; check_ite_lrat_reasons (g); if (internal->lrat) - check_correct_ite_flags (g); + check_correct_ite_flags (g); if (h) { garbage = true; check_ite_gate_implied (g); - check_ite_lrat_reasons (g, false); + check_ite_lrat_reasons (g); check_ite_gate_implied (h); - check_ite_lrat_reasons (h, false); + check_ite_lrat_reasons (h); int normalized_lhs = negate_lhs ? not_lhs : lhs; std::vector extra_reasons_lit, extra_reasons_ulit; add_ite_matching_proof_chain (g, h, normalized_lhs, h->lhs, extra_reasons_lit, extra_reasons_ulit); - if (merge_literals_lrat (normalized_lhs, h->lhs, extra_reasons_lit, - extra_reasons_ulit)) + // LHS can change for degenerated gates + if (g->lhs != normalized_lhs) + normalized_lhs = find_eager_representative (normalized_lhs); + if (merge_literals (g, h, normalized_lhs, h->lhs, extra_reasons_lit, + extra_reasons_ulit)) ++internal->stats.congruence.ites; delete_proof_chain (); CADICAL_assert (internal->unsat || chain.empty ()); @@ -6075,7 +6236,6 @@ void Closure::rewrite_ite_gate (Gate *g, int dst, int src) { if (g->indexed) remove_gate (git); LOG (g, "normalized"); - g->hash = hash_lits (nonces, g->rhs); index_gate (g); CADICAL_assert (g->arity () == 3); for (auto lit : g->rhs) @@ -6089,6 +6249,7 @@ void Closure::rewrite_ite_gate (Gate *g, int dst, int src) { mark_garbage (g); CADICAL_assert (chain.empty ()); + ++internal->stats.congruence.rewritten_ites; } void Closure::simplify_ite_gate_produce_unit_lrat (Gate *g, int lit, @@ -6096,7 +6257,6 @@ void Closure::simplify_ite_gate_produce_unit_lrat (Gate *g, int lit, size_t idx2) { if (!internal->lrat) return; - // TODO if (internal->val (lit) > 0) return; CADICAL_assert (internal->lrat); @@ -6110,9 +6270,11 @@ void Closure::simplify_ite_gate_produce_unit_lrat (Gate *g, int lit, Clause *d = g->pos_lhs_ids[idx2].clause; if (g->lhs == -g->rhs[0]) { - LOG ("special case of LHS=-cond where only one clause in LRAT is needed is needed"); + LOG ("special case of LHS=-cond where only one clause in LRAT is " + "needed is needed"); size_t idx = (internal->val (g->rhs[1]) < 0 ? idx2 : idx1); - c = produce_rewritten_clause_lrat (g->pos_lhs_ids[idx].clause, g->lhs, false, false); + c = produce_rewritten_clause_lrat (g->pos_lhs_ids[idx].clause, g->lhs, + false, false); CADICAL_assert (c); // not possible to do this in a single lrat chain push_id_and_rewriting_lrat_unit (c, Rewrite (), lrat_chain, true, @@ -6121,9 +6283,11 @@ void Closure::simplify_ite_gate_produce_unit_lrat (Gate *g, int lit, return; } if (g->lhs == g->rhs[0]) { - LOG ("special case of LHS=cond where only one clause in LRAT is needed is needed"); + LOG ("special case of LHS=cond where only one clause in LRAT is needed " + "is needed"); size_t idx = (internal->val (g->rhs[1]) > 0 ? idx2 : idx1); - c = produce_rewritten_clause_lrat (g->pos_lhs_ids[idx].clause, g->lhs, false, false); + c = produce_rewritten_clause_lrat (g->pos_lhs_ids[idx].clause, g->lhs, + false, false); CADICAL_assert (c); // not possible to do this in a single lrat chain push_id_and_rewriting_lrat_unit (c, Rewrite (), lrat_chain, true, @@ -6153,7 +6317,6 @@ void Closure::merge_and_gate_lrat_produce_lrat ( CADICAL_assert (internal->lrat); CADICAL_assert (g->tag == Gate_Type::And_Gate); CADICAL_assert (h->tag == Gate_Type::And_Gate); - CADICAL_assert (g->neg_lhs_ids.size () <= 1); update_and_gate_build_lrat_chain (g, h, reasons_lrat_src, reasons_lrat_usrc, remove_units); } @@ -6162,7 +6325,12 @@ void Closure::merge_and_gate_lrat_produce_lrat ( bool Closure::simplify_ite_gate_to_and (Gate *g, size_t idx1, size_t idx2, int removed_lit) { CADICAL_assert (internal->lrat_chain.empty ()); - CADICAL_assert (g->rhs.size () == 3); + CADICAL_assert (g->arity () == 3); + LOG ("transforming to gate[%" PRIu64 + "] %s = %s & %s with idx1=%zd, idx2=%zd", + g->id, LOGLIT (g->lhs), LOGLIT (g->rhs[0]), LOGLIT (g->rhs[1]), idx1, + idx2); + const int8_t orig_flag = g->degenerated_gate; #ifdef LOGGING for (auto litId : g->pos_lhs_ids) { LOG (litId.clause, "%d ->", litId.current_lit); @@ -6179,9 +6347,25 @@ bool Closure::simplify_ite_gate_to_and (Gate *g, size_t idx1, size_t idx2, } if (!internal->lrat) return false; - g->degenerated_and_neg = (g->degenerated_and_neg || g->rhs[1] == -g->lhs || g->rhs[0] == -g->lhs); - g->degenerated_and_pos = (g->degenerated_and_pos || g->rhs[0] == g->lhs || g->rhs[1] == g->lhs); + if (g->rhs[1] == -g->lhs || g->rhs[0] == -g->lhs) + g->degenerated_gate = Special_Gate::DEGENERATED_AND; + else if (g->rhs[0] == g->lhs || g->rhs[1] == g->lhs) + g->degenerated_gate = Special_Gate::DEGENERATED_AND; + else + g->degenerated_gate = Special_Gate::NORMAL; + + if (g->lhs == -removed_lit && internal->val (-removed_lit)) { + // 3 = 5 ? 1 : 3 where 3@0 = -1 + g->degenerated_gate = Special_Gate::DEGENERATED_AND_LHS_FALSE; + size_t new_idx1 = idx1; + size_t new_idx2 = idx2; + produce_rewritten_clause_lrat_and_clean (g->pos_lhs_ids, 0, new_idx1, + new_idx2, true); + CADICAL_assert (g->pos_lhs_ids.size () == 1); + CADICAL_assert (g->pos_lhs_ids[0].clause->size == 2); + return false; + } CADICAL_assert (g->pos_lhs_ids.size () == 4); CADICAL_assert (idx1 < g->pos_lhs_ids.size ()); CADICAL_assert (idx2 < g->pos_lhs_ids.size ()); @@ -6193,7 +6377,9 @@ bool Closure::simplify_ite_gate_to_and (Gate *g, size_t idx1, size_t idx2, if (g->pos_lhs_ids.size () == 1) { LOG (g, "degenerated AND gate"); - const int replacement_lit = (g->rhs[1] == g->lhs ? g->rhs[0] : g->rhs[1]); + CADICAL_assert (g->degenerated_gate != Special_Gate::NORMAL); + const int replacement_lit = + (g->rhs[1] == g->lhs ? g->rhs[0] : g->rhs[1]); for (auto &litId : g->pos_lhs_ids) { CADICAL_assert (litId.clause); LOG (litId.clause, "%d ->", litId.current_lit); @@ -6203,21 +6389,53 @@ bool Closure::simplify_ite_gate_to_and (Gate *g, size_t idx1, size_t idx2, litId.current_lit = replacement_lit; LOG (litId.clause, "%d ->", litId.current_lit); // TODO we need a replacement index - CADICAL_assert (std::find (begin (*litId.clause), end (*litId.clause), litId.current_lit) != - end (*litId.clause)); + CADICAL_assert (std::find (begin (*litId.clause), end (*litId.clause), + litId.current_lit) != end (*litId.clause)); CADICAL_assert (std::find (begin (g->rhs), end (g->rhs), litId.current_lit) != end (g->rhs)); } return false; } + LOG ("normal AND gate with clauses %zd (was: %zd) %zd (was: %zd)", + new_idx1, idx1, new_idx2, idx2); + Clause *c = nullptr; + if ((orig_flag == Special_Gate::NO_ELSE || + orig_flag == Special_Gate::NO_THEN) && + (find_eager_representative (g->lhs) == + find_eager_representative (g->rhs[0]) || + find_eager_representative (g->lhs) == + find_eager_representative (g->rhs[1]))) { + g->degenerated_gate = Special_Gate::DEGENERATED_AND; + LOG (g, "marking as degenerated"); + CADICAL_assert (find_eager_representative (g->lhs) == g->rhs[0] || + find_eager_representative (g->lhs) == g->rhs[1]); + Clause *d = g->pos_lhs_ids[new_idx1].clause; + // TODO there should be a better way than dereferencing the clause even + // if it is cheap as we have just written the proof out + CADICAL_assert (-g->lhs == d->literals[0] || -g->lhs == d->literals[1]); + int lit = d->literals[0] ^ d->literals[1] ^ -g->lhs; + LOG ("%d -> %d", g->lhs, find_eager_representative (g->lhs)); + LOG ("%d -> %d", d->literals[0], + find_eager_representative (d->literals[0])); + LOG ("%d -> %d", d->literals[1], + find_eager_representative (d->literals[1])); + LOG (d, "with reference %s at position %zd", LOGLIT (lit), new_idx1); + CADICAL_assert (d->size == 2); + g->pos_lhs_ids.clear (); + g->pos_lhs_ids.push_back ({lit, d}); + g->neg_lhs_ids.reset (); + // TODO should be before lrat test + g->lhs = find_eager_representative (g->lhs); + return false; + } CADICAL_assert (new_idx1 < g->pos_lhs_ids.size ()); - CADICAL_assert (new_idx2 < g->pos_lhs_ids.size ()); - Clause *c = g->pos_lhs_ids[new_idx1].clause; + c = g->pos_lhs_ids[new_idx1].clause; CADICAL_assert (c->size == 2); + CADICAL_assert (c); + CADICAL_assert (new_idx2 < g->pos_lhs_ids.size ()); CADICAL_assert (new_idx1 != new_idx2); Clause *d = g->pos_lhs_ids[new_idx2].clause; CADICAL_assert (c != d); - CADICAL_assert (c); CADICAL_assert (d); g->pos_lhs_ids.erase (std::remove_if (begin (g->pos_lhs_ids), end (g->pos_lhs_ids), @@ -6225,11 +6443,13 @@ bool Closure::simplify_ite_gate_to_and (Gate *g, size_t idx1, size_t idx2, return p.clause == d; }), end (g->pos_lhs_ids)); - CADICAL_assert (g->pos_lhs_ids.size () == 2); + CADICAL_assert (g->pos_lhs_ids.size () == 2 || + (g->degenerated_gate == Special_Gate::DEGENERATED_AND)); CADICAL_assert (lit); CADICAL_assert (other); CADICAL_assert (lit != other); - lrat_chain.push_back (c->id); + if (c) + lrat_chain.push_back (c->id); lrat_chain.push_back (d->id); Clause *e = add_tmp_binary_clause (lit, -other); @@ -6238,7 +6458,7 @@ bool Closure::simplify_ite_gate_to_and (Gate *g, size_t idx1, size_t idx2, [] (LitClausePair l) { return l.clause->size == 3; }); CADICAL_assert (long_clause != end (g->pos_lhs_ids)); LOG (long_clause->clause, "new long clause"); - g->neg_lhs_ids.push_back (*long_clause); + g->neg_lhs_ids = *long_clause; g->pos_lhs_ids.erase (long_clause); CADICAL_assert (g->pos_lhs_ids.size () == 1); g->pos_lhs_ids.push_back ({lit, e}); @@ -6268,12 +6488,13 @@ bool Closure::simplify_ite_gate_to_and (Gate *g, size_t idx1, size_t idx2, else if (litId.current_lit == -removed_lit) litId.current_lit = g->rhs[0]; LOG (litId.clause, "%d ->", litId.current_lit); - // TODO we need a replacement index CADICAL_assert (std::find (begin (g->rhs), end (g->rhs), litId.current_lit) != end (g->rhs)); CADICAL_assert (std::find (begin (*litId.clause), end (*litId.clause), litId.current_lit) != end (*litId.clause)); } + CADICAL_assert (g->pos_lhs_ids.size () == 2 || + g->degenerated_gate != Special_Gate::NORMAL); return false; } @@ -6334,10 +6555,17 @@ void Closure::simplify_ite_gate_condition_set ( for (auto litid : g->pos_lhs_ids) LOG (litid.clause, "clause in gate:"); #endif - push_id_and_rewriting_lrat_unit (c, Rewrite (), reasons_lrat, true, - Rewrite (), -g->lhs); - push_id_and_rewriting_lrat_unit (d, Rewrite (), reasons_back_lrat, true, - Rewrite (), g->lhs); + if (c) + push_id_and_rewriting_lrat_unit (c, Rewrite (), reasons_lrat, true, + Rewrite (), -g->lhs); + else + CADICAL_assert (g->degenerated_gate != Special_Gate::NORMAL); + + if (d) + push_id_and_rewriting_lrat_unit (d, Rewrite (), reasons_back_lrat, true, + Rewrite (), g->lhs); + else + CADICAL_assert (g->degenerated_gate != Special_Gate::NORMAL); } void Closure::simplify_ite_gate (Gate *g) { @@ -6363,8 +6591,7 @@ void Closure::simplify_ite_gate (Gate *g) { if (internal->lrat) simplify_ite_gate_condition_set (g, reasons_lrat, reasons_back_lrat, 0, 1); - if (merge_literals_lrat (lhs, then_lit, reasons_lrat, - reasons_back_lrat)) { + if (merge_literals (lhs, then_lit, reasons_lrat, reasons_back_lrat)) { ++internal->stats.congruence.unary_ites; ++internal->stats.congruence.unaries; } @@ -6372,8 +6599,7 @@ void Closure::simplify_ite_gate (Gate *g) { if (internal->lrat) simplify_ite_gate_condition_set (g, reasons_lrat, reasons_back_lrat, 2, 3); - if (merge_literals_lrat (lhs, else_lit, reasons_lrat, - reasons_back_lrat)) { + if (merge_literals (lhs, else_lit, reasons_lrat, reasons_back_lrat)) { ++internal->stats.congruence.unary_ites; ++internal->stats.congruence.unaries; } @@ -6388,20 +6614,24 @@ void Closure::simplify_ite_gate (Gate *g) { simplify_ite_gate_produce_unit_lrat (g, -lhs, 0, 2); learn_congruence_unit (-lhs); } else if (v_then > 0 && v_else < 0) { - if (internal->lrat) + // if the gate is a = a ? true : false, there is nothing to do and the + // lrat generation fails + if (internal->lrat && find_eager_representative (lhs) != + find_eager_representative (cond)) simplify_ite_gate_then_else_set (g, extra_reasons, extra_reasons_back, 1, 2); - if (merge_literals_lrat (lhs, cond, extra_reasons, - extra_reasons_back)) { + if (merge_literals (lhs, cond, extra_reasons, extra_reasons_back)) { ++internal->stats.congruence.unary_ites; ++internal->stats.congruence.unaries; } } else if (v_then < 0 && v_else > 0) { - if (internal->lrat) + // if the gate is a = -a ? false : true, there is nothing to do and + // the lrat generation fails + if (internal->lrat && find_eager_representative (lhs) != + find_eager_representative (-cond)) simplify_ite_gate_then_else_set (g, extra_reasons, extra_reasons_back, 0, 3); - if (merge_literals_lrat (lhs, -cond, extra_reasons_back, - extra_reasons)) { + if (merge_literals (lhs, -cond, extra_reasons_back, extra_reasons)) { ++internal->stats.congruence.unary_ites; ++internal->stats.congruence.unaries; } @@ -6438,7 +6668,6 @@ void Closure::simplify_ite_gate (Gate *g) { rhs.resize (2); CADICAL_assert (is_sorted (begin (rhs), end (rhs), sort_literals_by_var_smaller (internal))); - g->hash = hash_lits (nonces, rhs); check_and_gate_implied (g); Gate *h = find_and_lits (rhs); if (h) { @@ -6446,16 +6675,15 @@ void Closure::simplify_ite_gate (Gate *g) { std::vector reasons_lrat, reasons_lrat_back; if (internal->lrat) merge_and_gate_lrat_produce_lrat (g, h, reasons_lrat, - reasons_lrat_back, false); - if (merge_literals_lrat (g->lhs, h->lhs, reasons_lrat, - reasons_lrat_back)) { + reasons_lrat_back, true); + if (merge_literals (g, h, g->lhs, h->lhs, reasons_lrat, + reasons_lrat_back)) { ++internal->stats.congruence.ites; } } else { remove_gate (git); index_gate (g); garbage = false; - g->hash = hash_lits (nonces, g->rhs); for (auto lit : rhs) if (lit != cond && lit != then_lit && lit != else_lit) { connect_goccs (g, lit); @@ -6477,23 +6705,34 @@ void Closure::simplify_ite_gate (Gate *g) { void Closure::add_ite_matching_proof_chain ( Gate *g, Gate *h, int lhs1, int lhs2, std::vector &reasons1, std::vector &reasons2) { + if (!internal->proof) + return; check_ite_lrat_reasons (g); - check_ite_lrat_reasons (h, false); + check_ite_lrat_reasons (h); CADICAL_assert (g->lhs == lhs1); CADICAL_assert (h->lhs == lhs2); if (lhs1 == lhs2) return; - if (!internal->proof) - return; LOG (g, "starting ITE matching proof chain"); - LOG (h, "starting ITE matching proof chain with"); + if (g->degenerated_gate != Special_Gate::NORMAL) { + g->lhs = find_eager_representative (g->lhs); + lhs1 = find_eager_representative (lhs1); + LOG (g, "rewritten LHS of g"); + } + LOG (h, "matching ITE proof chain with"); + if (h->degenerated_gate != Special_Gate::NORMAL) { + h->lhs = find_eager_representative (h->lhs); + lhs2 = find_eager_representative (lhs2); + LOG (g, "rewritten LHS of h"); + } + LOG ("producing ITE gates %d %d", lhs1, lhs2); CADICAL_assert (unsimplified.empty ()); CADICAL_assert (chain.empty ()); if (internal->lrat) check_correct_ite_flags (g); const auto &rhs = g->rhs; - const int8_t flags_g = g->degenerated_ite; - const int8_t flags_h = h->degenerated_ite; + const int8_t flags_g = g->degenerated_gate; + const int8_t flags_h = h->degenerated_gate; const int cond = rhs[0]; LRAT_ID g_then_id = 0, g_neg_then_id = 0, g_neg_else_id = 0; LRAT_ID h_then_id = 0, h_neg_then_id = 0, h_else_id = 0; @@ -6528,25 +6767,33 @@ void Closure::add_ite_matching_proof_chain ( CADICAL_assert (g->pos_lhs_ids.size () == 4); auto &g_then_clause = g->pos_lhs_ids[0].clause; g_then_clause = - g_then_clause ? produce_rewritten_clause_lrat (g_then_clause, g->lhs, false) : nullptr; + g_then_clause + ? produce_rewritten_clause_lrat (g_then_clause, g->lhs, false) + : nullptr; if (g_then_clause) g_then_id = g_then_clause->id; auto &g_neg_then_clause = g->pos_lhs_ids[1].clause; - g_neg_then_clause = - g_neg_then_clause ? produce_rewritten_clause_lrat (g_neg_then_clause, g->lhs, false) : nullptr; + g_neg_then_clause = g_neg_then_clause + ? produce_rewritten_clause_lrat ( + g_neg_then_clause, g->lhs, false) + : nullptr; if (g_neg_then_clause) g_neg_then_id = g_neg_then_clause->id; auto &g_else_clause = g->pos_lhs_ids[2].clause; g_else_clause = - g_else_clause ? produce_rewritten_clause_lrat (g_else_clause, g->lhs, false) : g_else_clause; + g_else_clause + ? produce_rewritten_clause_lrat (g_else_clause, g->lhs, false) + : g_else_clause; if (g_else_clause) g_else_id = g_else_clause->id; auto &g_neg_else_clause = g->pos_lhs_ids[3].clause; - g_neg_else_clause = - g_neg_else_clause ? produce_rewritten_clause_lrat (g_neg_else_clause, g->lhs, false) : nullptr; + g_neg_else_clause = g_neg_else_clause + ? produce_rewritten_clause_lrat ( + g_neg_else_clause, g->lhs, false) + : nullptr; if (g_neg_else_clause) g_neg_else_id = g_neg_else_clause->id; @@ -6554,28 +6801,35 @@ void Closure::add_ite_matching_proof_chain ( CADICAL_assert (h->pos_lhs_ids.size () == 4); auto &h_then_clause = h->pos_lhs_ids[0].clause; h_then_clause = - h_then_clause ? produce_rewritten_clause_lrat (h_then_clause, h->lhs, false) : nullptr; + h_then_clause + ? produce_rewritten_clause_lrat (h_then_clause, h->lhs, false) + : nullptr; if (h_then_clause) h_then_id = h_then_clause->id; auto &h_neg_then_clause = h->pos_lhs_ids[1].clause; - h_neg_then_clause = - h_neg_then_clause ? produce_rewritten_clause_lrat (h_neg_then_clause, h->lhs, false) : nullptr; + h_neg_then_clause = h_neg_then_clause + ? produce_rewritten_clause_lrat ( + h_neg_then_clause, h->lhs, false) + : nullptr; if (h_neg_then_clause) h_neg_then_id = h_neg_then_clause->id; auto &h_else_clause = h->pos_lhs_ids[2].clause; h_else_clause = - h_else_clause ? produce_rewritten_clause_lrat (h_else_clause, h->lhs, false) : nullptr; + h_else_clause + ? produce_rewritten_clause_lrat (h_else_clause, h->lhs, false) + : nullptr; if (h_else_clause) h_else_id = h_else_clause->id; auto &h_neg_else_clause = h->pos_lhs_ids[3].clause; - h_neg_else_clause = - h_neg_else_clause ? produce_rewritten_clause_lrat (h_neg_else_clause, h->lhs, false) : nullptr; + h_neg_else_clause = h_neg_else_clause + ? produce_rewritten_clause_lrat ( + h_neg_else_clause, h->lhs, false) + : nullptr; if (h_neg_else_clause) h_neg_else_id = h_neg_else_clause->id; - } if (degenerated_g_cond) { @@ -6584,8 +6838,10 @@ void Closure::add_ite_matching_proof_chain ( unsimplified.push_back (lhs2); LRAT_ID id1 = 0; if (internal->lrat) { + CADICAL_assert (g_then_id); lrat_chain.push_back (g_then_id); - lrat_chain.push_back (h_neg_then_id); + if (h_neg_then_id) + lrat_chain.push_back (h_neg_then_id); } id1 = simplify_and_add_to_proof_chain (unsimplified); @@ -6595,7 +6851,8 @@ void Closure::add_ite_matching_proof_chain ( LRAT_ID id2 = 0; if (internal->lrat) { lrat_chain.push_back (g_neg_else_id); - lrat_chain.push_back (h_else_id); + if (h_else_id) + lrat_chain.push_back (h_else_id); } id2 = simplify_and_add_to_proof_chain (unsimplified); @@ -6614,7 +6871,8 @@ void Closure::add_ite_matching_proof_chain ( LRAT_ID id1 = 0; if (internal->lrat) { lrat_chain.push_back (h_then_id); - lrat_chain.push_back (g_neg_then_id); + if (g_neg_then_id) + lrat_chain.push_back (g_neg_then_id); } id1 = simplify_and_add_to_proof_chain (unsimplified); @@ -6624,7 +6882,8 @@ void Closure::add_ite_matching_proof_chain ( LRAT_ID id2 = 0; if (internal->lrat) { lrat_chain.push_back (h_neg_else_id); - lrat_chain.push_back (g_else_id); + if (g_else_id) + lrat_chain.push_back (g_else_id); } id2 = simplify_and_add_to_proof_chain (unsimplified); @@ -6642,9 +6901,10 @@ void Closure::add_ite_matching_proof_chain ( unsimplified.push_back (-lhs2); LRAT_ID id1 = 0; if (internal->lrat) { - CADICAL_assert (g_neg_then_id && h_then_id && g_else_id && h_neg_else_id); + CADICAL_assert (g_neg_then_id); lrat_chain.push_back (g_neg_then_id); - lrat_chain.push_back (h_then_id); + if (h_then_id) + lrat_chain.push_back (h_then_id); } id1 = simplify_and_add_to_proof_chain (unsimplified); @@ -6654,8 +6914,10 @@ void Closure::add_ite_matching_proof_chain ( LRAT_ID id2 = -1; if (internal->lrat) { + CADICAL_assert (g_else_id); lrat_chain.push_back (g_else_id); - lrat_chain.push_back (h_neg_else_id); + if (h_neg_else_id) + lrat_chain.push_back (h_neg_else_id); } id2 = simplify_and_add_to_proof_chain (unsimplified); CADICAL_assert (!internal->lrat || id1); @@ -6668,34 +6930,37 @@ void Closure::add_ite_matching_proof_chain ( if (degenerated_h_not_cond) { LOG ("special case: cond = -lhs, h degenerated"); - unsimplified.push_back (lhs1); - unsimplified.push_back (-lhs2); + unsimplified.push_back (-lhs1); + unsimplified.push_back (lhs2); LRAT_ID id1 = 0; if (internal->lrat) { - CADICAL_assert (g_neg_then_id && h_then_id && g_else_id && h_neg_else_id); + CADICAL_assert (h_neg_then_id); lrat_chain.push_back (h_neg_then_id); - lrat_chain.push_back (g_then_id); + if (g_then_id) + lrat_chain.push_back (g_then_id); } id1 = simplify_and_add_to_proof_chain (unsimplified); unsimplified.clear (); - unsimplified.push_back (-lhs1); - unsimplified.push_back (lhs2); + unsimplified.push_back (lhs1); + unsimplified.push_back (-lhs2); LRAT_ID id2 = -1; if (internal->lrat) { + CADICAL_assert (h_else_id); lrat_chain.push_back (h_else_id); - lrat_chain.push_back (g_neg_else_id); + if (g_neg_else_id) + lrat_chain.push_back (g_neg_else_id); } id2 = simplify_and_add_to_proof_chain (unsimplified); CADICAL_assert (!internal->lrat || id1); CADICAL_assert (!internal->lrat || id2); - reasons2.push_back (id1); - reasons1.push_back (id2); + reasons1.push_back (id1); + reasons2.push_back (id2); unsimplified.clear (); return; } - + LOG ("normal path"); CADICAL_assert (!internal->lrat || degenerated_g_then || (g_then_id && g_neg_then_id)); @@ -6796,15 +7061,15 @@ Gate *Closure::new_ite_gate (int lhs, int cond, int then_lit, int else_lit, std::vector reasons_implication, reasons_back; if (internal->lrat) { merge_ite_gate_same_then_else_lrat (clauses, reasons_implication, - reasons_back); + reasons_back); } - if (merge_literals_lrat (lhs, then_lit, reasons_implication, - reasons_back)) + if (merge_literals (lhs, then_lit, reasons_implication, reasons_back)) ++internal->stats.congruence.trivial_ite; return 0; } rhs.clear (); + rhs.reserve (3); rhs.push_back (cond); rhs.push_back (then_lit); rhs.push_back (else_lit); @@ -6825,15 +7090,15 @@ Gate *Closure::new_ite_gate (int lhs, int cond, int then_lit, int else_lit, g->lhs = lhs; check_ite_gate_implied (g); check_ite_lrat_reasons ( - g, false); // due to merges done before during AND gate detection! + g); // due to merges done before during AND gate detection! if (h) { check_ite_gate_implied (h); std::vector extra_reasons_lit, extra_reasons_ulit; add_ite_matching_proof_chain (h, g, h->lhs, lhs, extra_reasons_lit, extra_reasons_ulit); - if (merge_literals_lrat (h, g, h->lhs, lhs, extra_reasons_lit, - extra_reasons_ulit)) { + if (merge_literals (h, g, h->lhs, g->lhs, extra_reasons_lit, + extra_reasons_ulit)) { ++internal->stats.congruence.ites; LOG ("found merged literals"); } @@ -6846,7 +7111,6 @@ Gate *Closure::new_ite_gate (int lhs, int cond, int then_lit, int else_lit, g->garbage = false; g->indexed = true; g->shrunken = false; - g->hash = hash_lits (nonces, g->rhs); table.insert (g); ++internal->stats.congruence.gates; #ifdef LOGGING @@ -6861,6 +7125,7 @@ Gate *Closure::new_ite_gate (int lhs, int cond, int then_lit, int else_lit, } } check_ite_lrat_reasons (g); + ++internal->stats.congruence.ite_gates; return g; } @@ -6915,17 +7180,17 @@ void Closure::check_contained_module_rewriting (Clause *c, int lit, #endif } -void Closure::check_ite_lrat_reasons (Gate *g, bool normalized) { +void Closure::check_ite_lrat_reasons (Gate *g) { #ifndef CADICAL_NDEBUG CADICAL_assert (g->tag == Gate_Type::ITE_Gate); if (!internal->lrat) return; - CADICAL_assert (g->rhs.size () == 3); + CADICAL_assert (g->arity () == 3); CADICAL_assert (is_tautological_ite_gate (g) || g->pos_lhs_ids.size () == 4); - CADICAL_assert (g->neg_lhs_ids.empty ()); + CADICAL_assert (!g->neg_lhs_ids ()); CADICAL_assert (g->pos_lhs_ids.size () == 4); #else - (void) g, (void) normalized; + (void) g; #endif } @@ -6975,6 +7240,15 @@ void Closure::init_ite_gate_extraction ( CONTINUE_COUNTING_NEXT_CLAUSE:; } + VERBOSE (4, + "counted %zu ternary ITE gates" + "(%.0f%% of %" PRIu64 " irredundant clauses)", + ternary.size (), + percent (ternary.size (), internal->stats.current.irredundant), + internal->stats.current.irredundant); +#ifndef CADICAL_QUIET + size_t connected = 0; +#endif for (auto c : ternary) { CADICAL_assert (!c->garbage); CADICAL_assert (!c->redundant); @@ -6997,6 +7271,9 @@ void Closure::init_ite_gate_extraction ( if (twice < 2) goto CONTINUE_WITH_NEXT_TERNARY_CLAUSE; CADICAL_assert (c->size != 2); +#ifndef CADICAL_QUIET + connected++; +#endif for (auto lit : *c) internal->occs (lit).push_back (c); if (positive && negative) @@ -7005,6 +7282,19 @@ void Closure::init_ite_gate_extraction ( } ternary.clear (); + + VERBOSE ( + 4, "connected %zu large clauses %.0f%%", candidates.size (), + percent (candidates.size (), internal->stats.current.irredundant)); + +#ifndef CADICAL_QUIET + size_t size_candidates = candidates.size (); + VERBOSE (4, + "%zu candidates ITE base clauses " + "(%.0f%% of %zu connected)", + size_candidates, percent (size_candidates, connected), + connected); +#endif } void Closure::reset_ite_gate_extraction () { @@ -7032,6 +7322,11 @@ void Closure::copy_conditional_equivalences (int lit, else { CADICAL_assert (!second); second = other; +#ifdef CADICAL_NDEBUG + // all other literals are true, so we can stop here + // otherwise, one clause does not have length 3 (ignoring unit) + break; +#endif } } CADICAL_assert (first), CADICAL_assert (second); @@ -7078,8 +7373,30 @@ struct litpair_smaller { litpair_smaller (Internal *i) : internal (i) {} bool operator() (const lit_implication &a, const lit_implication &b) const { - const auto s = litpair_rank (internal) (a); - const auto t = litpair_rank (internal) (b); + const uint64_t s = litpair_rank (internal) (a); + const uint64_t t = litpair_rank (internal) (b); + return s < t; + } +}; + +struct litequivalence_rank { + CaDiCaL::Internal *internal; + litequivalence_rank (Internal *i) : internal (i) {} + typedef uint64_t Type; + Type operator() (const lit_equivalence &a) const { + uint64_t lita = internal->vlit (a.first); + uint64_t litb = internal->vlit (a.second); + return (lita << 32) + litb; + } +}; + +struct litequivalence_smaller { + CaDiCaL::Internal *internal; + litequivalence_smaller (Internal *i) : internal (i) {} + bool operator() (const lit_equivalence &a, + const lit_equivalence &b) const { + const uint64_t s = litequivalence_rank (internal) (a); + const uint64_t t = litequivalence_rank (internal) (b); return s < t; } }; @@ -7088,19 +7405,16 @@ lit_implications::const_iterator Closure::find_lit_implication_second_literal ( int lit, lit_implications::const_iterator begin, lit_implications::const_iterator end) { - LOG ("searching for %d in", lit); - for (auto it = begin; it != end; ++it) - LOG ("%d [%d]", it->first, it->second); lit_implications::const_iterator found = std::lower_bound ( begin, end, lit_implication{lit, lit}, - [] (const lit_implication &a, const lit_implication &b) { - return a.second < b.second; + [this] (const lit_implication &a, const lit_implication &b) { + return internal->vlit (a.second) < internal->vlit (b.second); }); #ifndef CADICAL_NDEBUG auto found2 = std::binary_search ( begin, end, lit_implication{lit, lit}, - [] (const lit_implication &a, const lit_implication &b) { - return a.second < b.second; + [this] (const lit_implication &a, const lit_implication &b) { + return internal->vlit (a.second) < internal->vlit (b.second); }); #endif @@ -7178,11 +7492,6 @@ void Closure::extract_condeq_pairs (int lit, lit_implications &condbin, lit_implications::const_iterator pos_begin = begin; int next_lit = 0; -#ifdef LOGGING - for (const auto &pair : condbin) - LOG ("unsorted conditional %d equivalence %d = %d", lit, pair.first, - pair.second); -#endif LOG ("searching for first positive literal for lit %d", lit); for (;;) { if (pos_begin == end) @@ -7249,8 +7558,8 @@ void Closure::extract_condeq_pairs (int lit, lit_implications &condbin, const size_t neg_size = neg_end - neg_begin; if (pos_size <= neg_size) { LOG ("searching negation of %zu conditional binary clauses " - "with positive %d in %zu conditional binary clauses with %d", - pos_size, (pos_lit), neg_size, (neg_lit)); + "with positive %s in %zu conditional binary clauses with %s", + pos_size, LOGLIT (pos_lit), neg_size, LOGLIT (neg_lit)); search_condeq (lit, pos_lit, pos_begin, pos_end, neg_lit, neg_begin, neg_end, condeq); } else { @@ -7289,8 +7598,9 @@ void Closure::find_conditional_equivalences (int lit, litpair_rank (this->internal), litpair_smaller (this->internal)); extract_condeq_pairs (lit, condbin, condeq); - MSORT (internal->opts.radixsortlim, begin (condbin), end (condbin), - litpair_rank (this->internal), litpair_smaller (this->internal)); + MSORT (internal->opts.radixsortlim, begin (condeq), end (condeq), + litequivalence_rank (this->internal), + litequivalence_smaller (this->internal)); #ifdef LOGGING for (auto pair : condeq) @@ -7309,15 +7619,20 @@ void Closure::merge_condeq (int cond, lit_equivalences &condeq, for (auto p : condeq) { const int lhs = p.first; const int then_lit = p.second; +#ifndef CADICAL_NDEBUG + for (auto q2 = begin (not_condeq); q2 != q; ++q2) + CADICAL_assert (q2->first < lhs); if (internal->lrat) p.check_invariant (); +#endif CADICAL_assert (lhs > 0); while (q != end_not_condeq && q->first < lhs) ++q; - while (q != end_not_condeq && q->first == lhs) { + auto q2 = q; + while (q2 != end_not_condeq && q2->first == lhs) { LOG ("looking when %d at p= %d %d", cond, p.first, p.second); LOG ("looking when %d at %d %d", -cond, q->first, q->second); - lit_equivalence not_cond_pair = *q++; + const lit_equivalence not_cond_pair = *q2++; const int else_lit = not_cond_pair.second; std::vector clauses; if (internal->lrat) { @@ -7335,8 +7650,7 @@ void Closure::merge_condeq (int cond, lit_equivalences &condeq, LitClausePair (else_lit, not_cond_pair.second_clause)); clauses.push_back ( LitClausePair (-else_lit, not_cond_pair.first_clause)); - if (internal->lrat) - not_cond_pair.check_invariant (); + not_cond_pair.check_invariant (); } new_ite_gate (lhs, cond, then_lit, else_lit, std::move (clauses)); if (internal->unsat) @@ -7387,7 +7701,9 @@ void Closure::extract_ite_gates () { return; START (extractites); std::vector candidates; - +#ifndef CADICAL_QUIET + const int64_t gates_before = internal->stats.congruence.ite_gates; +#endif init_ite_gate_extraction (candidates); for (auto idx : internal->vars) { @@ -7398,6 +7714,9 @@ void Closure::extract_ite_gates () { } } // Kissat has an alternative version MERGE_CONDITIONAL_EQUIVALENCES + VERBOSE (2, "[congruence-%" PRId64 "] found %" PRId64 " ITE clauses", + internal->stats.congruence.rounds, + internal->stats.congruence.ite_gates - gates_before); reset_ite_gate_extraction (); STOP (extractites); } @@ -7412,6 +7731,17 @@ void Closure::extract_gates () { STOP (extract); return; } + + if (internal->lrat) { // save some memory + mu2_ids.clear (); + shrink_vector (mu2_ids); + mu4_ids.clear (); + shrink_vector (mu4_ids); + } else { + CADICAL_assert (mu2_ids.empty ()); + CADICAL_assert (mu4_ids.empty ()); + } + extract_xor_gates (); CADICAL_assert (internal->unsat || lrat_chain.empty ()); CADICAL_assert (internal->unsat || chain.empty ()); @@ -7426,7 +7756,7 @@ void Closure::extract_gates () { /*------------------------------------------------------------------------*/ // top level function to extract gate -bool Internal::extract_gates () { +bool Internal::extract_gates (bool remove_units_before_run) { if (unsat) return false; if (!opts.congruence) @@ -7437,27 +7767,35 @@ bool Internal::extract_gates () { learn_empty_clause (); return false; } - if (congruence_delay.bumpreasons.limit) { + if (congruence_delay.bumpreasons.delay ()) { LOG ("delaying congruence %" PRId64 " more times", congruence_delay.bumpreasons.limit); - congruence_delay.bumpreasons.limit--; return false; } - // to remove false literals from clauses - // It makes the technique stronger as long clauses - // can become binary / ternary - // garbage_collection (); - - const int64_t old = stats.congruence.congruent; - const int old_merged = stats.congruence.congruent; - // congruencebinary is already doing it (and more actually) if (!internal->opts.congruencebinaries) { + if (remove_units_before_run) { + clear_watches (); + mark_satisfied_clauses_as_garbage (); // breaks watch lists + connect_watches (); + } const bool dedup = opts.deduplicate; opts.deduplicate = true; mark_duplicated_binary_clauses_as_garbage (); opts.deduplicate = dedup; + } else if (remove_units_before_run) { + // to remove false literals from clauses + // It makes the technique stronger as long clauses + // can become binary / ternary + + // In the SC2024 there are benchmarks where 16% of variables are units + // at the beginning, so we remove units during preprocessing. Later GC + // and other techniques are doing it anyway, so we try to avoid going + // over all clauses once more. + mark_satisfied_clauses_as_garbage (); // breaks watch lists but we + // unwatch anyway afterwards + report ('.'); } ++stats.congruence.rounds; clear_watches (); @@ -7470,13 +7808,14 @@ bool Internal::extract_gates () { CADICAL_assert (unsat || closure.chain.empty ()); CADICAL_assert (unsat || lrat_chain.empty ()); closure.extract_binaries (); + const int64_t old_merged = + stats.congruence + .congruent; // the binary stuff is covered by other techniques CADICAL_assert (unsat || closure.chain.empty ()); CADICAL_assert (unsat || lrat_chain.empty ()); closure.extract_gates (); CADICAL_assert (unsat || closure.chain.empty ()); CADICAL_assert (unsat || lrat_chain.empty ()); - internal->clear_watches (); - internal->connect_watches (); closure.reset_extraction (); if (!unsat) { @@ -7502,7 +7841,6 @@ bool Internal::extract_gates () { internal->connect_watches (); if (!internal->unsat) { propagated2 = propagated = 0; - propagate (); } CADICAL_assert (closure.new_unwatched_binary_clauses.empty ()); internal->reset_occs (); @@ -7512,23 +7850,22 @@ bool Internal::extract_gates () { const int64_t new_merged = stats.congruence.congruent; -#ifndef CADICAL_QUIET - phase ("congruence-phase", stats.congruence.rounds, "merged %ld literals", - new_merged - old_merged); -#endif - if (!unsat && !internal->propagate ()) - unsat = true; + PHASE ("congruence-phase", stats.congruence.rounds, + "merged %" PRId64 " literals", new_merged - old_merged); + if (!unsat && !internal->propagate ()) { + learn_empty_clause (); + } STOP_SIMPLIFIER (congruence, CONGRUENCE); - report ('c', !opts.reportall && !(stats.congruence.congruent - old)); + report ('c', + !opts.reportall && !(stats.congruence.congruent - old_merged)); #ifndef CADICAL_NDEBUG size_t watched = 0; for (auto v : vars) { for (auto sgn = -1; sgn <= 1; sgn += 2) { const int lit = v * sgn; for (auto w : watches (lit)) { - if (w.binary ()) - CADICAL_assert (!w.clause->garbage); + CADICAL_assert (!w.binary () || !w.clause->garbage); if (w.clause->garbage) continue; ++watched; @@ -7549,16 +7886,11 @@ bool Internal::extract_gates () { CADICAL_assert (!internal->occurring ()); if (new_merged == old_merged) { - congruence_delay.bumpreasons.interval++; + congruence_delay.bumpreasons.bump_delay (); } else { - congruence_delay.bumpreasons.interval /= 2; + congruence_delay.bumpreasons.reduce_delay (); } - LOG ("delay congruence internal %" PRId64, - congruence_delay.bumpreasons.interval); - congruence_delay.bumpreasons.limit = - congruence_delay.bumpreasons.interval; - return new_merged != old_merged; } diff --git a/src/sat/cadical/cadical_decide.cpp b/src/sat/cadical/cadical_decide.cpp index 0874cffd7..2a8d772a2 100644 --- a/src/sat/cadical/cadical_decide.cpp +++ b/src/sat/cadical/cadical_decide.cpp @@ -41,7 +41,83 @@ int Internal::next_decision_variable_with_best_score () { return res; } +void Internal::start_random_sequence () { + if (!opts.randec) + return; + + CADICAL_assert (!stable || opts.randecstable); + CADICAL_assert (stable || opts.randecfocused); + CADICAL_assert (!randomized_deciding); + + const uint64_t count = ++stats.randec.random_decision_phases; + const unsigned length = opts.randeclength * log (count + 10); + VERBOSE (3, + "starting random decision sequence " + "at %" PRId64 " conflicts for %u conflicts", + stats.conflicts, length); + randomized_deciding = length; + + const double delta = stats.randec.random_decision_phases * + log (stats.randec.random_decision_phases); + lim.random_decision = stats.conflicts + delta * opts.randecint; + VERBOSE (3, + "next random decision sequence " + "at %" PRId64 " conflicts current conflict: %" PRId64 + " conflicts", + lim.random_decision, stats.conflicts); +} + +int Internal::next_random_decision () { + CADICAL_assert (max_var); + if (!opts.randec) + return 0; + if (stable && !opts.randecstable) + return 0; + if (!stable && !opts.randecfocused) + return 0; + if (stats.conflicts < lim.random_decision) + return 0; + if (satisfied ()) + return 0; + + if (!randomized_deciding) { + if (level > (int) assumptions.size () + !!constraint.size ()) { + LOG ("random decision delayed because too deep"); + return 0; + } + start_random_sequence (); + } + LOG ("searching for random decision"); + Random random (internal->opts.seed); + random += stats.decisions; + ++stats.randec.random_decisions; + for (;;) { + int idx = 1 + (random.next () % max_var); + LOG ("trying lit %s", LOGLIT (idx)); + /* + // Kissat filters out active literals but we cannot do that because + // eliminated variables are not actively removed. + if (!flags (idx).active()) + continue; + */ + if (val (idx)) + continue; + return idx; + } + CADICAL_assert (false); +#if defined(WIN32) && !defined(__MINGW32__) + __assume(false); +#else + __builtin_unreachable (); +#endif +} + int Internal::next_decision_variable () { + int res = next_random_decision (); + if (res) { + LOG ("randomized decision %s", LOGLIT (res)); + return res; + } if (use_scores ()) return next_decision_variable_with_best_score (); else @@ -57,16 +133,39 @@ int Internal::next_decision_variable () { int Internal::decide_phase (int idx, bool target) { const int initial_phase = opts.phase ? 1 : -1; int phase = 0; - if (force_saved_phase) + if (force_saved_phase) { phase = phases.saved[idx]; - if (!phase) + LOG ("trying force_saved_phase, i.e., %d", phase); + } + CADICAL_assert (force_saved_phase || !phase); + if (!phase) { phase = phases.forced[idx]; // swapped with opts.forcephase case! - if (!phase && opts.forcephase) + LOG ("trying forced phase, i.e., %d", phase); + } + if (!phase && opts.forcephase) { phase = initial_phase; - if (!phase && target) + LOG ("trying initial phase, i.e., %d", phase); + } + if (!phase && target) { phase = phases.target[idx]; - if (!phase) - phase = phases.saved[idx]; + } + if (!phase) { + // ported from kissat where it does not seem very useful + if (opts.stubbornIOfocused && opts.rephase == 2) + switch ((stats.rephased.total >> 1) & 7) { + case 1: + phase = initial_phase; + break; + case 5: // kissat has 3 but 5 looks better + phase = -initial_phase; + break; + default: + phase = phases.saved[idx]; + break; + } + else + phase = phases.saved[idx]; + } // The following should not be necessary and in some version we had even // a hard 'COVER' CADICAL_assertion here to check for this. Unfortunately it diff --git a/src/sat/cadical/cadical_decompose.cpp b/src/sat/cadical/cadical_decompose.cpp index 9081321b4..b925da138 100644 --- a/src/sat/cadical/cadical_decompose.cpp +++ b/src/sat/cadical/cadical_decompose.cpp @@ -473,6 +473,11 @@ bool Internal::decompose_round () { } external->push_binary_clause_on_extension_stack (id2, idx, -other); decompose_ids[vlit (idx)] = id2; + for (auto &tracer : tracers) { + const int eidx = externalize (idx); + const int eother = externalize (other); + tracer->notify_equivalence (eidx, eother); + } clause.clear (); lrat_chain.clear (); diff --git a/src/sat/cadical/cadical_drattracer.cpp b/src/sat/cadical/cadical_drattracer.cpp index eb73b4c95..badfe8e67 100644 --- a/src/sat/cadical/cadical_drattracer.cpp +++ b/src/sat/cadical/cadical_drattracer.cpp @@ -87,7 +87,7 @@ void DratTracer::drat_delete_clause (const vector &clause) { /*------------------------------------------------------------------------*/ -void DratTracer::add_derived_clause (int64_t, bool, +void DratTracer::add_derived_clause (int64_t, bool, int, const vector &clause, const vector &) { if (file->closed ()) diff --git a/src/sat/cadical/cadical_elim.cpp b/src/sat/cadical/cadical_elim.cpp index 951dfee74..60315d47e 100644 --- a/src/sat/cadical/cadical_elim.cpp +++ b/src/sat/cadical/cadical_elim.cpp @@ -24,9 +24,8 @@ namespace CaDiCaL { inline double Internal::compute_elim_score (unsigned lit) { CADICAL_assert (1 <= lit), CADICAL_assert (lit <= (unsigned) max_var); - const unsigned uidx = 2 * lit; - const double pos = internal->ntab[uidx]; - const double neg = internal->ntab[uidx + 1]; + const double pos = noccs (lit); + const double neg = noccs (-lit); if (!pos) return -neg; if (!neg) @@ -1043,7 +1042,7 @@ void Internal::elim (bool update_limits) { #endif // Make sure there was a complete subsumption phase since last - // elimination including vivification etc. + // elimination // if (last.elim.subsumephases == stats.subsumephases) subsume (); diff --git a/src/sat/cadical/cadical_elimfast.cpp b/src/sat/cadical/cadical_elimfast.cpp index 90dacf8c0..95ad070b4 100644 --- a/src/sat/cadical/cadical_elimfast.cpp +++ b/src/sat/cadical/cadical_elimfast.cpp @@ -196,17 +196,18 @@ void Internal::try_to_fasteliminate_variable (Eliminator &eliminator, // First flush garbage clauses and check limits. + const int64_t occ_bound = opts.fastelimocclim; int64_t bound = opts.fastelimbound; int64_t pos = flush_elimfast_occs (pivot); - if (pos > bound) { + int64_t neg = flush_elimfast_occs (-pivot); + if (neg && pos > occ_bound) { LOG ("too many occurrences thus not eliminated %d", pivot); CADICAL_assert (!eliminator.schedule.contains (abs (pivot))); return; } - int64_t neg = flush_elimfast_occs (-pivot); - if (neg > bound) { + if (pos && neg > occ_bound) { LOG ("too many occurrences thus not eliminated %d", -pivot); CADICAL_assert (!eliminator.schedule.contains (abs (pivot))); return; @@ -286,7 +287,6 @@ int Internal::elimfast_round (bool &completed, delta = opts.elimmineff; if (delta > opts.elimmaxeff) delta = opts.elimmaxeff; - delta = max (delta, (int64_t) 2l * active ()); PHASE ("fastelim-round", stats.elimfastrounds, "limit of %" PRId64 " resolutions", delta); diff --git a/src/sat/cadical/cadical_extend.cpp b/src/sat/cadical/cadical_extend.cpp index c549e90fa..fab83ee3c 100644 --- a/src/sat/cadical/cadical_extend.cpp +++ b/src/sat/cadical/cadical_extend.cpp @@ -22,6 +22,14 @@ void External::push_id_on_extension_stack (int64_t id) { void External::push_clause_literal_on_extension_stack (int ilit) { CADICAL_assert (ilit); const int elit = internal->externalize (ilit); + const int eidx = abs (elit); + const bool is_extension_var = ervars[eidx]; + Flags &f = internal->flags (ilit); + if (is_extension_var) { + f.factored_but_on_reconstruction_stack = true; + LOG ("marking lit %s as tainted", LOGLIT (ilit)); + } + CADICAL_assert (elit); extension.push_back (elit); LOG ("pushing clause literal %d on extension stack (internal %d)", elit, diff --git a/src/sat/cadical/cadical_external.cpp b/src/sat/cadical/cadical_external.cpp index 67d1f918d..f6d7d652f 100644 --- a/src/sat/cadical/cadical_external.cpp +++ b/src/sat/cadical/cadical_external.cpp @@ -1,6 +1,8 @@ #include "global.h" #include "internal.hpp" +#include "util.hpp" + #include ABC_NAMESPACE_IMPL_START @@ -43,6 +45,11 @@ void External::init (int new_max_var, bool extension) { if ((size_t) new_max_var >= vsize) enlarge (new_max_var); LOG ("initialized %d external variables", new_vars); + reserve_at_least (ext_units, 2 * new_max_var + 2); + reserve_at_least (e2i, new_max_var + 1); + reserve_at_least (ervars, new_max_var + 1); + reserve_at_least (ext_flags, new_max_var + 1); + reserve_at_least (internal->i2e, new_max_var + 1); if (!max_var) { CADICAL_assert (e2i.empty ()); e2i.push_back (0); @@ -74,8 +81,6 @@ void External::init (int new_max_var, bool extension) { internal->stats.variables_extension += new_vars; else internal->stats.variables_original += new_vars; - if (new_max_var >= (int64_t) is_observed.size ()) - is_observed.resize (1 + (size_t) new_max_var, false); if (internal->opts.checkfrozen) if (new_max_var >= (int64_t) moltentab.size ()) moltentab.resize (1 + (size_t) new_max_var, false); @@ -415,6 +420,8 @@ void External::remove_observed_var (int elit) { if (eidx > max_var) return; + if ((size_t) eidx <= is_observed.size ()) + return; if (is_observed[eidx]) { // Follow opposite order of add_observed_var, first remove internal // is_observed @@ -438,11 +445,11 @@ void External::reset_observed_vars () { if (!is_observed.size ()) return; - CADICAL_assert (!max_var || (size_t) max_var + 1 == is_observed.size ()); - for (auto elit : vars) { int eidx = abs (elit); CADICAL_assert (eidx <= max_var); + if ((size_t) eidx >= is_observed.size ()) + break; if (is_observed[eidx]) { int ilit = internalize (elit); internal->remove_observed_var (ilit); @@ -514,7 +521,7 @@ void External::implied (std::vector &trailed) { // (Internal does not see these marks, so no earlier filter is // possible.) - trailed.clear(); + trailed.clear (); for (const auto &ilit : ilit_implicants) { CADICAL_assert (ilit); diff --git a/src/sat/cadical/cadical_external_propagate.cpp b/src/sat/cadical/cadical_external_propagate.cpp index 215170c8b..43224d4c7 100644 --- a/src/sat/cadical/cadical_external_propagate.cpp +++ b/src/sat/cadical/cadical_external_propagate.cpp @@ -2,6 +2,8 @@ #include "internal.hpp" +#include + ABC_NAMESPACE_IMPL_START namespace CaDiCaL { @@ -99,6 +101,7 @@ void Internal::set_tainted_literal () { } void Internal::renotify_trail_after_ilb () { + CADICAL_assert (opts.ilb); if (!external_prop || external_prop_is_lazy || !trail.size () || !opts.ilb) { return; @@ -124,9 +127,57 @@ void Internal::renotify_trail_after_local_search () { renotify_full_trail (); } +void Internal::renotify_full_trail_between_trail_pos ( + int start_level, int end_level, int propagator_level, + std::vector &assigned, bool start_new_level) { + CADICAL_assert (assigned.empty ()); + int j = start_level; +#ifdef LOGGING + LOG ("starting notification of level %d from trail %d .. %d", + propagator_level, start_level, end_level); +#else + (void) propagator_level; +#endif + if (start_new_level) { + if (assigned.size ()) + external->propagator->notify_assignment (assigned); + assigned.clear (); + external->propagator->notify_new_decision_level (); + } + for (; j < end_level; ++j) { + int ilit = trail[j]; + // In theory, 0 ilit can happen due to pseudo-decision levels + if (!ilit) + continue; + + if (!observed (ilit)) + continue; + + int elit = externalize (ilit); // TODO: double-check tainting + + LOG ("notifying elit %d @ %d aka %s", propagator_level, elit, + LOGLIT (ilit)); + CADICAL_assert (elit); + // Fixed variables might get mapped (during compact) to another + // non-observed but fixed variable. + // This happens on root level, so notification about their assignment + // is already done. + CADICAL_assert (external->observed (elit) || fixed (ilit)); + if (!external->ervars[abs (elit)]) + assigned.push_back (elit); + } + + if (assigned.size ()) + external->propagator->notify_assignment (assigned); + assigned.clear (); +} + // It repeats ALL assignments of the trail, so the already notified // root-level assignments will be notified multiple times. - +// +// As CaDiCaL is missing some '0' seperators, it is important to go +// over slices from the control stack instead of going over the trail +// directly. void Internal::renotify_full_trail () { const size_t end_of_trail = trail.size (); if (level) { @@ -136,56 +187,38 @@ void Internal::renotify_full_trail () { } std::vector assigned; - int prev_max_level = 0; - int current_level = 0; int propagator_level = 0; - while (notified < end_of_trail) { - int ilit = trail[notified++]; - // In theory, 0 ilit can happen due to pseudo-decision levels - if (!ilit) - current_level = prev_max_level + 1; - else - current_level = var (ilit).level; - - if (current_level > propagator_level) { - if (assigned.size ()) - external->propagator->notify_assignment (assigned); - while (current_level > propagator_level) { - external->propagator->notify_new_decision_level (); - propagator_level++; - } - assigned.clear (); - } - // Current level can be smaller than prev_max_level due to chrono - if (current_level > prev_max_level) - prev_max_level = current_level; - - if (!observed (ilit)) - continue; - - int elit = externalize (ilit); // TODO: double-check tainting - CADICAL_assert (elit); - // Fixed variables might get mapped (during compact) to another - // non-observed but fixed variable. - // This happens on root level, so notification about their assignment is - // already done. - CADICAL_assert (external->observed (elit) || fixed (ilit)); - if (!external->ervars[abs (elit)]) - assigned.push_back (elit); + const int c_size = control.size (); + { // first all root-level literals + const int start_level = 0; + const int end_level = + (control.size () > 1 ? control[1].trail : end_of_trail); + renotify_full_trail_between_trail_pos ( + start_level, end_level, propagator_level, assigned, false); } - if (assigned.size ()) - external->propagator->notify_assignment (assigned); - assigned.clear (); - // In case there are some left over empty levels on the top of the trail, - // the external propagtor must be notified about them so the levels are - // synced - while (level > propagator_level) { - external->propagator->notify_new_decision_level (); + // notify all intermediate levels + for (int i = 2; i < c_size; ++i) { + const int start_level = control[i - 1].trail; + const int end_level = control[i].trail; propagator_level++; + LOG ("notification of %d", propagator_level); + + renotify_full_trail_between_trail_pos ( + start_level, end_level, propagator_level, assigned, true); } + // and the current level if there is non-root level one + if (level) { + const int start_level = control.back ().trail; + propagator_level++; + renotify_full_trail_between_trail_pos ( + start_level, end_of_trail, propagator_level, assigned, true); + } + CADICAL_assert (propagator_level == level); + notified = trail.size (); + return; } @@ -849,7 +882,10 @@ bool Internal::external_check_solution () { // Here the variables must be filtered by external->is_observed, // because fixed variables are internally not necessarily observed // anymore. - for (int idx = 1; idx <= external->max_var; idx++) { + for (int idx = 1; + idx <= std::min ((int) external->is_observed.size () - 1, + external->max_var); + idx++) { if (!external->is_observed[idx]) continue; const int lit = external->ival (idx); diff --git a/src/sat/cadical/cadical_factor.cpp b/src/sat/cadical/cadical_factor.cpp index 3fee40685..a87ad237d 100644 --- a/src/sat/cadical/cadical_factor.cpp +++ b/src/sat/cadical/cadical_factor.cpp @@ -540,7 +540,7 @@ void Internal::add_self_subsuming_factor (Quotient *q, Quotient *p) { CADICAL_assert (lrat_chain.size () == 2); } if (clause.size () > 1) { - new_factor_clause (); + new_factor_clause (0); } else { const int unit = clause[0]; const signed char tmp = val (unit); @@ -599,9 +599,9 @@ bool Internal::self_subsuming_factor (Quotient *q) { void Internal::add_factored_divider (Quotient *q, int fresh) { const int factor = q->factor; LOG ("factored %d divider %d", factor, fresh); - clause.push_back (factor); clause.push_back (fresh); - new_factor_clause (); + clause.push_back (factor); + new_factor_clause (fresh); clause.clear (); if (lrat) mini_chain.push_back (-clause_id); @@ -616,11 +616,12 @@ void Internal::blocked_clause (Quotient *q, int not_fresh) { int64_t new_id = ++clause_id; q->bid = new_id; CADICAL_assert (clause.empty ()); + clause.push_back (not_fresh); for (Quotient *p = q; p; p = p->prev) clause.push_back (-p->factor); - clause.push_back (not_fresh); CADICAL_assert (!lrat || mini_chain.size ()); - proof->add_derived_clause (new_id, true, clause, mini_chain); + proof->add_derived_rat_clause (new_id, true, externalize (not_fresh), + clause, mini_chain); mini_chain.clear (); clause.clear (); } @@ -654,15 +655,15 @@ void Internal::add_factored_quotient (Quotient *q, int not_fresh) { lrat_chain.push_back (q->bid); } clause.push_back (not_fresh); - new_factor_clause (); + new_factor_clause (0); clause.clear (); lrat_chain.clear (); } if (proof) { + clause.push_back (not_fresh); for (Quotient *p = q; p; p = p->prev) { clause.push_back (-p->factor); } - clause.push_back (not_fresh); proof->delete_clause (q->bid, true, clause); clause.clear (); } @@ -769,6 +770,79 @@ void Internal::schedule_factorization (Factoring &factoring) { #endif } +void Internal::adjust_scores_and_phases_of_fresh_variables ( + Factoring &factoring) { + if (!opts.factorunbump) { + factoring.fresh.clear (); + return; + } + if (factoring.fresh.empty ()) + return; + +#if 0 // the scores are very low anyway + for (auto lit : factoring.fresh) { + CADICAL_assert (lit > 0 && internal->max_var); + const double old_score = internal->stab[lit]; + // make the scores a little different from each other with the newest having the highest score + const double new_score = 1.0 / (double)(internal->max_var - lit); + if (old_score == new_score) + continue; + if (!scores.contains (lit)) + continue; + LOG ("unbumping %s", LOGLIT(lit)); + internal->stab[lit] = new_score; + scores.update (lit); + } +#endif + + for (auto lit : factoring.fresh) { + LOG ("dequeuing %s", LOGLIT (lit)); + queue.dequeue (links, lit); + } + + for (auto lit : factoring.fresh) { + LOG ("dequeuing %s", LOGLIT (lit)); + queue.bury (links, lit); + } + + // fix the scores with negative numbers + int lit = queue.first; + queue.bumped = 0; + while (lit) { + btab[lit] = ++queue.bumped; + lit = links[lit].next; + } + stats.bumped = queue.bumped; + update_queue_unassigned (queue.last); + +#ifndef CADICAL_NDEBUG + for (auto v : vars) + CADICAL_assert (val (v) || scores.contains (v)); + lit = queue.first; + int next_lit = links[lit].next; + while (next_lit) { + CADICAL_assert (btab[lit] < btab[next_lit]); + const int tmp = links[next_lit].next; + CADICAL_assert (!tmp || links[tmp].prev == next_lit); + lit = next_lit; + next_lit = tmp; + } + + lit = queue.last; + next_lit = links[lit].prev; + while (next_lit) { + CADICAL_assert (btab[lit] > btab[next_lit]); + const int tmp = links[next_lit].prev; + CADICAL_assert (!tmp || links[tmp].next == next_lit); + lit = next_lit; + next_lit = tmp; + } + CADICAL_assert (queue.first); + CADICAL_assert (queue.last); +#endif + factoring.fresh.clear (); +} + bool Internal::run_factorization (int64_t limit) { Factoring factoring = Factoring (this, limit); schedule_factorization (factoring); @@ -834,8 +908,7 @@ bool Internal::run_factorization (int64_t limit) { const unsigned idx = factoring.schedule.front (); completed = occs (u2i (idx)).empty (); } - // kissat initializes scores for new variables at this point, however - // this is actually done already during resize of internal + adjust_scores_and_phases_of_fresh_variables (factoring); #ifndef CADICAL_QUIET report ('f', !factored); #endif @@ -863,6 +936,19 @@ bool Internal::factor () { return false; if (!opts.factor) return false; + + int v_active = active (); + size_t log_active = log10 (v_active); + size_t eliminations = stats.elimrounds; + size_t delay = opts.factordelay; + size_t delay_limit = eliminations + delay; + if (log_active > delay_limit) { + VERBOSE (3, + "factorization delayed as %zu = log10 (%u)" + "> eliminations + delay = %zu + %zu = %zu", + log_active, v_active, eliminations, delay, delay_limit); + return false; + } // The following CADICAL_assertion fails if there are *only* user propagator // clauses (which are redundant). // CADICAL_assert (stats.mark.factor || clauses.empty ()); @@ -879,6 +965,8 @@ bool Internal::factor () { if (!stats.factor) limit += opts.factoriniticks * 1e6; + mark_duplicated_binary_clauses_as_garbage (); + START_SIMPLIFIER (factor, FACTOR); stats.factor++; diff --git a/src/sat/cadical/cadical_flags.cpp b/src/sat/cadical/cadical_flags.cpp index 5c06a33b4..7b2b650cf 100644 --- a/src/sat/cadical/cadical_flags.cpp +++ b/src/sat/cadical/cadical_flags.cpp @@ -31,7 +31,7 @@ void Internal::mark_fixed (int lit) { // to know about it. // But at that point it is not guaranteed to be already on the trail, so // notification will happen only later. - CADICAL_assert (!level); + CADICAL_assert (!level || in_mode (BACKBONE)); } } diff --git a/src/sat/cadical/cadical_frattracer.cpp b/src/sat/cadical/cadical_frattracer.cpp index d35a18271..90f3e1a42 100644 --- a/src/sat/cadical/cadical_frattracer.cpp +++ b/src/sat/cadical/cadical_frattracer.cpp @@ -196,7 +196,7 @@ void FratTracer::add_original_clause (int64_t id, bool, frat_add_original_clause (id, clause); } -void FratTracer::add_derived_clause (int64_t id, bool, +void FratTracer::add_derived_clause (int64_t id, bool, int, const vector &clause, const vector &chain) { if (file->closed ()) diff --git a/src/sat/cadical/cadical_idruptracer.cpp b/src/sat/cadical/cadical_idruptracer.cpp index ec6467163..3028122f7 100644 --- a/src/sat/cadical/cadical_idruptracer.cpp +++ b/src/sat/cadical/cadical_idruptracer.cpp @@ -391,7 +391,7 @@ void IdrupTracer::idrup_solve_query () { /*------------------------------------------------------------------------*/ -void IdrupTracer::add_derived_clause (int64_t, bool, +void IdrupTracer::add_derived_clause (int64_t, bool, int, const vector &clause, const vector &) { if (file->closed ()) diff --git a/src/sat/cadical/cadical_internal.cpp b/src/sat/cadical/cadical_internal.cpp index abe18c59c..40ad514ff 100644 --- a/src/sat/cadical/cadical_internal.cpp +++ b/src/sat/cadical/cadical_internal.cpp @@ -27,8 +27,9 @@ Internal::Internal () tainted_literal (0), notified (0), probe_reason (0), propagated (0), propagated2 (0), propergated (0), best_assigned (0), target_assigned (0), no_conflict_until (0), unsat_constraint (false), - marked_failed (true), sweep_incomplete (false), citten (0), - num_assigned (0), proof (0), opts (this), + marked_failed (true), sweep_incomplete (false), + randomized_deciding (false), citten (0), num_assigned (0), proof (0), + opts (this), #ifndef CADICAL_QUIET profiles (this), force_phase_messages (false), #endif @@ -38,8 +39,10 @@ Internal::Internal () control.push_back (Level (0, 0)); // The 'dummy_binary' is used in 'try_to_subsume_clause' to fake a real - // clause, which then can be used to subsume or strengthen the given - // clause in one routine for both binary and non binary clauses. This + // clause (which then can be used to subsume or strengthen the given + // clause in one routine for both binary and non binary clauses) and + // in walk (which is only used as a placeholder in the watch lists + // when logging is off, since the clause is not accessed). This // fake binary clause is always kept non-redundant (and not-moved etc.) // due to the following 'memset'. Only literals will be changed. @@ -51,6 +54,8 @@ Internal::Internal () dummy_binary = (Clause *) new char[bytes]; memset (dummy_binary, 0, bytes); dummy_binary->size = 2; + + /*with C++17: static_*/ CADICAL_assert (max_used == (1 << USED_SIZE) - 1); } Internal::~Internal () { @@ -147,7 +152,6 @@ void Internal::enlarge (int new_max_var) { enlarge_zero (phases.target, new_vsize); enlarge_zero (phases.best, new_vsize); enlarge_zero (phases.prev, new_vsize); - enlarge_zero (phases.min, new_vsize); enlarge_zero (marks, new_vsize); } @@ -348,8 +352,8 @@ int Internal::propagate_assumptions () { if (proof) proof->solve_query (); if (opts.ilb) { - if (opts.ilbassumptions) - sort_and_reuse_assumptions (); + sort_and_reuse_assumptions (); + CADICAL_assert (opts.ilb == 2 || (size_t) level <= assumptions.size ()); stats.ilbtriggers++; stats.ilbsuccess += (level > 0); stats.levelsreused += level; @@ -419,9 +423,9 @@ void Internal::implied (std::vector &entrailed) { int last_assumption_level = assumptions.size (); if (constraint.size ()) last_assumption_level++; - - size_t trail_limit = trail.size(); - if (level > last_assumption_level) + + size_t trail_limit = trail.size (); + if (level > last_assumption_level) trail_limit = control[last_assumption_level + 1].trail; for (size_t i = 0; i < trail_limit; i++) @@ -559,6 +563,7 @@ void Internal::init_search_limits () { lim.rephase = stats.conflicts + opts.rephaseint; lim.rephased[0] = lim.rephased[1] = 0; + last.stabilize.rephased = 0; LOG ("new rephase limit %" PRId64 " after %" PRId64 " conflicts", lim.rephase, lim.rephase - stats.conflicts); @@ -589,17 +594,18 @@ void Internal::init_search_limits () { } else LOG ("keeping non-stable phase"); - if (!incremental) { - inc.stabilize = 0; - lim.stabilize = stats.conflicts + opts.stabilizeinit; - LOG ("initial stabilize limit %" PRId64 " after %d conflicts", - lim.stabilize, (int) opts.stabilizeinit); - } + inc.stabilize = 0; + last.stabilize.conflicts = stats.conflicts; + lim.stabilize = stats.conflicts + opts.stabilizeinit; + last.stabilize.ticks = stats.ticks.search[0]; + stats.stabphases = 0; + LOG ("new ticks-based stabilize limit %" PRId64 " after %d conflicts", + lim.stabilize, (int) opts.stabilizeinit); - if (opts.stabilize && opts.reluctant) { + if (opts.stabilize && opts.reluctant && opts.reluctantint) { LOG ("new restart reluctant doubling sequence period %d", opts.reluctant); - reluctant.enable (opts.reluctant, opts.reluctantmax); + reluctant.enable (opts.reluctantint, opts.reluctantmax); } else reluctant.disable (); @@ -622,11 +628,20 @@ void Internal::init_search_limits () { LOG ("no limit on decisions"); } else { lim.decisions = stats.decisions + inc.decisions; - LOG ("conflict limit after %" PRId64 " decisions at %" PRId64 + LOG ("decision limit after %" PRId64 " decisions at %" PRId64 " decisions", inc.decisions, lim.decisions); } + if (inc.ticks < 0) { + lim.ticks = -1; + LOG ("no limit on ticks"); + } else { + lim.ticks = stats.ticks.search[0] + stats.ticks.search[1] + inc.ticks; + LOG ("ticks limit after %" PRId64 " ticks at %" PRId64 " ticks", + inc.ticks, lim.ticks); + } + /*----------------------------------------------------------------------*/ // Initial preprocessing rounds. @@ -639,6 +654,41 @@ void Internal::init_search_limits () { LOG ("limiting to %" PRId64 " local search rounds", lim.localsearch); } + /*----------------------------------------------------------------------*/ + // tier 1 and tier 2 limits + if (incremental && opts.recomputetier) { + for (auto m : {true, false}) + for (auto &u : stats.used[m]) + u = 0; + stats.bump_used = {0, 0}; + for (auto u : {true, false}) { + tier1[u] = max (tier1[u], opts.tier1minglue ? opts.tier1minglue : 2); + tier2[u] = max (tier2[u], opts.tier2minglue ? opts.tier2minglue : 6); + } + stats.tierecomputed = 0; + } + + /*----------------------------------------------------------------------*/ + // clause decaying + if (incremental) + last.incremental_decay.last_id = 0; + else { + lim.incremental_decay = opts.incdecayint; + } + + /*----------------------------------------------------------------------*/ + + if (incremental) + mode = "keeping"; + else { + lim.random_decision = stats.conflicts + opts.randecinit; + mode = "initial"; + } + (void) mode; + LOG ("%s randomize decision limit %" PRId64 " after %" PRId64 + " conflicts", + mode, lim.random_decision, lim.random_decision - stats.conflicts); + /*----------------------------------------------------------------------*/ lim.initialized = true; @@ -693,28 +743,34 @@ bool Internal::preprocess_round (int round) { } // for now counts as one of the preprocessing rounds TODO: change this? -void Internal::preprocess_quickly () { +void Internal::preprocess_quickly (bool always) { if (unsat) return; if (!max_var) return; if (!opts.preprocesslight) return; + if (!always && stats.searches > 1) + return; START (preprocess); +#ifndef CADICAL_QUIET struct { int64_t vars, clauses; } before, after; before.vars = active (); before.clauses = stats.current.irredundant; +#endif // stats.preprocessings++; CADICAL_assert (!preprocessing); preprocessing = true; + report ('('); PHASE ("preprocessing", stats.preprocessings, "starting with %" PRId64 " variables and %" PRId64 " clauses", before.vars, before.clauses); - if (extract_gates ()) + if (extract_gates (true)) decompose (); + binary_clauses_backbone (); if (sweep ()) decompose (); @@ -724,10 +780,12 @@ void Internal::preprocess_quickly () { if (opts.fastelim) elimfast (); - // if (opts.condition) - // condition (false); + // if (opts.condition) + // condition (false); +#ifndef CADICAL_QUIET after.vars = active (); after.clauses = stats.current.irredundant; +#endif CADICAL_assert (preprocessing); preprocessing = false; PHASE ("preprocessing", stats.preprocessings, @@ -737,11 +795,17 @@ void Internal::preprocess_quickly () { report ('P'); } -int Internal::preprocess () { - preprocess_quickly (); +int Internal::preprocess (bool always) { + int res = 0; + if (!level && !unsat && opts.luckyearly) + res = lucky_phases (); + if (res) + return res; + preprocess_quickly (always); for (int i = 0; i < lim.preprocessing; i++) if (!preprocess_round (i)) break; + report (')'); if (unsat) return 20; return 0; @@ -836,7 +900,11 @@ int Internal::local_search_round (int round) { else limit = LONG_MAX; - int res = walk_round (limit, true); + int res; + if (opts.walkfullocc) + res = walk_full_occs_round (limit, true); + else + res = walk_round (limit, true); CADICAL_assert (localsearching); localsearching = false; @@ -883,12 +951,13 @@ int Internal::local_search () { // int Internal::solve (bool preprocess_only) { CADICAL_assert (clause.empty ()); + stats.searches++; START (solve); if (proof) proof->solve_query (); if (opts.ilb) { - if (opts.ilbassumptions) - sort_and_reuse_assumptions (); + sort_and_reuse_assumptions (); + CADICAL_assert (opts.ilb || (size_t) level <= assumptions.size ()); stats.ilbtriggers++; stats.ilbsuccess += (level > 0); stats.levelsreused += level; @@ -919,12 +988,14 @@ int Internal::solve (bool preprocess_only) { res = local_search (); } if (!res && !level) - res = preprocess (); + res = preprocess (preprocess_only); if (!preprocess_only) { + if (!res && !level && opts.luckylate) + res = lucky_phases (); if (!res && !level) res = local_search (); - if (!res && !level) - res = lucky_phases (); + if (!res) + decay_clauses_upon_incremental_clauses (); if (!res || (res == 10 && external_prop)) { if (res == 10 && external_prop && level) backtrack (); diff --git a/src/sat/cadical/cadical_kitten.c b/src/sat/cadical/cadical_kitten.c index da3738980..031c21b01 100644 --- a/src/sat/cadical/cadical_kitten.c +++ b/src/sat/cadical/cadical_kitten.c @@ -497,11 +497,12 @@ static void clear_cadical_kitten (cadical_kitten *cadical_kitten) { void *OLD_PTR = (P); \ CALLOC (T, (P), new_size / 2); \ const size_t BYTES = old_vars * sizeof *(P); \ - memcpy ((P), OLD_PTR, BYTES); \ + if ((P) && OLD_PTR) /* nullptr not allowed */ \ + memcpy ((P), OLD_PTR, BYTES); \ void *NEW_PTR = (P); \ (P) = (T*)OLD_PTR; \ DEALLOC ((P), old_size / 2); \ - (P) = (T*)NEW_PTR; \ + (P) = (T*)NEW_PTR; \ } while (0) #define RESIZE2(T, P) \ @@ -509,7 +510,8 @@ static void clear_cadical_kitten (cadical_kitten *cadical_kitten) { void *OLD_PTR = (P); \ CALLOC (T, (P), new_size); \ const size_t BYTES = old_lits * sizeof *(P); \ - memcpy ((P), OLD_PTR, BYTES); \ + if ((P) && OLD_PTR) /* nullptr not allowed */ \ + memcpy ((P), OLD_PTR, BYTES); \ void *NEW_PTR = (P); \ (P) = (T*)OLD_PTR; \ DEALLOC ((P), old_size); \ @@ -839,7 +841,8 @@ static void enlarge_external (cadical_kitten *cadical_kitten, size_t eidx) { unsigned *old_import = cadical_kitten->import; CALLOC (unsigned, cadical_kitten->import, new_size); const size_t bytes = old_evars * sizeof *cadical_kitten->import; - memcpy (cadical_kitten->import, old_import, bytes); + if (cadical_kitten->import && old_import) + memcpy (cadical_kitten->import, old_import, bytes); DEALLOC (old_import, old_size); cadical_kitten->esize = new_size; } @@ -927,10 +930,14 @@ void cadical_kitten_clear (cadical_kitten *cadical_kitten) { CADICAL_assert (!cadical_kitten->marks[i]); #endif - memset (cadical_kitten->phases, 0, vars); - memset (cadical_kitten->values, 0, lits); - memset (cadical_kitten->failed, 0, lits); - memset (cadical_kitten->vars, 0, vars); + if (cadical_kitten->phases) + memset (cadical_kitten->phases, 0, vars); + if (cadical_kitten->values) + memset (cadical_kitten->values, 0, lits); + if (cadical_kitten->failed) + memset (cadical_kitten->failed, 0, lits); + if (cadical_kitten->vars) + memset (cadical_kitten->vars, 0, vars); clear_cadical_kitten (cadical_kitten); } @@ -2441,14 +2448,6 @@ int cadical_kitten_compute_prime_implicant (cadical_kitten *cadical_kitten, void return res; } -static bool contains_blit (cadical_kitten *cadical_kitten, klause *c, const unsigned blit) { - for (all_literals_in_klause (lit, c)) { - if (lit == blit) - return true; - } - return false; -} - static bool prime_propagate_blit (cadical_kitten *cadical_kitten, const unsigned idx, const unsigned blit) { unsigned lit = 2 * idx; @@ -2518,7 +2517,9 @@ static bool prime_propagate_blit (cadical_kitten *cadical_kitten, const unsigned static int compute_prime_implicant_for (cadical_kitten *cadical_kitten, unsigned blit) { value *values = cadical_kitten->values; +#ifndef CADICAL_NDEBUG kar *vars = cadical_kitten->vars; +#endif unsigneds unassigned; INIT_STACK (unassigned); bool limit_hit = false; @@ -2532,7 +2533,7 @@ static int compute_prime_implicant_for (cadical_kitten *cadical_kitten, unsigned values[blit] = 0; values[blit ^ 1] = 0; PUSH_STACK (unassigned, tmp > 0 ? blit : blit ^ 1); - PUSH_STACK (cadical_kitten->prime[i], block); // will be negated! + PUSH_STACK (cadical_kitten->prime[ignoring], block); // will be negated! } else CADICAL_assert (false); for (all_stack (unsigned, lit, cadical_kitten->trail)) { @@ -2546,7 +2547,6 @@ static int compute_prime_implicant_for (cadical_kitten *cadical_kitten, unsigned continue; CADICAL_assert (values[lit]); // not true when flipping is involved const unsigned idx = lit / 2; - const unsigned ref = vars[idx].reason; CADICAL_assert (vars[idx].level); LOG ("non-prime candidate var %d", idx); if (prime_propagate_blit (cadical_kitten, idx, block)) { diff --git a/src/sat/cadical/cadical_lidruptracer.cpp b/src/sat/cadical/cadical_lidruptracer.cpp index db07e36e6..79adfea3a 100644 --- a/src/sat/cadical/cadical_lidruptracer.cpp +++ b/src/sat/cadical/cadical_lidruptracer.cpp @@ -462,7 +462,7 @@ void LidrupTracer::lidrup_solve_query () { /*------------------------------------------------------------------------*/ -void LidrupTracer::add_derived_clause (int64_t id, bool, +void LidrupTracer::add_derived_clause (int64_t id, bool, int, const vector &clause, const vector &chain) { if (file->closed ()) diff --git a/src/sat/cadical/cadical_limit.cpp b/src/sat/cadical/cadical_limit.cpp index 69417a473..96f979834 100644 --- a/src/sat/cadical/cadical_limit.cpp +++ b/src/sat/cadical/cadical_limit.cpp @@ -27,7 +27,7 @@ Last::Last () { memset (this, 0, sizeof *this); } Inc::Inc () { memset (this, 0, sizeof *this); - decisions = conflicts = -1; // unlimited + ticks = decisions = conflicts = -1; // unlimited } void Internal::limit_terminate (int l) { @@ -66,6 +66,18 @@ void Internal::limit_decisions (int l) { } } +void Internal::limit_ticks (int64_t l) { + if (l < 0 && inc.ticks < 0) { + LOG ("keeping unbounded ticks limit"); + } else if (l < 0) { + LOG ("reset ticks limit to be unbounded"); + inc.ticks = -1; + } else { + inc.ticks = l; + LOG ("new ticks limit of %" PRId64 " ticks", l); + } +} + void Internal::limit_preprocessing (int l) { if (l < 0) { LOG ("ignoring invalid preprocessing limit %d", l); @@ -101,6 +113,8 @@ bool Internal::is_valid_limit (const char *name) { return true; if (!strcmp (name, "localsearch")) return true; + if (!strcmp (name, "ticks")) + return true; return false; } @@ -116,6 +130,8 @@ bool Internal::limit (const char *name, int l) { limit_preprocessing (l); else if (!strcmp (name, "localsearch")) limit_local_search (l); + else if (!strcmp (name, "ticks")) + limit_ticks (l); else res = false; return res; @@ -128,6 +144,7 @@ void Internal::reset_limits () { limit_decisions (-1); limit_preprocessing (0); limit_local_search (0); + limit_ticks (-1); } } // namespace CaDiCaL diff --git a/src/sat/cadical/cadical_logging.cpp b/src/sat/cadical/cadical_logging.cpp index 589bf097e..9799d6f01 100644 --- a/src/sat/cadical/cadical_logging.cpp +++ b/src/sat/cadical/cadical_logging.cpp @@ -92,9 +92,8 @@ void Logger::log (Internal *internal, const Gate *g, const char *fmt, ...) { vprintf (fmt, ap); va_end (ap); if (g) { - printf ("%s%s%s gate[%" PRIu64 "] (arity: %ld) %s := %s", - g->degenerated_and_pos ? " deg+" : "", - g->degenerated_and_neg ? " deg-" : "", + printf ("%s%s gate[%" PRIu64 "] (arity: %zu) %s := %s", + special_gate_str (g->degenerated_gate).c_str (), g->garbage ? " garbage" : "", g->id, g->arity (), loglit (internal, g->lhs).c_str (), string_of_gate (g->tag).c_str ()); diff --git a/src/sat/cadical/cadical_lratchecker.cpp b/src/sat/cadical/cadical_lratchecker.cpp index 5f342a370..d2854c6d6 100644 --- a/src/sat/cadical/cadical_lratchecker.cpp +++ b/src/sat/cadical/cadical_lratchecker.cpp @@ -478,16 +478,20 @@ void LratChecker::add_original_clause (int64_t id, bool, STOP (checking); } -void LratChecker::add_derived_clause (int64_t id, bool, +void LratChecker::add_derived_clause (int64_t id, bool, int w, const vector &c, const vector &proof_chain) { START (checking); - LOG (c, "LRAT CHECKER addition of derived clause[%" PRId64 "]", id); + LOG (c, "LRAT CHECKER addition of derived %d clause[%" PRId64 "]", w, id); + CADICAL_assert (!w || c[0] == w); + if (w) + stats.rat++; stats.added++; stats.derived++; import_clause (c); last_id = id; CADICAL_assert (id == current_id + 1); + CADICAL_assert (!w || w == c[0]); current_id = id; if (size_clauses) { LratCheckerClause **p = find (id), *d = *p; @@ -541,7 +545,7 @@ void LratChecker::add_assumption_clause (int64_t id, const vector &c, stderr); fatal_message_end (); } - add_derived_clause (id, true, c, chain); + add_derived_clause (id, true, 0, c, chain); delete_clause (id, true, c); assumption_clauses.push_back (id); } diff --git a/src/sat/cadical/cadical_lrattracer.cpp b/src/sat/cadical/cadical_lrattracer.cpp index 4da728e18..165cfacfc 100644 --- a/src/sat/cadical/cadical_lrattracer.cpp +++ b/src/sat/cadical/cadical_lrattracer.cpp @@ -127,7 +127,7 @@ void LratTracer::lrat_delete_clause (int64_t id) { /*------------------------------------------------------------------------*/ -void LratTracer::add_derived_clause (int64_t id, bool, +void LratTracer::add_derived_clause (int64_t id, bool, int, const vector &clause, const vector &chain) { if (file->closed ()) diff --git a/src/sat/cadical/cadical_lucky.cpp b/src/sat/cadical/cadical_lucky.cpp index d67782db4..fee3894ee 100644 --- a/src/sat/cadical/cadical_lucky.cpp +++ b/src/sat/cadical/cadical_lucky.cpp @@ -25,7 +25,7 @@ namespace CaDiCaL { int Internal::unlucky (int res) { if (level > 0) - backtrack (); + backtrack_without_updating_phases (); if (conflict) conflict = 0; return res; @@ -34,7 +34,9 @@ int Internal::unlucky (int res) { int Internal::trivially_false_satisfiable () { LOG ("checking that all clauses contain a negative literal"); CADICAL_assert (!level); - CADICAL_assert (assumptions.empty ()); + int res = lucky_decide_assumptions (); + if (res) + return res; for (const auto &c : clauses) { if (terminated_asynchronously (100)) return unlucky (-1); @@ -81,7 +83,9 @@ int Internal::trivially_false_satisfiable () { int Internal::trivially_true_satisfiable () { LOG ("checking that all clauses contain a positive literal"); CADICAL_assert (!level); - CADICAL_assert (assumptions.empty ()); + int res = lucky_decide_assumptions (); + if (res) + return res; for (const auto &c : clauses) { if (terminated_asynchronously (100)) return unlucky (-1); @@ -132,7 +136,8 @@ inline bool Internal::lucky_propagate_discrepency (int dec) { if (no_conflict) return false; if (level > 1) { - backtrack (level - 1); + conflict = nullptr; + backtrack_without_updating_phases (level - 1); search_assume_decision (-dec); no_conflict = propagate (); if (no_conflict) @@ -155,7 +160,9 @@ int Internal::forward_false_satisfiable () { LOG ("checking increasing variable index false assignment"); CADICAL_assert (!unsat); CADICAL_assert (!level); - CADICAL_assert (assumptions.empty ()); + int res = lucky_decide_assumptions (); + if (res) + return res; for (auto idx : vars) { START: if (terminated_asynchronously (100)) @@ -180,7 +187,9 @@ int Internal::forward_true_satisfiable () { LOG ("checking increasing variable index true assignment"); CADICAL_assert (!unsat); CADICAL_assert (!level); - CADICAL_assert (assumptions.empty ()); + int res = lucky_decide_assumptions (); + if (res) + return res; for (auto idx : vars) { START: if (terminated_asynchronously (10)) @@ -207,8 +216,11 @@ int Internal::backward_false_satisfiable () { LOG ("checking decreasing variable index false assignment"); CADICAL_assert (!unsat); CADICAL_assert (!level); - CADICAL_assert (assumptions.empty ()); - for (int idx = max_var; idx > 0; idx--) { + int res = lucky_decide_assumptions (); + if (res) + return res; + for (auto it = vars.rbegin (); it != vars.rend (); ++it) { + int idx = *it; START: if (terminated_asynchronously (10)) return unlucky (-1); @@ -232,8 +244,11 @@ int Internal::backward_true_satisfiable () { LOG ("checking decreasing variable index true assignment"); CADICAL_assert (!unsat); CADICAL_assert (!level); - CADICAL_assert (assumptions.empty ()); - for (int idx = max_var; idx > 0; idx--) { + int res = lucky_decide_assumptions (); + if (res) + return res; + for (auto it = vars.rbegin (); it != vars.rend (); ++it) { + int idx = *it; START: if (terminated_asynchronously (10)) return unlucky (-1); @@ -264,7 +279,9 @@ int Internal::backward_true_satisfiable () { int Internal::positive_horn_satisfiable () { LOG ("checking that all clauses are positive horn satisfiable"); CADICAL_assert (!level); - CADICAL_assert (assumptions.empty ()); + int res = lucky_decide_assumptions (); + if (res) + return res; for (const auto &c : clauses) { if (terminated_asynchronously (10)) return unlucky (-1); @@ -320,10 +337,50 @@ int Internal::positive_horn_satisfiable () { return 10; } -int Internal::negative_horn_satisfiable () { - LOG ("checking that all clauses are negative horn satisfiable"); +int Internal::lucky_decide_assumptions () { CADICAL_assert (!level); - CADICAL_assert (assumptions.empty ()); + CADICAL_assert (!constraint.size ()); + int res = 0; + while ((size_t) level < assumptions.size ()) { + res = decide (); + if (res == 20) { + marked_failed = false; + return 20; + } + if (!propagate ()) { + break; + } + } + + if (conflict) { + // analyze and learn from the conflict. + LOG (conflict, "setting assumption lead to conflict"); + analyze_wrapper (); + backtrack (0); + CADICAL_assert (!conflict); + int res = 0; + while (!res) { + CADICAL_assert ((size_t) level <= assumptions.size ()); + if (unsat) + res = 20; + else if (!propagate ()) { + analyze_wrapper (); + } else { + res = decide_wrapper (); + } + } + CADICAL_assert (res == 20); + return 20; + } + return 0; +} + +int Internal::negative_horn_satisfiable () { + CADICAL_assert (!level); + LOG ("checking that all clauses are negative horn satisfiable"); + int res = lucky_decide_assumptions (); + if (res) + return res; for (const auto &c : clauses) { if (terminated_asynchronously (10)) return unlucky (-1); @@ -350,7 +407,7 @@ int Internal::negative_horn_satisfiable () { continue; if (!negative_literal) { if (level > 0) - backtrack (); + backtrack_without_updating_phases (); LOG (c, "no negative unassigned literal in"); return unlucky (0); } @@ -389,15 +446,21 @@ int Internal::lucky_phases () { if (!opts.lucky) return 0; - // TODO: Some of the lucky assignments can also be found if there are - // assumptions, but this is not completely implemented nor tested yet. - // Nothing done for constraint either. - // External propagator assumes a CDCL loop, so lucky is not tried here. - if (!assumptions.empty () || !constraint.empty () || external_prop) + if (!opts.luckyassumptions && !assumptions.empty ()) return 0; + // TODO: Some of the lucky assignments can also be found if there are + // constraint. + // External propagator assumes a CDCL loop, so lucky is not tried here. + if (!constraint.empty () || external_prop) + return 0; + if (!propagate ()) { + learn_empty_clause (); + return 20; + } START (search); START (lucky); + LOG ("starting lucky"); CADICAL_assert (!searching_lucky_phases); searching_lucky_phases = true; stats.lucky.tried++; @@ -405,18 +468,18 @@ int Internal::lucky_phases () { int res = trivially_false_satisfiable (); if (!res) res = trivially_true_satisfiable (); - if (!res) - res = forward_true_satisfiable (); if (!res) res = forward_false_satisfiable (); + if (!res) + res = forward_true_satisfiable (); if (!res) res = backward_false_satisfiable (); if (!res) res = backward_true_satisfiable (); - if (!res) - res = positive_horn_satisfiable (); if (!res) res = negative_horn_satisfiable (); + if (!res) + res = positive_horn_satisfiable (); if (res < 0) CADICAL_assert (termination_forced), res = 0; if (res == 10) @@ -424,11 +487,20 @@ int Internal::lucky_phases () { report ('l', !res); CADICAL_assert (searching_lucky_phases); + CADICAL_assert (res || !level); + if (res != 20) { + if (!propagate ()) { + LOG ("propagating units after elimination results in empty clause"); + learn_empty_clause (); + } + } + const int64_t units = active_before - stats.active; if (!res && units) - LOG ("lucky %zd units", units); + LOG ("lucky %" PRId64 " units", units); searching_lucky_phases = false; + STOP (lucky); STOP (search); diff --git a/src/sat/cadical/cadical_message.cpp b/src/sat/cadical/cadical_message.cpp index 42d8c0e48..fbc44bee3 100644 --- a/src/sat/cadical/cadical_message.cpp +++ b/src/sat/cadical/cadical_message.cpp @@ -25,6 +25,12 @@ void Internal::vmessage (const char *fmt, va_list &ap) { } void Internal::message (const char *fmt, ...) { +#ifdef LOGGING + if (!opts.log) +#endif + if (opts.quiet) + return; + va_list ap; va_start (ap, fmt); vmessage (fmt, ap); diff --git a/src/sat/cadical/cadical_parse.cpp b/src/sat/cadical/cadical_parse.cpp index 0668aca7d..e2901fdf5 100644 --- a/src/sat/cadical/cadical_parse.cpp +++ b/src/sat/cadical/cadical_parse.cpp @@ -52,6 +52,18 @@ inline const char *Parser::parse_positive_int (int &ch, int &res, return 0; } +inline const char *Parser::parse_positive_uint64_t (int &ch, uint64_t &res, + const char *name) { + CADICAL_assert (isdigit (ch)); + res = ch - '0'; + while (isdigit (ch = parse_char ())) { + int digit = ch - '0'; + if (UINT64_MAX / 10 < res || UINT64_MAX - digit < 10 * res) + PER ("too large '%s' in header", name); + res = 10 * res + digit; + } + return 0; +} static const char *cube_token = "unexpected 'a' in CNF"; inline const char *Parser::parse_lit (int &ch, int &lit, int &vars, @@ -99,7 +111,8 @@ const char *Parser::parse_dimacs_non_profiled (int &vars, int strict) { #endif bool found_inccnf_header = false; - int ch, clauses = 0; + int ch = 0; + uint64_t clauses = 0; vars = 0; // First read comments before header with possibly embedded options. @@ -162,11 +175,12 @@ const char *Parser::parse_dimacs_non_profiled (int &vars, int strict) { PER ("expected ' ' after 'p cnf %d'", vars); if (!isdigit (ch = parse_char ())) PER ("expected digit after 'p cnf %d '", vars); - err = parse_positive_int (ch, clauses, ""); + err = parse_positive_uint64_t (ch, clauses, ""); if (err) return err; if (ch != '\n') - PER ("expected new-line after 'p cnf %d %d'", vars, clauses); + PER ("expected new-line after 'p cnf %d %" PRIu64 "'", vars, + clauses); } else { if (parse_char () != 'n') PER ("expected 'n' after 'p c'"); @@ -190,21 +204,22 @@ const char *Parser::parse_dimacs_non_profiled (int &vars, int strict) { while (isspace (ch)); if (!isdigit (ch)) PER ("expected digit after 'p cnf %d '", vars); - err = parse_positive_int (ch, clauses, ""); + err = parse_positive_uint64_t (ch, clauses, ""); if (err) return err; while (ch != '\n') { if (ch != '\r' && !isspace (ch)) - PER ("expected new-line after 'p cnf %d %d'", vars, clauses); + PER ("expected new-line after 'p cnf %d %" PRIu64 "'", vars, + clauses); ch = parse_char (); } } - MSG ("found %s'p cnf %d %d'%s header", tout.green_code (), vars, - clauses, tout.normal_code ()); + MSG ("found %s'p cnf %d %" PRIu64 "'%s header", tout.green_code (), + vars, clauses, tout.normal_code ()); if (strict != FORCED) - solver->reserve (vars); + solver->resize (vars); internal->reserve_ids (clauses); } else if (!parse_inccnf_too) PER ("expected 'c' after 'p '"); @@ -237,7 +252,8 @@ const char *Parser::parse_dimacs_non_profiled (int &vars, int strict) { // Now read body of DIMACS part. // - int lit = 0, parsed = 0; + int lit = 0; + uint64_t parsed = 0; while ((ch = parse_char ()) != EOF) { if (ch == ' ' || ch == '\n' || ch == '\t' || ch == '\r') continue; @@ -272,8 +288,8 @@ const char *Parser::parse_dimacs_non_profiled (int &vars, int strict) { #ifndef CADICAL_QUIET double end = internal->time (); - MSG ("parsed %d clauses in %.2f seconds %s time", parsed, end - start, - internal->opts.realtime ? "real" : "process"); + MSG ("parsed %" PRIu64 " clauses in %.2f seconds %s time", parsed, + end - start, internal->opts.realtime ? "real" : "process"); #endif #ifndef CADICAL_QUIET diff --git a/src/sat/cadical/cadical_phases.cpp b/src/sat/cadical/cadical_phases.cpp index f7e4eaffd..ffb9398fd 100644 --- a/src/sat/cadical/cadical_phases.cpp +++ b/src/sat/cadical/cadical_phases.cpp @@ -8,8 +8,20 @@ namespace CaDiCaL { void Internal::copy_phases (vector &dst) { START (copy); - for (auto i : vars) - dst[i] = phases.saved[i]; + for (auto i : vars) { + const signed char tmp = phases.saved[i]; + if (tmp) + dst[i] = tmp; + } + STOP (copy); +} + +void Internal::save_assigned_phases (vector &dst) { + START (copy); + for (auto i : vars) { + if (vals[i]) + dst[i] = vals[i]; + } STOP (copy); } diff --git a/src/sat/cadical/cadical_probe.cpp b/src/sat/cadical/cadical_probe.cpp index 19ff0961f..6414dffaa 100644 --- a/src/sat/cadical/cadical_probe.cpp +++ b/src/sat/cadical/cadical_probe.cpp @@ -426,8 +426,8 @@ bool Internal::probe_propagate () { const int lit = -trail[propagated++]; LOG ("probe propagating %d over large clauses", -lit); Watches &ws = watches (lit); - ticks += 1 + cache_lines (ws.size (), - sizeof (sizeof (const_watch_iterator *))); + ticks += + 1 + cache_lines (ws.size (), sizeof (const_watch_iterator *)); size_t i = 0, j = 0; while (i != ws.size ()) { const Watch w = ws[j++] = ws[i++]; @@ -838,7 +838,7 @@ bool Internal::probe () { LOG ("probing %d", probe); probe_assign_decision (probe); if (probe_propagate ()) - backtrack (); + backtrack_without_updating_phases (); else failed_literal (probe); clean_probehbr_lrat (); @@ -944,13 +944,16 @@ void CaDiCaL::Internal::inprobe (bool update_limits) { if (probe ()) decompose (); - if (extract_gates ()) + if (extract_gates (preprocessing)) decompose (); + binary_clauses_backbone (); + mark_duplicated_binary_clauses_as_garbage (); if (sweep ()) // full occurrence list decompose (); // ... and (ELS) afterwards. (void) vivify (); // resets watches transred (); // builds big. - factor (); // resets watches, partial occurrence list + binary_clauses_backbone (); + factor (); // resets watches, partial occurrence list } if (external_prop) { diff --git a/src/sat/cadical/cadical_proof.cpp b/src/sat/cadical/cadical_proof.cpp index 13c9a1a5d..4cb548dc8 100644 --- a/src/sat/cadical/cadical_proof.cpp +++ b/src/sat/cadical/cadical_proof.cpp @@ -199,7 +199,9 @@ void Internal::flush_trace (bool print) { /*------------------------------------------------------------------------*/ -Proof::Proof (Internal *s) : internal (s) { LOG ("PROOF new"); } +Proof::Proof (Internal *s) : internal (s), witness (0) { + LOG ("PROOF new"); +} Proof::~Proof () { LOG ("PROOF delete"); } @@ -292,6 +294,20 @@ void Proof::add_derived_clause (Clause *c, const vector &chain) { add_derived_clause (); } +void Proof::add_derived_rat_clause (Clause *c, int w, + const vector &chain) { + LOG (c, "PROOF adding to proof derived witness %d", w); + CADICAL_assert (clause.empty ()); + CADICAL_assert (proof_chain.empty ()); + add_literals (c); + for (const auto &cid : chain) + proof_chain.push_back (cid); + clause_id = c->id; + redundant = c->redundant; + witness = w; + add_derived_clause (); +} + void Proof::add_derived_clause (int64_t id, bool r, const vector &c, const vector &chain) { LOG (c, "PROOF adding derived clause"); @@ -306,6 +322,22 @@ void Proof::add_derived_clause (int64_t id, bool r, const vector &c, add_derived_clause (); } +void Proof::add_derived_rat_clause (int64_t id, bool r, int l, + const vector &c, + const vector &chain) { + LOG (c, "PROOF adding derived witness %d clause", l); + CADICAL_assert (clause.empty ()); + CADICAL_assert (proof_chain.empty ()); + for (const auto &lit : c) + add_literal (lit); + for (const auto &cid : chain) + proof_chain.push_back (cid); + clause_id = id; + redundant = r; + witness = l; + add_derived_clause (); +} + void Proof::add_assumption_clause (int64_t id, const vector &c, const vector &chain) { // literals of c are already external @@ -530,11 +562,13 @@ void Proof::add_derived_clause () { redundant); CADICAL_assert (clause_id); for (auto &tracer : tracers) { - tracer->add_derived_clause (clause_id, redundant, clause, proof_chain); + tracer->add_derived_clause (clause_id, redundant, witness, clause, + proof_chain); } proof_chain.clear (); clause.clear (); clause_id = 0; + witness = 0; } void Proof::delete_clause () { diff --git a/src/sat/cadical/cadical_propagate.cpp b/src/sat/cadical/cadical_propagate.cpp index a4f21ee6a..4b3e905d0 100644 --- a/src/sat/cadical/cadical_propagate.cpp +++ b/src/sat/cadical/cadical_propagate.cpp @@ -24,7 +24,7 @@ namespace CaDiCaL { // relevant in conflict analysis or in root-level fixing steps. static Clause decision_reason_clause; -static Clause *decision_reason = &decision_reason_clause; +Clause *Internal::decision_reason = &decision_reason_clause; // If chronological backtracking is used the actual assignment level might // be lower than the current decision level. In this case the assignment @@ -89,7 +89,7 @@ void Internal::build_chain_for_units (int lit, Clause *reason, void Internal::build_chain_for_empty () { if (!lrat || !lrat_chain.empty ()) return; - CADICAL_assert (!level); + CADICAL_assert (!level || in_mode (BACKBONE)); CADICAL_assert (lrat_chain.empty ()); CADICAL_assert (conflict); LOG (conflict, "lrat for global empty clause with conflict"); @@ -320,6 +320,7 @@ bool Internal::propagate () { } literal_iterator lits = w.clause->begin (); + CADICAL_assert (lits[0] == lit || lits[1] == lit); // Simplify code by forcing 'lit' to be the second literal in the // clause. This goes back to MiniSAT. We use a branch-less version @@ -351,7 +352,8 @@ bool Internal::propagate () { literal_iterator k = middle; // Find replacement watch 'r' at position 'k' with value 'v'. - + CADICAL_assert (lits + 2 <= k); + LOG (w.clause, "search starting at %d", w.clause->pos); int r = 0; signed char v = -1; @@ -492,6 +494,10 @@ bool Internal::propagate () { } } + if (conflict && randomized_deciding) { + if (!--randomized_deciding) + VERBOSE (3, "last random decision conflict"); + } STOP (propagate); return !conflict; diff --git a/src/sat/cadical/cadical_reduce.cpp b/src/sat/cadical/cadical_reduce.cpp index cc6f35228..5acd8a113 100644 --- a/src/sat/cadical/cadical_reduce.cpp +++ b/src/sat/cadical/cadical_reduce.cpp @@ -48,9 +48,9 @@ void Internal::mark_clauses_to_be_flushed () { const unsigned used = c->used; if (used) c->used--; - if (c->glue < tier1limit && used) + if (c->glue <= tier1limit && used) continue; - if (c->glue < tier2limit && used >= max_used - 1) + if (c->glue <= tier2limit && used >= max_used - 1) continue; mark_garbage (c); // flush unused clauses if (c->hyper) diff --git a/src/sat/cadical/cadical_rephase.cpp b/src/sat/cadical/cadical_rephase.cpp index 3b2e62526..12b1f515d 100644 --- a/src/sat/cadical/cadical_rephase.cpp +++ b/src/sat/cadical/cadical_rephase.cpp @@ -27,6 +27,12 @@ bool Internal::rephasing () { return false; if (opts.forcephase) return false; + if (opts.rephase == 2) { + if (stable) + return stats.stabconflicts > lim.rephase; + else + return false; + } return stats.conflicts > lim.rephase; } @@ -99,7 +105,10 @@ char Internal::rephase_walk () { stats.rephased.walk++; PHASE ("rephase", stats.rephased.total, "starting local search to improve current phase"); - walk (); + if (opts.walkfullocc) + walk_full_occs (); + else + walk (); return 'W'; } @@ -108,9 +117,12 @@ char Internal::rephase_walk () { void Internal::rephase () { stats.rephased.total++; + last.stabilize.rephased++; + CADICAL_assert (last.stabilize.rephased <= stats.rephased.total); PHASE ("rephase", stats.rephased.total, "reached rephase limit %" PRId64 " after %" PRId64 " conflicts", - lim.rephase, stats.conflicts); + lim.rephase, + opts.rephase == 2 ? stats.stabconflicts : stats.conflicts); // Report current 'target' and 'best' and then set 'rephased' below, which // will trigger reporting the new 'target' and 'best' after updating it in @@ -119,8 +131,6 @@ void Internal::rephase () { report ('~', 1); backtrack (); - clear_phases (phases.target); - target_assigned = 0; size_t count = lim.rephased[stable]++; bool single; @@ -208,6 +218,52 @@ void Internal::rephase () { type = 0; break; } + } else if (opts.rephase == 2 && opts.walk) { + // (inverted,best,walk, + // flipping,best,walk, + // random,best,walk, + // original,best,walk)^\omega + switch (count % 12) { + case 0: + type = rephase_inverted (); + break; + case 1: + type = rephase_best (); + break; + case 2: + type = rephase_walk (); + break; + case 3: + type = rephase_flipping (); + break; + case 4: + type = rephase_best (); + break; + case 5: + type = rephase_walk (); + break; + case 6: + type = rephase_random (); + break; + case 7: + type = rephase_best (); + break; + case 8: + type = rephase_walk (); + break; + case 9: + type = rephase_original (); + break; + case 10: + type = rephase_best (); + break; + case 11: + type = rephase_walk (); + break; + default: + type = 0; + break; + } } else if (stable && !opts.walk) { // original,inverted,(best,original,best,inverted)^\omega if (!count) @@ -288,7 +344,7 @@ void Internal::rephase () { CADICAL_assert (!stable && opts.walk && opts.walknonstable); // flipping,(random,best,walk,flipping,best,walk)^\omega if (!count) - type = rephase_flipping (); + type = rephase_original (); else switch ((count - 1) % 6) { case 0: @@ -316,8 +372,14 @@ void Internal::rephase () { } CADICAL_assert (type); + // clear after walk such that random walk can still access the target + // by using the saved phases + copy_phases (phases.target); + target_assigned = 0; + int64_t delta = opts.rephaseint * (stats.rephased.total + 1); - lim.rephase = stats.conflicts + delta; + lim.rephase = + (opts.rephase == 2 ? stats.stabconflicts : stats.conflicts) + delta; PHASE ("rephase", stats.rephased.total, "new rephase limit %" PRId64 " after %" PRId64 " conflicts", @@ -331,6 +393,10 @@ void Internal::rephase () { last.rephase.conflicts = stats.conflicts; rephased = type; + if (!marked_failed || unsat_constraint) { + CADICAL_assert (opts.warmup); + return; + } if (stable) shuffle_scores (); else diff --git a/src/sat/cadical/cadical_report.cpp b/src/sat/cadical/cadical_report.cpp index 18271d17e..20e0518f6 100644 --- a/src/sat/cadical/cadical_report.cpp +++ b/src/sat/cadical/cadical_report.cpp @@ -47,6 +47,7 @@ r start of solving after restoring clauses 1 end of solving returns satisfiable 0 end of solving returns unsatisfiable ? end of solving due to interrupt +k binary backbone extraction ('kernel') l lucky phase solving p failed literal probing round (lower case 'p') . before reducing redundant clauses @@ -138,8 +139,13 @@ Report::Report (const char *h, int precision, int min, double value) REPORT ("rate", 0, 2, averages.current.decisions) \ REPORT ("conflicts", 0, 4, stats.conflicts) \ REPORT ("redundant", 0, 4, stats.current.redundant) \ - REPORT ("trail", -1, 2, TRAIL) \ + REPORT ("size/glue", 1, 2, \ + relative (averages.current.size, averages.current.glue.slow)) \ + REPORT ("size", 0, 1, averages.current.size) \ REPORT ("glue", 0, 1, averages.current.glue.slow) \ + REPORT ("tier1", 0, 1, tier1[stable]) \ + REPORT ("tier2", 0, 1, tier2[stable]) \ + REPORT ("trail", -1, 2, TRAIL) \ REPORT ("irredundant", 0, 4, stats.current.irredundant) \ REPORT ("variables", 0, 3, active ()) \ REPORT ("remaining", -1, 2, REMAINING) @@ -273,12 +279,23 @@ void Internal::report (char type, int verbose) { tout.bold (); tout.underline (); break; + case '(': + case ')': + tout.bold (); + tout.yellow (); + break; + case '{': + case '}': + tout.normal (); + break; default: break; } fputc (type, stdout); if (stable || type == ']') tout.magenta (); + else if (preprocessing || type == ')') + tout.bold (), tout.yellow (); else if (type != 'L' && type != 'P') tout.normal (); for (int i = 0; i < n; i++) { diff --git a/src/sat/cadical/cadical_restart.cpp b/src/sat/cadical/cadical_restart.cpp index d309d5f10..09eae624b 100644 --- a/src/sat/cadical/cadical_restart.cpp +++ b/src/sat/cadical/cadical_restart.cpp @@ -35,12 +35,19 @@ bool Internal::stabilizing () { STOP (stable); else STOP (unstable); - //const int64_t delta_conflicts = - // stats.conflicts - last.stabilize.conflicts; + + CADICAL_assert (last.stabilize.ticks >= 0); + CADICAL_assert (last.stabilize.conflicts >= 0 && + last.stabilize.conflicts <= stats.conflicts); + CADICAL_assert (last.stabilize.ticks <= stats.ticks.search[stable]); const int64_t delta_ticks = stats.ticks.search[stable] - last.stabilize.ticks; - //const char *current_mode = stable ? "stable" : "unstable"; - //const char *next_mode = stable ? "unstable" : "stable"; +#ifndef CADICAL_QUIET + const int64_t delta_conflicts = + stats.conflicts - last.stabilize.conflicts; + const char *current_mode = stable ? "stable" : "unstable"; + const char *next_mode = stable ? "unstable" : "stable"; +#endif PHASE ("stabilizing", stats.stabphases, "reached %s stabilization limit %" PRId64 " after %" PRId64 " conflicts and %" PRId64 " ticks at %" PRId64 @@ -53,24 +60,26 @@ bool Internal::stabilizing () { // ticks inc.stabilize = 1; - stable = !stable; // Switch!!!!! - int64_t next_delta_ticks = inc.stabilize; int64_t stabphases = stats.stabphases + 1; next_delta_ticks *= stabphases * stabphases; - lim.stabilize = stats.ticks.search[stable] + next_delta_ticks; - if (lim.stabilize <= stats.ticks.search[stable]) - lim.stabilize = stats.ticks.search[stable] + 1; + const bool next_stable = !stable; + lim.stabilize = stats.ticks.search[next_stable] + next_delta_ticks; + last.stabilize.ticks = stats.ticks.search[next_stable]; + if (lim.stabilize <= stats.ticks.search[next_stable]) + lim.stabilize = stats.ticks.search[next_stable] + 1; + PHASE ("stabilizing", stats.stabphases, + "next %s stabilization limit %" PRId64 + " at ticks interval %" PRId64, + next_mode, lim.stabilize, next_delta_ticks); + + stable = !stable; // Switch!!!!! if (stable) stats.stabphases++; swap_averages (); - PHASE ("stabilizing", stats.stabphases, - "next %s stabilization limit %" PRId64 - " at ticks interval %" PRId64, - next_mode, lim.stabilize, next_delta_ticks); report (stable ? '[' : '{'); if (stable) START (stable); @@ -91,14 +100,24 @@ bool Internal::restarting () { return false; if ((size_t) level < assumptions.size () + 2) return false; - if (stabilizing ()) + if (stabilizing () && opts.reluctant) return reluctant; if (stats.conflicts <= lim.restart) return false; double f = averages.current.glue.fast; - double margin = (100.0 + opts.restartmargin) / 100.0; - double s = averages.current.glue.slow, l = margin * s; - LOG ("EMA glue slow %.2f fast %.2f limit %.2f", s, f, l); + int p = stable ? opts.restartmarginstable : opts.restartmarginfocused; + double m = (100.0 + p) / 100.0; + double s = averages.current.glue.slow; + double l = m * s; + +#ifndef CADICAL_QUIET + char c = l > f ? '>' : l < f ? '<' : '='; + VERBOSE (3, + "restart glue limit " + "%g = %.2f * %g (slow glue) %c %g (fast glue)", + l, m, s, c, f); +#endif + return l <= f; } diff --git a/src/sat/cadical/cadical_solver.cpp b/src/sat/cadical/cadical_solver.cpp index 84d350cf2..7dd03e8be 100644 --- a/src/sat/cadical/cadical_solver.cpp +++ b/src/sat/cadical/cadical_solver.cpp @@ -1,5 +1,6 @@ #include "global.h" +#include "cadical.hpp" #include "internal.hpp" ABC_NAMESPACE_IMPL_START @@ -60,7 +61,7 @@ void Solver::transition_to_steady_state () { external->reset_assumptions (); external->reset_concluded (); external->reset_constraint (); - } else if (state() == INCONCLUSIVE) { + } else if (state () == INCONCLUSIVE) { external->reset_assumptions (); external->reset_concluded (); external->reset_constraint (); @@ -92,6 +93,18 @@ static void log_api_call (Internal *internal, const char *name, int arg, name, arg, tout.log_code (), suffix); } +static void log_api_call (Internal *internal, const char *name, int arg, + int b, const char *suffix) { + Logger::log (internal, "API call %s'%s (%d, %d)'%s %s", tout.api_code (), + name, arg, b, tout.log_code (), suffix); +} + +static void log_api_call (Internal *internal, const char *name, int arg, + int b, int c) { + Logger::log (internal, "API call %s'%s (%d, %d)'%s %d", tout.api_code (), + name, arg, b, tout.log_code (), c); +} + static void log_api_call (Internal *internal, const char *name, const char *arg, const char *suffix) { Logger::log (internal, "API call %s'%s (\"%s\")'%s %s", tout.api_code (), @@ -119,6 +132,12 @@ static void log_api_call_begin (Internal *internal, const char *name, log_api_call (internal, name, arg, "started"); } +static void log_api_call_begin (Internal *internal, const char *name, + int arg, int b) { + Logger::log_empty_line (internal); + log_api_call (internal, name, arg, b, "started"); +} + static void log_api_call_begin (Internal *internal, const char *name, const char *arg) { Logger::log_empty_line (internal); @@ -196,6 +215,11 @@ static void log_api_call_returns (Internal *internal, const char *name, res ? "returns 'true'" : "returns 'false'"); } +static void log_api_call_returns (Internal *internal, const char *name, + int lit, int b, int res) { + log_api_call (internal, name, lit, b, res); +} + static void log_api_call_returns (Internal *internal, const char *name, const char *arg, const char *res) { Logger::log (internal, "API call %s'%s (\"%s\")'%s returns '%s'", @@ -281,6 +305,13 @@ void Solver::trace_api_call (const char *s0, int i1) const { fflush (trace_api_file); } +void Solver::trace_api_call (const char *s0, int i1, int b) const { + CADICAL_assert (trace_api_file); + LOG ("TRACE %s %d %d", s0, i1, b); + fprintf (trace_api_file, "%s %d %d\n", s0, i1, b); + fflush (trace_api_file); +} + void Solver::trace_api_call (const char *s0, const char *s1) const { CADICAL_assert (trace_api_file); LOG ("TRACE %s %s", s0, s1); @@ -437,23 +468,34 @@ int Solver::vars () { return res; } -void Solver::reserve (int min_max_var) { - TRACE ("reserve", min_max_var); +void Solver::resize (int min_max_var) { + TRACE ("resize", min_max_var); REQUIRE_VALID_STATE (); transition_to_steady_state (); external->reset_extended (); external->init (min_max_var); - LOG_API_CALL_END ("reserve", min_max_var); + LOG_API_CALL_END ("resize", min_max_var); } -int Solver::reserve_difference (int number_of_vars) { - TRACE ("reserve_difference", number_of_vars); +int Solver::declare_more_variables (int number_of_vars) { + TRACE ("declare_more_variables", number_of_vars); REQUIRE_VALID_STATE (); transition_to_steady_state (); external->reset_extended (); int new_max_var = external->max_var + number_of_vars; external->init (new_max_var); - LOG_API_CALL_END ("reserve_difference", number_of_vars); + LOG_API_CALL_END ("declare_more_variables", number_of_vars); + return new_max_var; +} + +int Solver::declare_one_more_variable () { + TRACE ("declare_one_more_variable"); + REQUIRE_VALID_STATE (); + transition_to_steady_state (); + external->reset_extended (); + int new_max_var = external->max_var + 1; + external->init (new_max_var); + LOG_API_CALL_END ("declare_one_more_variable"); return new_max_var; } @@ -673,8 +715,9 @@ void Solver::assume (int lit) { int Solver::lookahead () { TRACE ("lookahead"); REQUIRE_VALID_OR_SOLVING_STATE (); + transition_to_steady_state (); int lit = external->lookahead (); - TRACE ("lookahead"); + LOG_API_CALL_END ("lookahead", lit); return lit; } @@ -730,8 +773,9 @@ int Solver::propagate () { void Solver::implied (std::vector &entrailed) { TRACE ("implied"); REQUIRE_VALID_STATE (); - REQUIRE (state () == INCONCLUSIVE, - "can only get implied literals only in unknown state"); + REQUIRE ( + state () == INCONCLUSIVE || state () == SATISFIED, + "can only get implied literals only in unknown or satisfied state"); external->conclude_unknown (); external->implied (entrailed); if (tracing_nb_lidrup_env_var_method) @@ -775,11 +819,13 @@ int Solver::call_external_solve_and_check_results (bool preprocess_only) { FATAL ("copying assumption checker failed"); } #endif +#if 0 // was necessary when INCONCLUSIVE state did not exist if (!res) { external->reset_assumptions (); external->reset_constraint (); external->reset_concluded (); } +#endif return res; } @@ -799,23 +845,33 @@ int Solver::simplify (int rounds) { REQUIRE (rounds >= 0, "negative number of simplification rounds '%d'", rounds); internal->limit ("preprocessing", rounds); + const int lucky = internal->opts.lucky; + internal->opts.lucky = 0; const int res = call_external_solve_and_check_results (true); + internal->opts.lucky = lucky; LOG_API_CALL_RETURNS ("simplify", rounds, res); return res; } /*------------------------------------------------------------------------*/ -int Solver::val (int lit) { - TRACE ("val", lit); +int Solver::val ( + int lit, bool use_default_value_for_declared_but_not_used_variable) { + LOG_API_CALL_BEGIN ( + "val", lit, + (int) use_default_value_for_declared_but_not_used_variable); REQUIRE_VALID_STATE (); REQUIRE_VALID_LIT (lit); REQUIRE (state () == SATISFIED, "can only get value in satisfied state"); + if (!use_default_value_for_declared_but_not_used_variable) + REQUIRE (lit < external->max_var, "lit of undeclare variable"); if (!external->extended) external->extend (); external->conclude_sat (); int res = external->ival (lit); - LOG_API_CALL_RETURNS ("val", lit, res); + LOG_API_CALL_RETURNS ( + "val", lit, use_default_value_for_declared_but_not_used_variable, + res); CADICAL_assert (state () == SATISFIED); CADICAL_assert (res == lit || res == -lit); return res; @@ -1247,9 +1303,10 @@ bool Solver::disconnect_proof_tracer (FileTracer *tracer) { void Solver::conclude () { TRACE ("conclude"); REQUIRE_VALID_STATE (); - REQUIRE (state () == UNSATISFIED || state () == SATISFIED || - state () == INCONCLUSIVE, - "can only conclude in satisfied, unsatisfied or inconclusive state"); + REQUIRE ( + state () == UNSATISFIED || state () == SATISFIED || + state () == INCONCLUSIVE, + "can only conclude in satisfied, unsatisfied or inconclusive state"); if (state () == UNSATISFIED) internal->conclude_unsat (); else if (state () == SATISFIED) @@ -1759,6 +1816,33 @@ void Solver::error (const char *fmt, ...) { va_end (ap); } +int64_t Solver::get_statistic_value (const char *opt) const { + REQUIRE_INITIALIZED (); + if (!strcmp (opt, "conflicts")) + return internal->stats.conflicts; + if (!strcmp (opt, "decisions")) + return internal->stats.decisions; + if (!strcmp (opt, "ticks")) + return internal->stats.ticks.search[0] + + internal->stats.ticks.search[1]; + if (!strcmp (opt, "propagations")) + return internal->stats.propagations.search; + if (!strcmp (opt, "clauses")) + return internal->stats.current.total; + if (!strcmp (opt, "redundant")) + return internal->stats.current.redundant; + if (!strcmp (opt, "irredundant")) + return internal->stats.current.irredundant; + if (!strcmp (opt, "fixed")) + return internal->stats.all.fixed; + if (!strcmp (opt, "eliminated")) + return internal->stats.all.eliminated + + internal->stats.all.fasteliminated; + if (!strcmp (opt, "subsitutued")) + return internal->stats.all.substituted; + return -1; +} + /*------------------------------------------------------------------------*/ int Solver::clauses () { diff --git a/src/sat/cadical/cadical_stats.cpp b/src/sat/cadical/cadical_stats.cpp index 8b07745d6..b6621f15d 100644 --- a/src/sat/cadical/cadical_stats.cpp +++ b/src/sat/cadical/cadical_stats.cpp @@ -55,13 +55,12 @@ void Stats::print (Internal *internal) { propagations += stats.propagations.search; propagations += stats.propagations.transred; propagations += stats.propagations.vivify; - propagations += stats.propagations.walk; int64_t vivified = stats.vivifysubs + stats.vivifystrs; int64_t searchticks = stats.ticks.search[0] + stats.ticks.search[1]; int64_t inprobeticks = stats.ticks.vivify + stats.ticks.probe + stats.ticks.factor + stats.ticks.ternary + - stats.ticks.sweep; + stats.ticks.sweep + stats.ticks.backbone; int64_t totalticks = searchticks + inprobeticks; size_t extendbytes = internal->external->extension.size (); @@ -96,6 +95,25 @@ void Stats::print (Internal *internal) { PRT (" backtracked: %15" PRId64 " %10.2f %% of conflicts", stats.backtracks, percent (stats.backtracks, stats.conflicts)); } + if (all || stats.incremental_decay) { + PRT ("inc-decay: %15" PRId64 " %10.2f %% per search", + stats.incremental_decay, + percent (stats.incremental_decay, stats.searches)); + } + if (all || stats.backbone.rounds) { + PRT ("backbone: %15" PRId64 " %10.2f %% of vars", + stats.backbone.probes, + percent (stats.backbone.probes, stats.vars)); + PRT (" rounds: %15" PRId64 " %10.2f per phase", + stats.backbone.rounds, + relative (stats.backbone.rounds, stats.backbone.phases)); + PRT (" phases: %15" PRId64 " %10.2f interval", + stats.backbone.phases, + relative (stats.conflicts, stats.backbone.phases)); + PRT (" units: %15" PRId64 " %10.2f per phase", + stats.backbone.units, + relative (stats.backbone.units, stats.backbone.phases)); + } if (all || stats.conditioned) { PRT ("conditioned: %15" PRId64 " %10.2f %% of irredundant clauses", @@ -147,6 +165,15 @@ void Stats::print (Internal *internal) { PRT (" searched: %15" PRId64 " %10.2f per decision", stats.searched, relative (stats.searched, stats.decisions)); } + if (all || stats.randec.random_decisions) { + PRT ("rand. dec phase: %15" PRId64 " %10.2f per interval", + stats.randec.random_decision_phases, + relative (stats.randec.random_decision_phases, stats.decisions)); + PRT ("random decs: %15" PRId64 " %10.2f per phase", + stats.randec.random_decisions, + relative (stats.randec.random_decisions, + stats.randec.random_decision_phases)); + } if (all || stats.all.eliminated) { PRT ("eliminated: %15" PRId64 " %10.2f %% of all variables", stats.all.eliminated, percent (stats.all.eliminated, stats.vars)); @@ -301,7 +328,7 @@ void Stats::print (Internal *internal) { PRT ("learned: %15" PRId64 " %10.2f %% per conflict", stats.learned.clauses, percent (stats.learned.clauses, stats.conflicts)); - PRT ("@ bumped: %15" PRId64 " %10.2f per learned", + PRT (" bumped: %15" PRId64 " %10.2f per learned", stats.bumped, relative (stats.bumped, stats.learned.clauses)); PRT (" recomputed: %15" PRId64 " %10.2f %% per learned", stats.recomputed, @@ -387,9 +414,6 @@ void Stats::print (Internal *internal) { PRT (" vivifyprops: %15" PRId64 " %10.2f %% of propagations", stats.propagations.vivify, percent (stats.propagations.vivify, propagations)); - PRT (" walkprops: %15" PRId64 " %10.2f %% of propagations", - stats.propagations.walk, - percent (stats.propagations.walk, propagations)); if (all || stats.reactivated) { PRT ("reactivated: %15" PRId64 " %10.2f %% of all variables", stats.reactivated, percent (stats.reactivated, stats.vars)); @@ -610,6 +634,8 @@ void Stats::print (Internal *internal) { stats.ticks.search[0], percent (stats.ticks.search[0], searchticks)); PRT (" inprobeticks: %15" PRId64 " %10.2f %% totalticks", inprobeticks, percent (inprobeticks, totalticks)); + PRT (" backboneticks:%15" PRId64 " %10.2f %% searchticks", + stats.ticks.backbone, percent (stats.ticks.backbone, searchticks)); PRT (" factorticks: %15" PRId64 " %10.2f %% searchticks", stats.ticks.factor, percent (stats.ticks.factor, searchticks)); PRT (" probeticks: %15" PRId64 " %10.2f %% searchticks", @@ -620,6 +646,20 @@ void Stats::print (Internal *internal) { stats.ticks.ternary, percent (stats.ticks.ternary, searchticks)); PRT (" vivifyticks: %15" PRId64 " %10.2f %% searchticks", stats.ticks.vivify, percent (stats.ticks.vivify, searchticks)); + PRT (" walkticks: %15" PRId64 " %10.2f %% searchticks", + stats.ticks.walk, percent (stats.ticks.walk, searchticks)); + PRT (" walkflipticks:%15" PRId64 " %10.2f %% searchticks", + stats.ticks.walkflip, percent (stats.ticks.walkflip, searchticks)); + PRT (" walkflipbrk: %15" PRId64 " %10.2f %% searchticks", + stats.ticks.walkflipbroken, + percent (stats.ticks.walkflipbroken, searchticks)); + PRT (" walkflipWL: %15" PRId64 " %10.2f %% searchticks", + stats.ticks.walkflipWL, + percent (stats.ticks.walkflipWL, searchticks)); + PRT (" walkpickticks:%15" PRId64 " %10.2f %% searchticks", + stats.ticks.walkpick, percent (stats.ticks.walkpick, searchticks)); + PRT (" walkbreak: %15" PRId64 " %10.2f %% searchticks", + stats.ticks.walkbreak, percent (stats.ticks.walkbreak, searchticks)); if (all) { PRT ("tier recomputed: %15" PRId64 " %10.2f interval", stats.tierecomputed, @@ -646,6 +686,18 @@ void Stats::print (Internal *internal) { relative (stats.conflicts, stats.vivifications)); PRT (" vivifychecks: %15" PRId64 " %10.2f %% per conflict", stats.vivifychecks, percent (stats.vivifychecks, stats.conflicts)); + const int64_t vivified = stats.vivifiedtier1 + stats.vivifiedtier2 + + stats.vivifiedtier3 + stats.vivifiedirred; + PRT (" vivified: %15" PRId64 " %10.2f %% per check", vivified, + percent (vivified, stats.vivifychecks)); + PRT (" vified-irred: %15" PRId64 " %10.2f %% per vivified", + stats.vivifiedirred, percent (stats.vivifiedirred, vivified)); + PRT (" vified-tier1: %15" PRId64 " %10.2f %% per vivified", + stats.vivifiedtier1, percent (stats.vivifiedtier1, vivified)); + PRT (" vified-tier2: %15" PRId64 " %10.2f %% per vivified", + stats.vivifiedtier2, percent (stats.vivifiedtier2, vivified)); + PRT (" vified-tier3: %15" PRId64 " %10.2f %% per vivified", + stats.vivifiedtier3, percent (stats.vivifiedtier3, vivified)); PRT (" vivifysched: %15" PRId64 " %10.2f %% checks per scheduled", stats.vivifysched, percent (stats.vivifychecks, stats.vivifysched)); @@ -656,6 +708,9 @@ void Stats::print (Internal *internal) { stats.vivifyinst, percent (stats.vivifyinst, stats.vivifychecks)); PRT (" vivifysubs: %15" PRId64 " %10.2f %% per subsumed", stats.vivifysubs, percent (stats.vivifysubs, stats.subsumed)); + PRT (" vivifyflushed: %15" PRId64 " %10.2f %% per subsumed", + stats.vivifyflushed, + percent (stats.vivifyflushed, stats.subsumed)); PRT (" vivifysubred: %15" PRId64 " %10.2f %% per subs", stats.vivifysubred, percent (stats.vivifysubred, stats.vivifysubs)); @@ -681,13 +736,31 @@ void Stats::print (Internal *internal) { percent (stats.vivifydemote, stats.vivifystrs)); PRT (" vivifydecs: %15" PRId64 " %10.2f per checks", stats.vivifydecs, relative (stats.vivifydecs, stats.vivifychecks)); - PRT (" vivifyreused: %15" PRId64 " %10.2f %% per decision", + PRT (" vivifyreused: %15" PRId64 + " %10.2f %% per non-reused decision", stats.vivifyreused, percent (stats.vivifyreused, stats.vivifydecs)); } if (all || stats.walk.count) { PRT ("walked: %15" PRId64 " %10.2f interval", stats.walk.count, relative (stats.conflicts, stats.walk.count)); + if (all || stats.warmup.count) { + PRT (" prop-warmup: %15" PRId64 " %10.2f per warmup", + stats.warmup.propagated, + relative (stats.warmup.propagated, stats.warmup.count)); + PRT (" dec-warmup: %15" PRId64 " %10.2f per warmup", + stats.warmup.decision, + relative (stats.warmup.decision, stats.warmup.count)); + PRT (" dummydec-w: %15" PRId64 " %10.2f per warmup", + stats.warmup.dummydecision, + relative (stats.warmup.dummydecision, stats.warmup.count)); + PRT (" conflicts: %15" PRId64 " %10.2f per warmup", + stats.warmup.conflicts, + relative (stats.warmup.conflicts, stats.warmup.count)); + PRT (" warmup: %15" PRId64 " %10.2f per walk", + stats.warmup.count, + relative (stats.warmup.count, stats.walk.count)); + } #ifndef CADICAL_QUIET if (internal->profiles.walk.value > 0) PRT (" flips: %15" PRId64 " %10.2f M per second", @@ -704,6 +777,9 @@ void Stats::print (Internal *internal) { percent (stats.walk.minimum, stats.added.irredundant)); PRT (" broken: %15" PRId64 " %10.2f per flip", stats.walk.broken, relative (stats.walk.broken, stats.walk.flips)); + PRT (" improved: %15" PRId64 " %10.2f per walk", + stats.walk.improved, + relative (stats.walk.improved, stats.walk.count)); } if (all || stats.weakened) { PRT ("weakened: %15" PRId64 " %10.2f average size", @@ -736,13 +812,23 @@ void Stats::print (Internal *internal) { PRT (" unaries: %15" PRId64 " %10.2f per round", stats.congruence.unaries, relative (stats.congruence.rounds, stats.congruence.unaries)); - PRT (" rewri.-ands: %15" PRId64 " %10.2f per round", + int64_t rewritten = stats.congruence.rewritten_ands + + stats.congruence.rewritten_xors + + stats.congruence.rewritten_ites; + PRT (" rewritten: %15" PRId64 " %10.2f per round", rewritten, + percent (rewritten, stats.congruence.rounds)); + PRT (" rewri.-ands: %15" PRId64 " %10.2f per rewritten", stats.congruence.rewritten_ands, - relative (stats.congruence.rounds, - stats.congruence.rewritten_ands)); - PRT (" subsumed: %15" PRId64 " %10.2f per round", + percent (stats.congruence.rewritten_ands, rewritten)); + PRT (" rewri.-xors: %15" PRId64 " %10.2f%% per rewritten", + stats.congruence.rewritten_xors, + percent (stats.congruence.rewritten_xors, rewritten)); + PRT (" rewri.-ites: %15" PRId64 " %10.2f%% per rewritten", + stats.congruence.rewritten_ites, + percent (stats.congruence.rewritten_ites, rewritten)); + PRT (" subsumed: %15" PRId64 " %10.2f%% per round", stats.congruence.subsumed, - relative (stats.congruence.rounds, stats.congruence.subsumed)); + relative (stats.congruence.subsumed, stats.congruence.rounds)); } LINE (); @@ -750,6 +836,10 @@ void Stats::print (Internal *internal) { tout.magenta_code (), internal->opts.realtime ? "real" : "process", tout.normal_code ()); + SECTION ("glue usage"); + + internal->print_tier_usage_statistics (); + #endif // ifndef CADICAL_QUIET } @@ -810,6 +900,8 @@ void LratChecker::print_stats () { stats.original, percent (stats.original, stats.added)); MSG ("derived: %15" PRId64 " %10.2f %% of all clauses", stats.derived, percent (stats.derived, stats.added)); + MSG ("rat: %15" PRId64 " %10.2f %% of derived clauses", + stats.rat, percent (stats.rat, stats.derived)); MSG ("deleted: %15" PRId64 " %10.2f %% of all clauses", stats.deleted, percent (stats.deleted, stats.added)); MSG ("finalized: %15" PRId64 " %10.2f %% of all clauses", diff --git a/src/sat/cadical/cadical_subsume.cpp b/src/sat/cadical/cadical_subsume.cpp index b75040ca8..c2a5fa1b0 100644 --- a/src/sat/cadical/cadical_subsume.cpp +++ b/src/sat/cadical/cadical_subsume.cpp @@ -308,7 +308,6 @@ inline int Internal::try_to_subsume_clause (Clause *c, return 0; } - struct subsume_less_noccs { Internal *internal; subsume_less_noccs (Internal *i) : internal (i) {} diff --git a/src/sat/cadical/cadical_sweep.cpp b/src/sat/cadical/cadical_sweep.cpp index 4dca2b4bc..05eb9eb4c 100644 --- a/src/sat/cadical/cadical_sweep.cpp +++ b/src/sat/cadical/cadical_sweep.cpp @@ -923,6 +923,13 @@ int64_t Internal::add_sweep_binary (sweep_proof_clause pc, int lit, proof->weaken_minus (id, clause); } external->push_binary_clause_on_extension_stack (id, lit, other); + for (auto &tracer : tracers) { + if (externalize (lit) < 0) + break; + const int elit = externalize (lit); + const int eother = externalize (other); + tracer->notify_equivalence (elit, -eother); + } clause.clear (); lrat_chain.clear (); return id; @@ -1639,7 +1646,7 @@ const char *Internal::sweep_variable (Sweeper &sweeper, int idx) { units = stats.sweep_units - units; solved = stats.sweep_solved - solved; #endif - VERBOSE (3, + VERBOSE (4, "complete swept variable %d backbone with %" PRIu64 " units in %" PRIu64 " solver calls", externalize (idx), units, solved); @@ -1915,7 +1922,7 @@ bool Internal::sweep () { const char *res = #endif sweep_variable (sweeper, idx); - VERBOSE (2, "swept[%" PRIu64 "] external variable %d %s", swept, + VERBOSE (3, "swept[%" PRIu64 "] external variable %d %s", swept, externalize (idx), res); if (++swept == limit) { VERBOSE (2, diff --git a/src/sat/cadical/cadical_tier.cpp b/src/sat/cadical/cadical_tier.cpp index 92faf507f..f8f4e0841 100644 --- a/src/sat/cadical/cadical_tier.cpp +++ b/src/sat/cadical/cadical_tier.cpp @@ -1,6 +1,8 @@ #include "global.h" #include "internal.hpp" +#include "util.hpp" +#include ABC_NAMESPACE_IMPL_START @@ -14,8 +16,9 @@ void Internal::recompute_tier () { const int64_t delta = stats.tierecomputed >= 16 ? 1u << 16 : (1u << stats.tierecomputed); lim.recompute_tier = stats.conflicts + delta; - LOG ("rescheduling in %zd at %zd (conflicts at %zd)", delta, - lim.recompute_tier, stats.conflicts); + LOG ("rescheduling in %" PRId64 " at %" PRId64 " (conflicts at %" PRId64 + ")", + delta, lim.recompute_tier, stats.conflicts); #ifndef CADICAL_NDEBUG uint64_t total_used = 0; for (auto u : stats.used[stable]) @@ -34,13 +37,21 @@ void Internal::recompute_tier () { stats.bump_used[stable] * opts.tier1limit / 100; uint64_t accumulated_tier2_limit = stats.bump_used[stable] * opts.tier2limit / 100; - uint64_t accumulated_used = 0; - for (size_t glue = 0; glue < stats.used[stable].size (); ++glue) { + tier1[stable] = 1; + tier2[stable] = 1; + uint64_t accumulated_used = stats.used[stable][0]; + size_t glue = 1; + for (; glue < stats.used[stable].size (); ++glue) { const uint64_t u = stats.used[stable][glue]; accumulated_used += u; - if (accumulated_used <= accumulated_tier1_limit) { + if (accumulated_used >= accumulated_tier1_limit) { tier1[stable] = glue; + break; } + } + for (; glue < stats.used[stable].size (); ++glue) { + const uint64_t u = stats.used[stable][glue]; + accumulated_used += u; if (accumulated_used >= accumulated_tier2_limit) { tier2[stable] = glue; break; @@ -48,12 +59,137 @@ void Internal::recompute_tier () { } } - LOG ("tier1 limit = %d in %s mode", tier1[stable], - stable ? "stable" : "focused"); - LOG ("tier2 limit = %d in %s mode", tier2[stable], - stable ? "stable" : "focused"); + CADICAL_assert (tier1[stable] > 0); + + CADICAL_assert (tier1[stable]); + CADICAL_assert (tier2[stable]); + + if (tier1[stable] < opts.tier1minglue) { + LOG ("tier1 limit of %d is too low, setting %d instead", tier1[stable], + opts.tier1minglue); + tier1[stable] = opts.tier1minglue; + } + if (tier2[stable] < opts.tier2minglue) { + LOG ("tier2 limit of %d is too low, setting %d instead", tier2[stable], + opts.tier2minglue); + tier2[stable] = opts.tier2minglue; + } + if (tier1[stable] >= tier2[stable]) + tier2[stable] = tier1[stable] + 1; + CADICAL_assert (tier2[stable] > tier1[stable]); + + PHASE ("retiered", stats.tierecomputed, + "tier1 limit = %d in %s mode, tier2 limit = %d in %s mode", + tier1[stable], stable ? "stable" : "focused", tier2[stable], + stable ? "stable" : "focused"); } +void Internal::print_tier_usage_statistics () { + recompute_tier (); + + for (auto stable : {false, true}) { + unsigned total_used = 0; + for (size_t glue = 0; glue < stats.used[stable].size (); ++glue) + total_used += stats.used[stable][glue]; + + const std::string mode = stable ? "stable" : "focused"; + const size_t tier1 = internal->tier1[stable]; + const size_t tier2 = internal->tier2[stable]; + if (tier1 > tier2 && opts.reducetier1glue > opts.reducetier2glue) { + MSG ("tier1 > tier 2 due to the options, giving up"); + break; + } + CADICAL_assert (tier1 <= tier2); + unsigned prefix, suffix; + unsigned span = tier2 - tier1 + 1; + const unsigned max_printed = 5; + CADICAL_assert (max_printed & 1), CADICAL_assert (max_printed / 2 > 0); + if (span > max_printed) { + prefix = tier1 + max_printed / 2 - 1; + suffix = tier2 - max_printed / 2 + 1; + } else + prefix = UINT_MAX, suffix = 0; + + uint64_t accumulated_middle = 0; + int glue_digits = 1, clauses_digits = 1; + for (unsigned glue = 0; glue <= stats.used[stable].size (); glue++) { + if (glue < tier1) + continue; + uint64_t used = stats.used[stable][glue]; + int tmp_glue = 0, tmp_clauses = 0; + if (glue <= prefix || suffix <= glue) { + tmp_glue = glue; + tmp_clauses = used; + } else { + accumulated_middle += used; + if (glue + 1 == suffix) { + tmp_glue = (prefix + 1) + (glue) + 1; + tmp_clauses = (accumulated_middle); + } + } + if (tmp_glue > glue_digits) + glue_digits = tmp_glue; + if (tmp_clauses > clauses_digits) + clauses_digits = tmp_clauses; + if (glue == tier2) + break; + } + + accumulated_middle = 0; + uint64_t accumulated = 0; + std::string output; + for (unsigned glue = 0; glue <= stats.used[stable].size (); glue++) { + uint64_t used = stats.used[stable][glue]; + accumulated += used; + if (glue < tier1) + continue; + if (glue <= prefix || suffix <= glue + 1) { + output += mode + " glue "; + } + if (glue <= prefix || suffix <= glue) { + std::string glue_str = std::to_string (glue); + output += glue_str; + output += " used " + std::to_string (used); + output += + " clauses " + + std::to_string (percent (used, total_used)).substr (0, 5) + "%"; + output += " accumulated " + + std::to_string (percent (accumulated, total_used)) + .substr (0, 5) + + "%"; + if (glue == tier1) + output += " tier1"; + if (glue == tier2) + output += " tier2"; + MSG ("%s", output.c_str ()); + output.clear (); + } else { + accumulated_middle += used; + if (glue + 1 == suffix) { + std::string glue_str = std::to_string (prefix + 1) + "-" + + std::to_string (suffix - 1); + output += + glue_str + " used " + std::to_string (accumulated_middle); + output += + " clauses " + + std::to_string (percent (accumulated_middle, total_used)) + .substr (0, 5) + + "%"; + output += " accumulated " + + std::to_string (percent (accumulated, total_used)) + .substr (0, 5) + + "%"; + MSG ("%s", output.c_str ()); + output.clear (); + } + } + if (glue == tier2) + break; + } + + LINE (); + } +} } // namespace CaDiCaL ABC_NAMESPACE_IMPL_END diff --git a/src/sat/cadical/cadical_veripbtracer.cpp b/src/sat/cadical/cadical_veripbtracer.cpp index b1f7c95a7..4d743bf9b 100644 --- a/src/sat/cadical/cadical_veripbtracer.cpp +++ b/src/sat/cadical/cadical_veripbtracer.cpp @@ -190,11 +190,41 @@ inline void VeripbTracer::put_binary_id (int64_t id, bool can_be_negative) { /*------------------------------------------------------------------------*/ +void VeripbTracer::veripb_add_derived_clause (int64_t id, bool redundant, + int witness, + const vector &clause) { + CADICAL_assert (witness == clause[0]); + file->put ("red "); + for (const auto &external_lit : clause) { + file->put ("1 "); + if (external_lit < 0) + file->put ('~'); + file->put ('x'); + file->put (abs (external_lit)); + file->put (' '); + } + file->put (">= 1 : "); + file->put ('x'); + file->put (abs (witness)); + file->put (" -> "); + if (witness < 0) + file->put ("0"); + else + file->put ("1"); + file->put (";\n"); + if (!redundant && checked_deletions) { + file->put ("core id "); + file->put (id); + file->put (";\n"); + } +} + void VeripbTracer::veripb_add_derived_clause ( int64_t id, bool redundant, const vector &clause, const vector &chain) { file->put ("pol "); bool first = true; + CADICAL_assert (!chain.empty ()); for (auto p = chain.rbegin (); p != chain.rend (); p++) { auto cid = *p; if (first) { @@ -206,7 +236,7 @@ void VeripbTracer::veripb_add_derived_clause ( file->put (" + s"); } } - file->put ("\n"); + file->put (";\n"); file->put ("e "); for (const auto &external_lit : clause) { file->put ("1 "); @@ -216,13 +246,13 @@ void VeripbTracer::veripb_add_derived_clause ( file->put (abs (external_lit)); file->put (' '); } - file->put (">= 1 ; "); + file->put (">= 1 : "); file->put (id); - file->put (" ;\n"); + file->put (";\n"); if (!redundant && checked_deletions) { file->put ("core id "); file->put (id); - file->put ("\n"); + file->put (";\n"); } } @@ -237,19 +267,19 @@ void VeripbTracer::veripb_add_derived_clause (int64_t id, bool redundant, file->put (abs (external_lit)); file->put (' '); } - file->put (">= 1 ;\n"); + file->put (">= 1;\n"); if (!redundant && checked_deletions) { file->put ("core id "); file->put (id); - file->put ("\n"); + file->put (";\n"); } } void VeripbTracer::veripb_begin_proof (int64_t reserved_ids) { - file->put ("pseudo-Boolean proof version 2.0\n"); + file->put ("pseudo-Boolean proof version 3.0\n"); file->put ("f "); file->put (reserved_ids); - file->put ("\n"); + file->put (";\n"); } void VeripbTracer::veripb_delete_clause (int64_t id, bool redundant) { @@ -261,18 +291,18 @@ void VeripbTracer::veripb_delete_clause (int64_t id, bool redundant) { file->put ("delc "); } file->put (id); - file->put ("\n"); + file->put (";\n"); } void VeripbTracer::veripb_report_status (bool unsat, int64_t conflict_id) { - file->put ("output NONE\n"); + file->put ("output NONE;\n"); if (unsat) { file->put ("conclusion UNSAT : "); file->put (conflict_id); - file->put (" \n"); + file->put (";\n"); } else - file->put ("conclusion NONE\n"); - file->put ("end pseudo-Boolean proof\n"); + file->put ("conclusion NONE;\n"); + file->put ("end pseudo-Boolean proof;\n"); } void VeripbTracer::veripb_strengthen (int64_t id) { @@ -280,7 +310,7 @@ void VeripbTracer::veripb_strengthen (int64_t id) { return; file->put ("core id "); file->put (id); - file->put ("\n"); + file->put (";\n"); } /*------------------------------------------------------------------------*/ @@ -295,12 +325,15 @@ void VeripbTracer::begin_proof (int64_t id) { } void VeripbTracer::add_derived_clause (int64_t id, bool redundant, + int witness, const vector &clause, const vector &chain) { if (file->closed ()) return; LOG ("VERIPB TRACER tracing addition of derived clause[%" PRId64 "]", id); - if (with_antecedents) + if (witness) + veripb_add_derived_clause (id, redundant, witness, clause); + else if (with_antecedents) veripb_add_derived_clause (id, redundant, clause, chain); else veripb_add_derived_clause (id, redundant, clause); diff --git a/src/sat/cadical/cadical_version.cpp b/src/sat/cadical/cadical_version.cpp index a0cceef63..dfc9c79fe 100644 --- a/src/sat/cadical/cadical_version.cpp +++ b/src/sat/cadical/cadical_version.cpp @@ -26,15 +26,15 @@ #ifdef CADICAL_NBUILD #ifndef VERSION -#define VERSION "2.2.0-rc1" +#define VERSION "2.2.0" #endif // VERSION #endif // CADICAL_NBUILD - /*------------------------------------------------------------------------*/ +/*------------------------------------------------------------------------*/ - // The copyright of the code is here. +// The copyright of the code is here. - static const char *COPYRIGHT = "Copyright (c) 2016-2024"; +static const char *COPYRIGHT = "Copyright (c) 2016-2024"; static const char *AUTHORS = "A. Biere, M. Fleury, N. Froleyks, K. Fazekas, F. Pollitt, T. Faller"; static const char *AFFILIATIONS = diff --git a/src/sat/cadical/cadical_vivify.cpp b/src/sat/cadical/cadical_vivify.cpp index 5ec6401cf..44f3cf306 100644 --- a/src/sat/cadical/cadical_vivify.cpp +++ b/src/sat/cadical/cadical_vivify.cpp @@ -52,6 +52,7 @@ namespace CaDiCaL { inline void Internal::vivify_subsume_clause (Clause *subsuming, Clause *subsumed) { + CADICAL_assert (subsumed != subsuming); stats.subsumed++; stats.vivifysubs++; #ifndef CADICAL_NDEBUG @@ -75,7 +76,12 @@ inline void Internal::vivify_subsume_clause (Clause *subsuming, } CADICAL_assert (real_size_subsuming <= real_size_subsumed); #endif - LOG (subsumed, "subsumed"); + LOG (subsumed, "subsumed to be deleted"); + LOG (subsuming, "subsuming to be (un)deleted"); + if (subsumed->redundant && subsuming->redundant && + subsuming->glue < subsumed->glue) { + promote_clause (subsuming, subsumed->glue); + } if (subsumed->redundant) { stats.subred++; ++stats.vivifysubred; @@ -116,7 +122,9 @@ inline void Internal::vivify_subsume_clause (Clause *subsuming, } // demoting a clause (opposite is promote from subsume.cpp) - +// +// This turned out to not be useful, but we kept it for the API call in the +// proof tracers. inline void Internal::demote_clause (Clause *c) { stats.subsumed++; stats.vivifydemote++; @@ -329,112 +337,22 @@ struct vivify_more_noccs_kissat { vivify_more_noccs_kissat (Internal *i) : internal (i) {} bool operator() (int a, int b) { - unsigned t = internal->noccs (a); - unsigned s = internal->noccs (b); - return ((t - s) | ((b - a) & ~(s - t))) >> 31; - } -}; - -// Sort candidate clauses by the number of occurrences (actually by their -// score) of their literals, with clauses to be vivified first last. We -// assume that clauses are sorted w.r.t. more occurring (higher score) -// literals first (with respect to 'vivify_more_noccs'). -// -// For example if there are the following (long irredundant) clauses -// -// 1 -3 -4 (A) -// -1 -2 3 4 (B) -// 2 -3 4 (C) -// -// then we have the following literal scores using Jeroslow Wang scores and -// normalizing it with 2^12 (which is the same as 1<<12): -// -// nocc ( 1) = 2^12 * (2^-3 ) = 512 3. -// nocc (-1) = 2^12 * (2^-4 ) = 256 6. -// nocc ( 2) = 2^12 * (2^-3 ) = 512 4. -// nocc (-2) = 2^12 * (2^-4 ) = 256 7. @1 -// nocc ( 3) = 2^12 * (2^-4 ) = 256 8. -// nocc (-3) = 2^12 * (2^-3 + 2^-3) = 1024 1. -// nocc ( 4) = 2^12 * (2^-3 + 2^-4) = 768 2. -// nocc (-4) = 2^12 * (2^-3 ) = 512 5. -// -// which gives the literal order (according to 'vivify_more_noccs') -// -// -3, 4, 1, 2, -4, -1, -2, 3 -// -// Then sorting the literals in each clause gives -// -// -3 1 -4 (A') -// 4 -1 -2 3 (B') @2 -// -3 4 2 (C') -// -// and finally sorting those clauses lexicographically w.r.t. scores is -// -// -3 4 2 (C') -// -3 1 -4 (A') @3 -// 4 -1 -2 3 (B') -// -// This order is defined by 'vivify_clause_later' which returns 'true' if -// the first clause should be vivified later than the second. - -struct vivify_clause_later { - - Internal *internal; - - vivify_clause_later (Internal *i) : internal (i) {} - - bool operator() (Clause *a, Clause *b) const { - - if (a == b) - return false; - - // First focus on clauses scheduled in the last vivify round but not - // checked yet since then. - // - if (!a->vivify && b->vivify) - return true; - if (a->vivify && !b->vivify) - return false; - - // Among redundant clauses (in redundant mode) prefer small glue. - // - if (a->redundant) { - CADICAL_assert (b->redundant); - if (a->glue > b->glue) - return true; - if (a->glue < b->glue) - return false; - } - - // Then prefer shorter size. - // - if (a->size > b->size) - return true; - if (a->size < b->size) - return false; - - // Now compare literals in the clauses lexicographically with respect to - // the literal order 'vivify_more_noccs' assuming literals are sorted - // decreasingly with respect to that order. - // - const auto eoa = a->end (), eob = b->end (); - auto j = b->begin (); - for (auto i = a->begin (); i != eoa && j != eob; i++, j++) - if (*i != *j) - return vivify_more_noccs (internal) (*j, *i); - - return j == eob; // Prefer shorter clauses to be vivified first. + unsigned s = internal->noccs (a); + unsigned t = internal->noccs (b); + return (((t - s) | + ((internal->vlit (a) - internal->vlit (b)) & ~(s - t))) >> + 31); } }; /*------------------------------------------------------------------------*/ -// Attempting on-the-fly subsumption during sorting when the last line is -// reached in 'vivify_clause_later' above turned out to be trouble some for -// identical clauses. This is the single point where 'vivify_clause_later' -// is not asymmetric and would require 'stable' sorting for determinism. It -// can also not be made 'complete' on-the-fly. Instead of on-the-fly -// subsumption we thus go over the sorted scheduled in a linear scan +// Attempting on-the-fly subsumption during sorting when the last line +// is turned out to be trouble some for identical clauses. This is +// the single point where sorting is not asymmetric and would require +// 'stable' sorting for determinism. It can also not be made +// 'complete' on-the-fly. Instead of on-the-fly subsumption, we +// (optionnaly) thus go over the sorted scheduled in a linear scan // again and remove certain subsumed clauses (the subsuming clause is // syntactically a prefix of the subsumed clause), which includes // those troublesome syntactically identical clauses. @@ -448,16 +366,16 @@ struct vivify_flush_smaller { for (; i != eoa && j != eob; i++, j++) if (*i != *j) return *i < *j; - - return j == eob && i != eoa; + const bool smaller = j == eob && i != eoa; + return smaller; } }; void Internal::flush_vivification_schedule (std::vector &schedule, int64_t &ticks) { ticks += 1 + 3 * cache_lines (schedule.size (), sizeof (Clause *)); + // we cannot use msort here, as we cannot calculate a score stable_sort (schedule.begin (), schedule.end (), vivify_flush_smaller ()); - const auto end = schedule.end (); auto j = schedule.begin (), i = j; @@ -493,6 +411,7 @@ void Internal::flush_vivification_schedule (std::vector &schedule, "flushed %" PRId64 " subsumed scheduled clauses", subsumed); stats.vivifysubs += subsumed; + stats.vivifyflushed += subsumed; if (subsumed) { schedule.resize (j - schedule.begin ()); @@ -507,7 +426,6 @@ void Internal::flush_vivification_schedule (std::vector &schedule, // we schedule a clause to be vivified. For redundant clauses we initially // only try to vivify them if they are likely to survive the next 'reduce' // operation, but this left the last schedule empty most of the time. - bool Internal::consider_to_vivify_clause (Clause *c) { if (c->garbage) return false; @@ -552,7 +470,7 @@ struct vivify_better_watch { // Common code to actually strengthen a candidate clause. The resulting // strengthened clause is communicated through the global 'clause'. -void Internal::vivify_strengthen (Clause *c) { +void Internal::vivify_strengthen (Clause *c, int64_t &ticks) { CADICAL_assert (!clause.empty ()); @@ -566,7 +484,7 @@ void Internal::vivify_strengthen (Clause *c) { // lrat_chain.clear (); done in search_assign stats.vivifyunits++; - bool ok = propagate (); + bool ok = vivify_propagate (ticks); if (!ok) learn_empty_clause (); @@ -613,6 +531,8 @@ void Internal::vivify_strengthen (Clause *c) { ++stats.vivifystrs; } +// When shortening clauses, we have to update the watched literals. +// Actually we don't do it, and instead simply backtrack back enough. void Internal::vivify_sort_watched (Clause *c) { sort (c->begin (), c->end (), vivify_better_watch (this)); @@ -637,18 +557,20 @@ void Internal::vivify_sort_watched (Clause *c) { CADICAL_assert (new_level >= 0); if (new_level < level) - backtrack (new_level); + backtrack_without_updating_phases (new_level); CADICAL_assert (val (lit0) >= 0); CADICAL_assert (val (lit1) >= 0 || (val (lit0) > 0 && val (lit1) < 0 && var (lit0).level <= var (lit1).level)); } + +/*------------------------------------------------------------------------*/ + // Conflict analysis from 'start' which learns a decision only clause. // // We cannot use the stack-based implementation of Kissat, because we need // to iterate over the conflict in topological ordering to produce a valid // LRAT proof - void Internal::vivify_analyze (Clause *start, bool &subsumes, Clause **subsuming, const Clause *const candidate, int implied, @@ -685,7 +607,7 @@ void Internal::vivify_analyze (Clause *start, bool &subsumes, continue; LOG ("unit reason for %d", other); int64_t id = unit_id (-other); - LOG ("adding unit reason %zd for %d", id, other); + LOG ("adding unit reason %" PRId64 " for %s", id, LOGLIT (other)); unit_chain.push_back (id); f.seen = true; analyzed.push_back (other); @@ -703,9 +625,9 @@ void Internal::vivify_analyze (Clause *start, bool &subsumes, analyzed.push_back (other); f.seen = true; } - if (start->redundant) { - const int new_glue = recompute_glue (start); - promote_clause (start, new_glue); + if (reason != start && reason->redundant) { + const int new_glue = recompute_glue (reason); + promote_clause (reason, new_glue); } if (subsumes) { CADICAL_assert (reason); @@ -740,6 +662,9 @@ void Internal::vivify_analyze (Clause *start, bool &subsumes, (void) candidate; } +/*------------------------------------------------------------------------*/ +// First decide which clause (candidate or conflict) to analyze and +// how to do it. We also prepare the clause by removing units. void Internal::vivify_deduce (Clause *candidate, Clause *conflict, int implied, Clause **subsuming, bool &redundant) { @@ -761,7 +686,7 @@ void Internal::vivify_deduce (Clause *candidate, Clause *conflict, } else { reason = (conflict ? conflict : candidate); CADICAL_assert (reason); - CADICAL_assert (!reason->garbage); + CADICAL_assert (!reason->garbage || reason->size == 2); mark2 (candidate); subsumes = (candidate != reason); redundant = reason->redundant; @@ -780,7 +705,7 @@ void Internal::vivify_deduce (Clause *candidate, Clause *conflict, // nevertheless we can use var (l) as if l was still assigned // because var is updated lazily int64_t id = unit_id (-lit); - LOG ("adding unit reason %zd for %d", id, lit); + LOG ("adding unit reason %" PRId64 " for %s", id, LOGLIT (lit)); unit_chain.push_back (id); } f.seen = true; @@ -837,8 +762,10 @@ void Internal::vivify_deduce (Clause *candidate, Clause *conflict, lrat_chain.clear (); } } + /*------------------------------------------------------------------------*/ +// checks whether the clause can be strengthen or not. bool Internal::vivify_shrinkable (const std::vector &sorted, Clause *conflict) { @@ -875,6 +802,7 @@ bool Internal::vivify_shrinkable (const std::vector &sorted, } return false; } + /*------------------------------------------------------------------------*/ inline void Internal::vivify_increment_stats (const Vivifier &vivifier) { @@ -894,10 +822,11 @@ inline void Internal::vivify_increment_stats (const Vivifier &vivifier) { break; } } + /*------------------------------------------------------------------------*/ -// instantiate last literal (see the description of the hack track 2023), -// fix the watches and -// backtrack two level back +// instantiate last literal (see the description of the hack track +// 2023), fix the watches and backtrack two level back to make sure +// the watches are correct. bool Internal::vivify_instantiate ( const std::vector &sorted, Clause *c, std::vector> &lrat_stack, @@ -909,7 +838,7 @@ bool Internal::vivify_instantiate ( CADICAL_assert (!var (lit).reason); CADICAL_assert (var (lit).level); CADICAL_assert (val (lit)); - backtrack (level - 1); + backtrack_without_updating_phases (level - 1); CADICAL_assert (val (lit) == 0); stats.vivifydecs++; vivify_assume (lit); @@ -948,6 +877,7 @@ bool Internal::vivify_clause (Vivifier &vivifier, Clause *c) { CADICAL_assert (c->size > 2); // see (NO-BINARY) below CADICAL_assert (analyzed.empty ()); + CADICAL_assert (!ignore); c->vivify = false; // mark as checked / tried c->vivified = true; // and globally remember @@ -984,6 +914,8 @@ bool Internal::vivify_clause (Vivifier &vivifier, Clause *c) { } sort (sorted.begin (), sorted.end (), vivify_more_noccs_kissat (this)); + CADICAL_assert (std::is_sorted (sorted.begin (), sorted.end (), + vivify_more_noccs (this))); // The actual vivification checking is performed here, by assuming the // negation of each of the remaining literals of the clause in turn and @@ -1047,7 +979,7 @@ bool Internal::vivify_clause (Vivifier &vivifier, Clause *c) { const int decision = control[l].decision; if (-lit == decision) { LOG ("reusing decision %d at decision level %d", decision, l); - stats.vivifyreused++; + ++stats.vivifyreused; if (++l > level) break; } else { @@ -1169,15 +1101,13 @@ bool Internal::vivify_clause (Vivifier &vivifier, Clause *c) { reverse (lrat_chain.begin (), lrat_chain.end ()); } + // For an explanation of the code, see our POS'25 paper ``Revisiting + // Clause Vivification'' (although for the experiments we focused on + // Kissat instead of CaDiCaL) if (subsuming) { CADICAL_assert (c != subsuming); - LOG (c, "deleting subsumed clause"); - if (c->redundant && subsuming->redundant && c->glue < subsuming->glue) { - promote_clause (c, c->glue); - } vivify_subsume_clause (subsuming, c); res = false; - // stats.vivifysubs++; // already done in vivify_subsume_clause } else if (vivify_shrinkable (sorted, conflict)) { vivify_increment_stats (vivifier); LOG ("vivify succeeded, learning new clause"); @@ -1185,7 +1115,7 @@ bool Internal::vivify_clause (Vivifier &vivifier, Clause *c) { LOG (lrat_chain, "lrat"); LOG (clause, "learning clause"); conflict = nullptr; // TODO dup from below - vivify_strengthen (c); + vivify_strengthen (c, ticks); res = true; } else if (subsume && c->redundant) { LOG (c, "vivification implied"); @@ -1193,8 +1123,8 @@ bool Internal::vivify_clause (Vivifier &vivifier, Clause *c) { ++stats.vivifyimplied; res = true; } else if ((conflict || subsume) && !c->redundant && !redundant) { - LOG ("demote clause from irredundant to redundant"); if (opts.vivifydemote) { + LOG ("demote clause from irredundant to redundant"); demote_clause (c); const int new_glue = recompute_glue (c); promote_clause (c, new_glue); @@ -1235,6 +1165,25 @@ bool Internal::vivify_clause (Vivifier &vivifier, Clause *c) { clear_analyzed_literals (); // TODO why needed? lrat_chain.clear (); conflict = nullptr; + ignore = nullptr; + + if (res) { + switch (vivifier.tier) { + case Vivify_Mode::IRREDUNDANT: + ++stats.vivifiedirred; + break; + case Vivify_Mode::TIER1: + ++stats.vivifiedtier1; + break; + case Vivify_Mode::TIER2: + ++stats.vivifiedtier2; + break; + default: + CADICAL_assert (vivifier.tier == Vivify_Mode::TIER3); + ++stats.vivifiedtier3; + break; + } + } return res; } @@ -1251,9 +1200,9 @@ bool Internal::vivify_clause (Vivifier &vivifier, Clause *c) { // its own but this is not actually necessary. // -// Non-recursive version, as some bugs have been found. DFS over the -// reasons with preordering (aka we explore the entire reason before -// exploring deeper) +// Non-recursive version, as some bugs have been found by Dominik Schreiber +// during his very large experiments. DFS over the reasons with preordering +// (aka we explore the entire reason before exploring deeper) void Internal::vivify_build_lrat ( int lit, Clause *reason, std::vector> &stack) { @@ -1309,8 +1258,6 @@ void Internal::vivify_build_lrat ( inline void Internal::vivify_chain_for_units (int lit, Clause *reason) { if (!lrat) return; - // LOG ("building chain for units"); bad line for debugging - // equivalence if (opts.chrono && assignment_level (lit, reason)) return; if (level) return; // not decision level 0 CADICAL_assert (lrat_chain.empty ()); @@ -1349,7 +1296,8 @@ vivify_ref create_ref (Internal *internal, Clause *c) { { const int64_t lit_count = internal->noccs (lit); CADICAL_assert (lit_count); - LOG ("checking literal %d with %zd occurrences", lit, lit_count); + LOG ("checking literal %s with %" PRId64 " occurrences", + LOGLIT (lit), lit_count); if (lit_count <= best_count) continue; best_count = lit_count; @@ -1361,32 +1309,35 @@ vivify_ref create_ref (Internal *internal, Clause *c) { CADICAL_assert (best_count); CADICAL_assert (best_count < UINT32_MAX); ref.count[i] = - ((uint64_t) best_count << 32) + (uint64_t) internal->vlit (best); - LOG ("final count at position %d is %d - %d: %lu", i, best, best_count, - ref.count[i]); + (((uint64_t) best_count) << 32) + (uint64_t) internal->vlit (best); + LOG ("final count at position %d is %s - %u: %" PRIu64, i, + LOGLIT (best), best_count, ref.count[i]); lits[i] = best; } return ref; } /*------------------------------------------------------------------------*/ inline void -Internal::vivify_prioritize_leftovers ([[maybe_unused]] char tag, - size_t prioritized, +Internal::vivify_prioritize_leftovers (char tag, size_t prioritized, std::vector &schedule) { if (prioritized) { PHASE ("vivify", stats.vivifications, - "[phase %c] leftovers of %" PRId64 " clause", tag, prioritized); + "[phase %c] leftovers of %zu clause", tag, prioritized); } else { PHASE ("vivify", stats.vivifications, "[phase %c] prioritizing all clause", tag); for (auto c : schedule) c->vivify = true; } +#ifdef CADICAL_QUIET + (void) tag; +#endif const size_t max = opts.vivifyschedmax; if (schedule.size () > max) { if (prioritized) { - std::partition (begin (schedule), end (schedule), - [] (Clause *c) { return c->vivify; }); + // put the left-overs first to be kept + std::stable_partition (begin (schedule), end (schedule), + [] (Clause *c) { return c->vivify; }); } schedule.resize (max); } @@ -1463,6 +1414,8 @@ void Internal::vivify_initialize (Vivifier &vivifier, int64_t &ticks) { if (opts.vivifyflush) { clear_watches (); for (auto &sched : vivifier.schedules) { + // approximation for schedule + ticks += 1 + cache_lines (sched.size (), sizeof (Clause *)); for (const auto &c : sched) { // Literals in scheduled clauses are sorted with their highest score // literals first (as explained above in the example at '@2'). This @@ -1471,41 +1424,26 @@ void Internal::vivify_initialize (Vivifier &vivifier, int64_t &ticks) { // below. // sort (c->begin (), c->end (), vivify_more_noccs (this)); + // ++ticks; } // Flush clauses subsumed by another clause with the same prefix, // which also includes flushing syntactically identical clauses. // flush_vivification_schedule (sched, ticks); } + // approximation for schedule + // ticks += 1 + cache_lines (clauses.size (), sizeof (Clause *)); connect_watches (); // watch all relevant clauses } -#if 0 - connect_watches (); // watch all relevant clauses - vivify_propagate (ticks); -#endif vivify_propagate (ticks); + + PHASE ("vivify", stats.vivifications, + "[phase %c] leftovers out of %zu clauses", 'u', + vivifier.schedule_tier1.size ()); } inline std::vector ¤t_refs_schedule (Vivifier &vivifier) { - switch (vivifier.tier) { - case Vivify_Mode::TIER1: - return vivifier.refs_schedule_tier1; - break; - case Vivify_Mode::TIER2: - return vivifier.refs_schedule_tier2; - break; - case Vivify_Mode::TIER3: - return vivifier.refs_schedule_tier3; - break; - default: - return vivifier.refs_schedule_irred; - break; - } -#if defined(WIN32) && !defined(__MINGW32__) - __assume(false); -#else - __builtin_unreachable (); -#endif + return vivifier.refs_schedule; } inline std::vector ¤t_schedule (Vivifier &vivifier) { @@ -1580,19 +1518,16 @@ void Internal::vivify_round (Vivifier &vivifier, int64_t ticks_limit) { if (terminated_asynchronously ()) return; - PHASE ("vivify", stats.vivifications, - "starting %c vivification round ticks limit %" PRId64 "", - vivifier.tag, ticks_limit); - - PHASE ("vivify", stats.vivifications, - "starting %c vivification round ticks limit %" PRId64 "", - vivifier.tag, ticks_limit); - - CADICAL_assert (watching ()); - auto &refs_schedule = current_refs_schedule (vivifier); auto &schedule = current_schedule (vivifier); + PHASE ("vivify", stats.vivifications, + "starting %c vivification round ticks limit %" PRId64 + " with %zu clauses", + vivifier.tag, ticks_limit, schedule.size ()); + + CADICAL_assert (watching ()); + int64_t ticks = 1 + schedule.size (); // Sort candidates, with first to be tried candidate clause last, i.e., @@ -1609,7 +1544,7 @@ void Internal::vivify_round (Vivifier &vivifier, int64_t ticks_limit) { // // first build the schedule with vivifier_refs - auto end_schedule = end (schedule); + const auto end_schedule = end (schedule); refs_schedule.resize (schedule.size ()); std::transform (begin (schedule), end_schedule, begin (refs_schedule), [&] (Clause *c) { return create_ref (this, c); }); @@ -1629,8 +1564,7 @@ void Internal::vivify_round (Vivifier &vivifier, int64_t ticks_limit) { std::transform (begin (refs_schedule), end (refs_schedule), begin (schedule), [] (vivify_ref c) { return c.clause; }); - erase_vector (refs_schedule); - LOG ("clause after sorting final:"); + refs_schedule.clear (); } else { // skip sorting but still put clauses with the vivify tag at the end to // be done first Kissat does this implicitely by going twice over all @@ -1697,10 +1631,10 @@ void Internal::vivify_round (Vivifier &vivifier, int64_t ticks_limit) { for (auto c : schedule) c->vivify = true; #elif 1 - // if we have gone through all the leftovers, the current clauses are - // leftovers for the next round - if (!schedule.empty () && !schedule.front ()->vivify && - schedule.back ()->vivify) + // if we have gone through all the leftovers (the next candidate + // is not one), all the current clauses are leftovers for the next + // round + if (!schedule.empty () && !schedule.back ()->vivify) for (auto c : schedule) c->vivify = true; #else @@ -1852,8 +1786,15 @@ bool Internal::vivify () { } int64_t init_ticks = 0; - // Refill the schedule every time. Unchecked clauses are 'saved' by + // Refill the schedule every time. Unchecked clauses are 'saved' by // setting their 'vivify' bit, such that they can be tried next time. + // There are two things to denote: the option 'vivifyonce' does what it is + // supposed to do, and it works because the ticks are kept for the next + // schedule. Also, we limit the size of the schedule to limit the cost of + // sorting. + // + // TODO: After limiting, the cost we fixed some heuristics bug, so maybe + // we could increase the limit. // // TODO: count against ticks.vivify directly instead of this unholy // shifting. @@ -1880,9 +1821,9 @@ bool Internal::vivify () { } if (!unsat && tier2effort) { - erase_vector ( - vivifier.schedule_tier1); // save memory (well, not really as we - // already reached the peak memory) + // save memory (well, not really as we + // already reached the peak memory) + erase_vector (vivifier.schedule_tier1); if (limit < stats.ticks.vivify) limit = stats.ticks.vivify; const double effort = (total * tier2effort) / sumeffort; @@ -1953,6 +1894,7 @@ bool Internal::vivify () { STOP_SIMPLIFIER (vivify, VIVIFY); private_steps = false; + CADICAL_assert (!ignore); return true; } diff --git a/src/sat/cadical/cadical_walk.cpp b/src/sat/cadical/cadical_walk.cpp index 13b1bf9f4..6cfb5580b 100644 --- a/src/sat/cadical/cadical_walk.cpp +++ b/src/sat/cadical/cadical_walk.cpp @@ -1,6 +1,8 @@ #include "global.h" +#include "walk.hpp" #include "internal.hpp" +#include "random.hpp" ABC_NAMESPACE_IMPL_START @@ -10,21 +12,45 @@ namespace CaDiCaL { // Random walk local search based on 'ProbSAT' ideas. +// We (based on the Master project from Leah Hohl) tried to ticks +// local search similarly to the other parts of the solver with +// limited success however. +// +// On the problem `ncc_none_5047_6_3_3_3_0_435991723', the broken part +// of walk_flip is very cheap and should not be counted in ticks, but +// on various other problems `9pipe_k' it is very important to ticks +// this part too. + +// using ClauseOrBinary = std::variant ; + struct Walker { Internal *internal; - Random random; // local random number generator - int64_t propagations; // number of propagations - int64_t limit; // limit on number of propagations - vector broken; // currently unsatisfied clauses - double epsilon; // smallest considered score - vector table; // break value to score table - vector scores; // scores of candidate literals - - double score (unsigned); // compute score from break count - - Walker (Internal *, double size, int64_t limit); + // for efficiency, storing the model each time an improvement is + // found is too costly. Instead we store some of the flips since + // last time and the position of the best model found so far. + Random random; // local random number generator + int64_t ticks; // ticks to approximate run time + int64_t limit; // limit on number of propagations + vector broken; // currently unsatisfied clauses + double epsilon; // smallest considered score + vector table; // break value to score table + vector scores; // scores of candidate literals + std::vector + flips; // remember the flips compared to the last best saved model + int best_trail_pos; + int64_t minimum = INT64_MAX; + std::vector best_values; // best model stored so far + double score (unsigned); // compute score from break count +#ifndef CADICAL_NDEBUG + std::vector current_best_model; // best model found so far +#endif + Walker (Internal *, int64_t limit); + void populate_table (double size); + void push_flipped (int flipped); + void save_walker_trail (bool); + void save_final_minimum (int64_t old_minimum); }; // These are in essence the CB values from Adrian Balint's thesis. They @@ -64,11 +90,18 @@ inline static double fitcbval (double size) { // Initialize the data structures for one local search round. -Walker::Walker (Internal *i, double size, int64_t l) +Walker::Walker (Internal *i, int64_t l) : internal (i), random (internal->opts.seed), // global random seed - propagations (0), limit (l) { + ticks (0), limit (l), best_trail_pos (-1) { random += internal->stats.walk.count; // different seed every time + flips.reserve (i->max_var / 4); + best_values.resize (i->max_var + 1, 0); +#ifndef CADICAL_NDEBUG + current_best_model.resize (i->max_var + 1, 0); +#endif +} +void Walker::populate_table (double size) { // This is the magic constant in ProbSAT (also called 'CB'), which we pick // according to the average size every second invocation and otherwise // just the default '2.0', which turns into the base '0.5'. @@ -87,6 +120,117 @@ Walker::Walker (Internal *i, double size, int64_t l) table.size ()); } +// Add the literal to flip to the queue + +void Walker::push_flipped (int flipped) { + LOG ("push literal %s on the flips", LOGLIT (flipped)); + CADICAL_assert (flipped); + if (best_trail_pos < 0) { + LOG ("not pushing flipped %s to already invalid trail", + LOGLIT (flipped)); + return; + } + + const size_t size_trail = flips.size (); + const size_t limit = internal->max_var / 4 + 1; + if (size_trail < limit) { + flips.push_back (flipped); + LOG ("pushed flipped %s to trail which now has size %zd", + LOGLIT (flipped), size_trail + 1); + return; + } + + if (best_trail_pos) { + LOG ("trail reached limit %zd but has best position %d", limit, + best_trail_pos); + save_walker_trail (true); + flips.push_back (flipped); + LOG ("pushed flipped %s to trail which now has size %zu", + LOGLIT (flipped), flips.size ()); + return; + } else { + LOG ("trail reached limit %zd without best position", limit); + flips.clear (); + LOG ("not pushing %s to invalidated trail", LOGLIT (flipped)); + best_trail_pos = -1; + LOG ("best trail position becomes invalid"); + } +} + +void Walker::save_walker_trail (bool keep) { + CADICAL_assert (best_trail_pos != -1); + CADICAL_assert ((size_t) best_trail_pos <= flips.size ()); +// CADICAL_assert (!keep || best_trail_pos == flips.size()); +#ifdef LOGGING + const size_t size_trail = flips.size (); +#endif + const int kept = flips.size () - best_trail_pos; + LOG ("saving %d values of flipped literals on trail of size %zd", + best_trail_pos, flips.size ()); + + const auto begin = flips.begin (); + const auto best = flips.begin () + best_trail_pos; + const auto end = flips.end (); + + auto it = begin; + for (; it != best; ++it) { + const int lit = *it; + CADICAL_assert (lit); + const signed char value = sign (lit); + const int idx = std::abs (lit); + best_values[idx] = value; + } + if (!keep) { + LOG ("no need to shift and keep remaining %u literals", kept); + return; + } + +#ifndef CADICAL_NDEBUG + for (auto v : internal->vars) { + if (internal->active (v)) + CADICAL_assert (best_values[v] == current_best_model[v]); + } +#endif + LOG ("flushed %u literals %.0f%% from trail", best_trail_pos, + percent (best_trail_pos, size_trail)); + CADICAL_assert (it == best); + auto jt = begin; + for (; it != end; ++it, ++jt) { + CADICAL_assert (jt <= it); + CADICAL_assert (it < end); + *jt = *it; + } + + CADICAL_assert ((int) (end - jt) == best_trail_pos); + CADICAL_assert ((int) (jt - begin) == kept); + flips.resize (kept); + LOG ("keeping %u literals %.0f%% on trail", kept, + percent (kept, size_trail)); + LOG ("reset best trail position to 0"); + best_trail_pos = 0; +} + +// finally export_ the final minimum +void Walker::save_final_minimum (int64_t old_init_minimum) { + CADICAL_assert (minimum <= old_init_minimum); +#ifdef CADICAL_NDEBUG + (void) old_init_minimum; +#endif + + if (!best_trail_pos || best_trail_pos == -1) + LOG ("minimum already saved"); + else + save_walker_trail (false); + + ++internal->stats.walk.improved; + for (auto v : internal->vars) { + if (best_values[v]) + internal->phases.saved[v] = best_values[v]; + else + CADICAL_assert (!internal->active (v)); + } + internal->copy_phases (internal->phases.prev); +} // The scores are tabulated for faster computation (to avoid 'pow'). inline double Walker::score (unsigned i) { @@ -97,15 +241,22 @@ inline double Walker::score (unsigned i) { /*------------------------------------------------------------------------*/ -Clause *Internal::walk_pick_clause (Walker &walker) { +ClauseOrBinary Internal::walk_pick_clause (Walker &walker) { require_mode (WALK); CADICAL_assert (!walker.broken.empty ()); int64_t size = walker.broken.size (); if (size > INT_MAX) size = INT_MAX; int pos = walker.random.pick_int (0, size - 1); - Clause *res = walker.broken[pos]; - LOG (res, "picking random position %d", pos); + ClauseOrBinary res = walker.broken[pos]; +#ifdef LOGGING + Clause *c; + if (!res.is_binary ()) + c = res.clause (); + else + c = res.tagged_binary ().d; + LOG (c, "picking random position %d", pos); +#endif return res; } @@ -114,12 +265,14 @@ Clause *Internal::walk_pick_clause (Walker &walker) { // Compute the number of clauses which would be become unsatisfied if 'lit' // is flipped and set to false. This is called the 'break-count' of 'lit'. -unsigned Internal::walk_break_value (int lit) { - +unsigned Internal::walk_break_value (int lit, int64_t &ticks) { require_mode (WALK); + START (walkbreak); CADICAL_assert (val (lit) > 0); + const int64_t oldticks = ticks; unsigned res = 0; // The computed break-count of 'lit'. + ticks += (1 + cache_lines (watches (lit).size (), sizeof (Clause *))); for (auto &w : watches (lit)) { CADICAL_assert (w.blit != lit); @@ -131,6 +284,11 @@ unsigned Internal::walk_break_value (int lit) { } Clause *c = w.clause; +#ifdef LOGGING + CADICAL_assert (c != dummy_binary); +#endif + ++ticks; + CADICAL_assert (lit == c->literals[0]); // Now try to find a second satisfied literal starting at 'literals[1]' @@ -168,9 +326,10 @@ unsigned Internal::walk_break_value (int lit) { *i = prev; prev = other; } - res++; // Literal 'lit' single satisfies clause 'c'. } + stats.ticks.walkbreak += (ticks - oldticks); + STOP (walkbreak); return res; } @@ -191,6 +350,8 @@ unsigned Internal::walk_break_value (int lit) { int Internal::walk_pick_lit (Walker &walker, Clause *c) { LOG ("picking literal by break-count"); CADICAL_assert (walker.scores.empty ()); + const int64_t old = walker.ticks; + walker.ticks += 1; double sum = 0; int64_t propagations = 0; for (const auto lit : *c) { @@ -201,16 +362,15 @@ int Internal::walk_pick_lit (Walker &walker, Clause *c) { } CADICAL_assert (active (lit)); propagations++; - unsigned tmp = walk_break_value (-lit); + unsigned tmp = walk_break_value (-lit, walker.ticks); double score = walker.score (tmp); LOG ("literal %d break-count %u score %g", lit, tmp, score); walker.scores.push_back (score); sum += score; } + (void) propagations; // TODO actually unused? LOG ("scored %zd literals", walker.scores.size ()); CADICAL_assert (!walker.scores.empty ()); - walker.propagations += propagations; - stats.propagations.walk += propagations; CADICAL_assert (walker.scores.size () <= (size_t) c->size); const double lim = sum * walker.random.generate_double (); LOG ("score sum %g limit %g", sum, lim); @@ -236,13 +396,78 @@ int Internal::walk_pick_lit (Walker &walker, Clause *c) { } walker.scores.clear (); LOG ("picking literal %d by break-count", res); + stats.ticks.walkpick += walker.ticks - old; + return res; +} + +int Internal::walk_pick_lit (Walker &walker, ClauseOrBinary c) { + if (c.is_binary ()) + return walk_pick_lit (walker, c.tagged_binary ()); + return walk_pick_lit (walker, c.clause ()); +} + +int Internal::walk_pick_lit (Walker &walker, const TaggedBinary c) { + LOG ("picking literal by break-count on binary clause [%" PRIu64 "]%s %s", + c.d->id, LOGLIT (c.lit), LOGLIT (c.other)); + CADICAL_assert (walker.scores.empty ()); + const int64_t old = walker.ticks; + double sum = 0; + int64_t propagations = 0; + const std::array clause = {c.lit, c.other}; + for (const auto lit : clause) { + CADICAL_assert (active (lit)); + if (var (lit).level == 1) { + LOG ("skipping assumption %d for scoring", -lit); + continue; + } + CADICAL_assert (active (lit)); + CADICAL_assert (val (lit) < 0); + propagations++; + unsigned tmp = walk_break_value (-lit, walker.ticks); + double score = walker.score (tmp); + LOG ("literal %d break-count %u score %g", lit, tmp, score); + walker.scores.push_back (score); + sum += score; + } + (void) propagations; // TODO unused? + LOG ("scored %zd literals", walker.scores.size ()); + CADICAL_assert (!walker.scores.empty ()); + CADICAL_assert (walker.scores.size () <= (size_t) 2); + const double lim = sum * walker.random.generate_double (); + LOG ("score sum %g limit %g", sum, lim); + const auto end = clause.end (); + auto i = clause.begin (); + auto j = walker.scores.begin (); + int res = 0; + for (;;) { + CADICAL_assert (i != end); + res = *i++; + if (var (res).level > 1) + break; + LOG ("skipping assumption %d without score", -res); + } + sum = *j++; + while (sum <= lim && i != end) { + res = *i++; + if (var (res).level == 1) { + LOG ("skipping assumption %d without score", -res); + continue; + } + sum += *j++; + } + CADICAL_assert (res); + walker.scores.clear (); + LOG ("picking literal %d by break-count", res); + stats.ticks.walkpick += walker.ticks - old; return res; } /*------------------------------------------------------------------------*/ -void Internal::walk_flip_lit (Walker &walker, int lit) { - +// flips a literal unless we run out of ticks. +bool Internal::walk_flip_lit (Walker &walker, int lit) { + START (walkflip); + const int64_t old = walker.ticks; require_mode (WALK); LOG ("flipping assign %d", lit); CADICAL_assert (val (lit) < 0); @@ -254,39 +479,74 @@ void Internal::walk_flip_lit (Walker &walker, int lit) { set_val (idx, tmp); CADICAL_assert (val (lit) > 0); + // we are going to need it anyway and it probably still is in memory + const Watches &ws = watches (-lit); + if (!ws.empty ()) { + const Watch &w = ws[0]; +#ifndef WIN32 + __builtin_prefetch (&w, 0, 1); +#endif + } + // Then remove 'c' and all other now satisfied (made) clauses. { // Simply go over all unsatisfied (broken) clauses. LOG ("trying to make %zd broken clauses", walker.broken.size ()); - // We need to measure (and bound) the memory accesses during traversing - // broken clauses in terms of 'propagations'. This is tricky since we - // are not actually propagating literals. Instead we use the clause - // variable 'ratio' as an approximation to the number of clauses used - // during propagating a literal. Note that we use a one-watch scheme. - // Accordingly the number of broken clauses traversed divided by that - // ratio is an approximation of the number of propagation this would - // correspond to (in terms of memory access). To eagerly update these - // statistics we simply increment the propagation counter after every - // 'ratio' traversed clause. These propagations are particularly - // expensive if the number of broken clauses is large which usually - // happens initially. - // - const double ratio = clause_variable_ratio (); const auto eou = walker.broken.end (); + // broken is in cache given how central it is... but not always (see the + // ncc problems). Value was heuristically determined to give reasonnable + // values. + walker.ticks += + 1 + cache_lines (walker.broken.size (), sizeof (Clause *)); auto j = walker.broken.begin (), i = j; -#ifdef LOGGING +#if defined(LOGGING) || !defined(CADICAL_NDEBUG) int64_t made = 0; #endif - int64_t count = 0; while (i != eou) { - Clause *d = *j++ = *i++; + ClauseOrBinary tagged = *j++ = *i++; - int *literals = d->literals, prev = 0; + if (tagged.is_binary ()) { + const TaggedBinary &b = tagged.tagged_binary (); + const int clit = b.lit; + const int other = b.other; + CADICAL_assert (val (clit) < 0 || val (other) < 0); +#if defined(LOGGING) + CADICAL_assert (b.d->literals[0] == clit || b.d->literals[1] == clit); + CADICAL_assert (b.d->literals[0] == other || b.d->literals[1] == other); +#endif + if (clit == lit || other == lit) { + LOG (b.d, "made"); + const int first_lit = lit; + const int second_lit = clit ^ lit ^ other; +#ifdef LOGGING + watch_binary_literal (first_lit, second_lit, b.d); +#else + // placeholder for the clause, does not matter + watch_binary_literal (first_lit, second_lit, dummy_binary); +#endif + ++walker.ticks; +#if defined(LOGGING) || !defined(CADICAL_NDEBUG) + made++; +#endif + j--; + } else { + LOG (b.d, "still broken"); + CADICAL_assert (val (clit) < 0 && val (other) < 0); + } + continue; + } + + // now the expansive part + Clause *d = tagged.clause (); + ++walker.ticks; + int *literals = d->literals; + LOG (d, "search for replacement"); + int prev = 0; // Find 'lit' in 'd'. // const int size = d->size; @@ -299,18 +559,17 @@ void Internal::walk_flip_lit (Walker &walker, int lit) { break; CADICAL_assert (val (other) < 0); } - // If 'lit' is in 'd' then move it to the front to watch it. // if (prev == lit) { literals[0] = lit; LOG (d, "made"); watch_literal (literals[0], literals[1], d); -#ifdef LOGGING + ++walker.ticks; +#if defined(LOGGING) || !defined(CADICAL_NDEBUG) made++; #endif j--; - } else { // Otherwise the clause is not satisfied, undo shift. for (int i = size - 1; i >= 0; i--) { @@ -319,41 +578,81 @@ void Internal::walk_flip_lit (Walker &walker, int lit) { prev = other; } } - - if (count--) - continue; - - // Update these counters eagerly. Otherwise if we delay the update - // until all clauses are traversed, interrupting the solver has a high - // chance of giving bogus statistics on the number of 'propagations' - // in 'walk', if it is interrupted in this loop. - - count = ratio; // Starting counting down again. - walker.propagations++; - stats.propagations.walk++; + LOG (d, "clause after undoing shift"); } - LOG ("made %" PRId64 " clauses by flipping %d", made, lit); + CADICAL_assert ((int64_t) (j - walker.broken.begin ()) + made == + (int64_t) walker.broken.size ()); walker.broken.resize (j - walker.broken.begin ()); + LOG ("made %" PRId64 " clauses by flipping %d, still %zu broken", made, + lit, walker.broken.size ()); +#ifndef CADICAL_NDEBUG + for (auto d : walker.broken) { + if (d.is_binary ()) { + const TaggedBinary &b = d.tagged_binary (); + CADICAL_assert (val (b.lit) < 0 && val (b.other) < 0); + } else { + for (auto lit : *d.clause ()) + CADICAL_assert (val (lit) < 0); + } + } +#endif + if (walker.ticks > walker.limit) { + STOP (walkflip); + return false; + } } + stats.ticks.walkflipbroken += walker.ticks - old; + + const int64_t old_after_broken = walker.ticks; + // Finally add all new unsatisfied (broken) clauses. { - walker.propagations++; // This really corresponds now to one - stats.propagations.walk++; // propagation (in a one-watch scheme). - #ifdef LOGGING int64_t broken = 0; #endif Watches &ws = watches (-lit); + // probably still in cache + walker.ticks += 1 + cache_lines (ws.size (), sizeof (Clause *)); LOG ("trying to break %zd watched clauses", ws.size ()); for (const auto &w : ws) { Clause *d = w.clause; - LOG (d, "unwatch %d in", -lit); + const bool binary = w.binary (); + if (binary) { + const int other = w.blit; + CADICAL_assert (w.blit != -lit); + if (val (other) > 0) { + LOG (d, "unwatch %d in", -lit); + watch_binary_literal (other, -lit, d); + ++walker.ticks; + continue; + } + LOG (d, "broken"); +#ifdef LOGGING + CADICAL_assert (d != dummy_binary); +#endif + walker.broken.push_back (TaggedBinary (d, -lit, other)); + ++walker.ticks; +#ifdef LOGGING + broken++; +#endif + continue; + } + + if (walker.ticks > walker.limit) { + STOP (walkflip); + return false; + } + // now the expansive part + CADICAL_assert (d->size != 2); + ++walker.ticks; int *literals = d->literals, replacement = 0, prev = -lit; - CADICAL_assert (literals[0] == -lit); + CADICAL_assert (d->size == w.size); const int size = d->size; + CADICAL_assert (literals[0] == -lit); + for (int i = 1; i < size; i++) { const int other = literals[i]; CADICAL_assert (active (other)); @@ -366,19 +665,23 @@ void Internal::walk_flip_lit (Walker &walker, int lit) { break; } if (replacement) { + CADICAL_assert (-lit != replacement); literals[1] = -lit; literals[0] = replacement; - CADICAL_assert (-lit != replacement); watch_literal (replacement, -lit, d); + ++walker.ticks; + LOG (d, "found replacement"); } else { for (int i = size - 1; i > 0; i--) { // undo shift const int other = literals[i]; literals[i] = prev; prev = other; } + CADICAL_assert (literals[0] == -lit); LOG (d, "broken"); walker.broken.push_back (d); + ++walker.ticks; #ifdef LOGGING broken++; #endif @@ -387,6 +690,10 @@ void Internal::walk_flip_lit (Walker &walker, int lit) { LOG ("broken %" PRId64 " clauses by flipping %d", broken, lit); ws.clear (); } + STOP (walkflip); + stats.ticks.walkflipWL += walker.ticks - old_after_broken; + stats.ticks.walkflip += walker.ticks - old; + return true; } /*------------------------------------------------------------------------*/ @@ -395,14 +702,58 @@ void Internal::walk_flip_lit (Walker &walker, int lit) { inline void Internal::walk_save_minimum (Walker &walker) { int64_t broken = walker.broken.size (); - if (broken >= stats.walk.minimum) + if (broken >= walker.minimum) return; - VERBOSE (3, "new global minimum %" PRId64 "", broken); - stats.walk.minimum = broken; + if (broken <= stats.walk.minimum) { + stats.walk.minimum = broken; + VERBOSE (3, "new global minimum %" PRId64 "", broken); + } else { + VERBOSE (3, "new walk minimum %" PRId64 "", broken); + } + + walker.minimum = broken; + +#ifndef CADICAL_NDEBUG for (auto i : vars) { const signed char tmp = vals[i]; if (tmp) - phases.min[i] = phases.saved[i] = tmp; + walker.current_best_model[i] = tmp; + } + if (walker.minimum == 0) { + for (auto c : clauses) { + if (c->garbage) + continue; + if (c->redundant) + continue; + int satisfied = 0; + for (const auto &lit : *c) { + const int tmp = internal->val (lit); + if (tmp > 0) { + LOG (c, "satisfied literal %d in", lit); + satisfied++; + } + } + CADICAL_assert (satisfied); + } + } +#endif + if (walker.best_trail_pos == -1) { + VERBOSE (3, "saving the new walk minimum %" PRId64 "", broken); + for (auto i : vars) { + const signed char tmp = vals[i]; + if (tmp) { + walker.best_values[i] = tmp; +#ifndef CADICAL_NDEBUG + CADICAL_assert (tmp == walker.current_best_model[i]); +#endif + } else { + CADICAL_assert (!active (i)); + } + } + walker.best_trail_pos = 0; + } else { + walker.best_trail_pos = walker.flips.size (); + LOG ("new best trail position %u", walker.best_trail_pos); } } @@ -410,13 +761,6 @@ inline void Internal::walk_save_minimum (Walker &walker) { int Internal::walk_round (int64_t limit, bool prev) { - backtrack (); - if (propagated < trail.size () && !propagate ()) { - LOG ("empty clause after root level propagation"); - learn_empty_clause (); - return 20; - } - stats.walk.count++; clear_watches (); @@ -435,34 +779,15 @@ int Internal::walk_round (int64_t limit, bool prev) { } #endif - PHASE ("walk", stats.walk.count, - "random walk limit of %" PRId64 " propagations", limit); - - // First compute the average clause size for picking the CB constant. - // - double size = 0; - int64_t n = 0; - for (const auto c : clauses) { - if (c->garbage) - continue; - if (c->redundant) { - if (!opts.walkredundant) - continue; - if (!likely_to_be_kept_clause (c)) - continue; - } - size += c->size; - n++; - } - double average_size = relative (size, n); - - PHASE ("walk", stats.walk.count, - "%" PRId64 " clauses average size %.2f over %d variables", n, - average_size, active ()); + PHASE ("walk", stats.walk.count, "random walk limit of %" PRId64 " ticks", + limit); // Instantiate data structures for this local search round. // - Walker walker (internal, average_size, limit); + Walker walker (internal, limit); +#ifndef CADICAL_QUIET + int old_global_minimum = stats.walk.minimum; +#endif bool failed = false; // Inconsistent assumptions? @@ -498,6 +823,8 @@ int Internal::walk_round (int64_t limit, bool prev) { if (!failed) { + // warmup stores the result in phases, not in target + const bool target = opts.warmup ? false : stable || opts.target == 2; for (auto idx : vars) { if (!active (idx)) { LOG ("skipping inactive variable %d", idx); @@ -512,7 +839,7 @@ int Internal::walk_round (int64_t limit, bool prev) { if (prev) tmp = phases.prev[idx]; if (!tmp) - tmp = sign (decide_phase (idx, true)); + tmp = sign (decide_phase (idx, target)); CADICAL_assert (tmp == 1 || tmp == -1); set_val (idx, tmp); CADICAL_assert (level == 2); @@ -524,6 +851,9 @@ int Internal::walk_round (int64_t limit, bool prev) { #ifdef LOGGING int64_t watched = 0; #endif + + double size = 0; + int64_t n = 0; for (const auto c : clauses) { if (c->garbage) @@ -539,6 +869,8 @@ int Internal::walk_round (int64_t limit, bool prev) { int satisfied = 0; // clause satisfied? int *lits = c->literals; + size += c->size; + n++; const int size = c->size; // Move to front satisfied literals and determine whether there @@ -565,16 +897,31 @@ int Internal::walk_round (int64_t limit, bool prev) { } if (satisfied) { - watch_literal (lits[0], lits[1], c); + LOG (c, "pushing to satisfied"); + if (c->size == 2) + watch_binary_literal (lits[0], lits[1], c); + else + watch_literal (lits[0], lits[1], c); #ifdef LOGGING watched++; #endif } else { CADICAL_assert (satisfiable); // at least one non-assumed variable ... LOG (c, "broken"); - walker.broken.push_back (c); + CADICAL_assert (c->size == size); + if (size == 2) + walker.broken.push_back (TaggedBinary (c)); + else + walker.broken.push_back (c); } } + + double average_size = relative (size, n); + walker.populate_table (average_size); + PHASE ("walk", stats.walk.count, + "%" PRId64 " clauses average size %.2f over %d variables", n, + average_size, active ()); + #ifdef LOGGING if (!failed) { int64_t broken = walker.broken.size (); @@ -586,13 +933,14 @@ int Internal::walk_round (int64_t limit, bool prev) { #endif } - int64_t old_global_minimum = stats.walk.minimum; + CADICAL_assert (failed || walker.table.size ()); int res; // Tells caller to continue with local search. if (!failed) { int64_t broken = walker.broken.size (); + int64_t initial_minimum = broken; PHASE ("walk", stats.walk.count, "starting with %" PRId64 " unsatisfied clauses " @@ -601,21 +949,25 @@ int Internal::walk_round (int64_t limit, bool prev) { stats.current.irredundant); walk_save_minimum (walker); + CADICAL_assert (stats.walk.minimum <= walker.minimum); int64_t minimum = broken; #ifndef CADICAL_QUIET int64_t flips = 0; #endif while (!terminated_asynchronously () && !walker.broken.empty () && - walker.propagations < walker.limit) { + walker.ticks < walker.limit) { #ifndef CADICAL_QUIET flips++; #endif stats.walk.flips++; stats.walk.broken += broken; - Clause *c = walk_pick_clause (walker); + ClauseOrBinary c = walk_pick_clause (walker); const int lit = walk_pick_lit (walker, c); - walk_flip_lit (walker, lit); + bool finished = walk_flip_lit (walker, lit); + if (!finished) + break; + walker.push_flipped (lit); broken = walker.broken.size (); LOG ("now have %" PRId64 " broken clauses in total", broken); if (broken >= minimum) @@ -626,33 +978,41 @@ int Internal::walk_round (int64_t limit, bool prev) { walk_save_minimum (walker); } - if (minimum < old_global_minimum) + walker.save_final_minimum (initial_minimum); + +#ifndef CADICAL_QUIET + if (minimum == initial_minimum) { + PHASE ("walk", internal->stats.walk.count, + "%sno improvement %" PRId64 "%s in %" PRId64 " flips and " + "%" PRId64 " ticks", + tout.bright_yellow_code (), minimum, tout.normal_code (), + flips, walker.ticks); + } else if (minimum < old_global_minimum) PHASE ("walk", stats.walk.count, "%snew global minimum %" PRId64 "%s in %" PRId64 " flips and " - "%" PRId64 " propagations", + "%" PRId64 " ticks", tout.bright_yellow_code (), minimum, tout.normal_code (), - flips, walker.propagations); + flips, walker.ticks); else PHASE ("walk", stats.walk.count, "best phase minimum %" PRId64 " in %" PRId64 " flips and " - "%" PRId64 " propagations", - minimum, flips, walker.propagations); + "%" PRId64 " ticks", + minimum, flips, walker.ticks); if (opts.profile >= 2) { - PHASE ("walk", stats.walk.count, - "%.2f million propagations per second", - relative (1e-6 * walker.propagations, - time () - profiles.walk.started)); + PHASE ("walk", stats.walk.count, "%.2f million ticks per second", + 1e-6 * + relative (walker.ticks, time () - profiles.walk.started)); PHASE ("walk", stats.walk.count, "%.2f thousand flips per second", relative (1e-3 * flips, time () - profiles.walk.started)); } else { - PHASE ("walk", stats.walk.count, "%.2f million propagations", - 1e-6 * walker.propagations); + PHASE ("walk", stats.walk.count, "%.2f ticks", 1e-6 * walker.ticks); PHASE ("walk", stats.walk.count, "%.2f thousand flips", 1e-3 * flips); } +#endif if (minimum > 0) { LOG ("minimum %" PRId64 " non-zero thus potentially continue", @@ -671,8 +1031,6 @@ int Internal::walk_round (int64_t limit, bool prev) { "aborted due to inconsistent assumptions"); } - copy_phases (phases.prev); - for (auto idx : vars) if (active (idx)) set_val (idx, 0); @@ -689,20 +1047,48 @@ int Internal::walk_round (int64_t limit, bool prev) { force_phase_messages = false; } #endif - + stats.ticks.walk += walker.ticks; return res; } void Internal::walk () { START_INNER_WALK (); - int64_t limit = stats.propagations.search; + + backtrack (); + if (propagated < trail.size () && !propagate ()) { + LOG ("empty clause after root level propagation"); + learn_empty_clause (); + STOP_INNER_WALK (); + return; + } + + int res = 0; + if (opts.warmup) + res = warmup (); + if (res) { + LOG ("stopping walk due to warmup"); + STOP_INNER_WALK (); + return; + } + const int64_t ticks = stats.ticks.search[0] + stats.ticks.search[1]; + int64_t limit = ticks - last.walk.ticks; + VERBOSE (2, + "walk scheduling: last %" PRId64 " current %" PRId64 + " delta %" PRId64, + last.walk.ticks, ticks, limit); + last.walk.ticks = ticks; limit *= 1e-3 * opts.walkeffort; if (limit < opts.walkmineff) limit = opts.walkmineff; - if (limit > opts.walkmaxeff) - limit = opts.walkmaxeff; + // local search is very cache friendly, so we actually really go over a + // lot of ticks + if (limit > 1e3 * opts.walkmaxeff) { + MSG ("reached maximum efficiency %" PRId64, limit); + limit = 1e3 * opts.walkmaxeff; + } (void) walk_round (limit, false); STOP_INNER_WALK (); + CADICAL_assert (!unsat); } } // namespace CaDiCaL diff --git a/src/sat/cadical/cadical_walk_full_occs.cpp b/src/sat/cadical/cadical_walk_full_occs.cpp new file mode 100644 index 000000000..67fbbb990 --- /dev/null +++ b/src/sat/cadical/cadical_walk_full_occs.cpp @@ -0,0 +1,972 @@ +#include "global.h" + +#include "internal.hpp" +#include + +ABC_NAMESPACE_IMPL_START + +namespace CaDiCaL { + +/*------------------------------------------------------------------------*/ + +// Random walk local search based on 'ProbSAT' ideas. +struct Tagged { + bool binary : 1; + unsigned counter_pos : 31; +#ifndef CADICAL_NDEBUG + Clause *c; +#endif + explicit Tagged () { CADICAL_assert (false); } + explicit Tagged (Clause *d, unsigned pos) + : binary (d->size == 2), counter_pos (pos) { + CADICAL_assert ((pos & (1 << 31)) == 0); +#ifndef CADICAL_NDEBUG + c = d; +#endif + } +}; + +struct Counter { + unsigned count; // number of false literals + unsigned pos; // pos in the broken clauses + Clause *clause; // pointer to the clause itself + explicit Counter (unsigned c, unsigned p, Clause *d) + : count (c), pos (p), clause (d) {} + explicit Counter (unsigned c, Clause *d) + : count (c), pos (UINT32_MAX), clause (d) {} +}; + +struct WalkerFO { + + Internal *internal; + + // for efficiency, storing the model each time an improvement is + // found is too costly. Instead we store some of the flips since + // last time and the position of the best model found so far. + Random random; // local random number generator + int64_t ticks; // ticks to approximate run time + int64_t limit; // limit on number of propagations + vector broken; // currently unsatisfied clauses + double epsilon; // smallest considered score + vector table; // break value to score table + vector scores; // scores of candidate literals + std::vector + flips; // remember the flips compared to the last best saved model + int best_trail_pos; + int64_t minimum = INT64_MAX; + std::vector best_values; // best model found so far + double score (unsigned); // compute score from break count + + std::vector tclauses; + std::vector> tcounters; + + using TOccs = std::vector; + TOccs &occs (int lit) { + const int idx = internal->vlit (lit); + CADICAL_assert ((size_t) idx < tcounters.size ()); + return tcounters[idx]; + } + const TOccs &occs (int lit) const { + const int idx = internal->vlit (lit); + CADICAL_assert ((size_t) idx < tcounters.size ()); + return tcounters[idx]; + } + void connect_clause (int lit, Clause *clause, unsigned pos) { + CADICAL_assert (pos < tclauses.size ()); + CADICAL_assert (tclauses[pos].clause == clause); + LOG (clause, "connecting clause on %d with already in occurrences %zu", + lit, occs (lit).size ()); + occs (lit).push_back (Tagged (clause, pos)); + } + void connect_clause (Clause *clause, unsigned pos) { + CADICAL_assert (pos < tclauses.size ()); + CADICAL_assert (tclauses[pos].clause == clause); + for (auto lit : *clause) + connect_clause (lit, clause, pos); + } + + void check_occs () const { +#ifndef CADICAL_NDEBUG + for (auto lit : internal->lits) { + for (auto w : occs (lit)) { + CADICAL_assert (w.counter_pos < tclauses.size ()); + } + } + + unsigned unsatisfied = 0; + for (auto c : tclauses) { + unsigned count = 0; + LOG (c.clause, "checking clause with counter %d:", c.count); + for (auto lit : *c.clause) { + if (internal->val (lit) > 0) + ++count; + } + CADICAL_assert (count == c.count); + if (!count) + ++unsatisfied; + } + CADICAL_assert (broken.size () == unsatisfied); +#endif + } + void check_broken () const { +#ifndef CADICAL_NDEBUG + for (size_t i = 0; i < broken.size (); ++i) { + const Tagged t = broken[i]; + CADICAL_assert (t.c); + CADICAL_assert (t.counter_pos < tclauses.size ()); + CADICAL_assert (tclauses[t.counter_pos].clause == t.c); + for (auto lit : *t.c) { + CADICAL_assert (internal->val (lit) < 0); + } + } +#endif + } + + void check_all () const { + check_broken (); + check_occs (); + } + + static const uint32_t invalid_position = UINT32_MAX; + void make_clause (Tagged t); + + WalkerFO (Internal *, double size, int64_t limit); + void push_flipped (int flipped); + void save_walker_trail (bool); + void save_final_minimum (int64_t old_minimum); + void make_clauses_along_occurrences (int lit); + void make_clauses_along_unsatisfied (int lit); + void make_clauses (int lit); + void break_clauses (int lit); + unsigned walk_full_occs_pick_clause (); + void walk_full_occs_flip_lit (int lit); + int walk_full_occs_pick_lit (Clause *); + unsigned walk_full_occs_break_value (int lit); +}; + +// These are in essence the CB values from Adrian Balint's thesis. They +// denote the inverse 'cb' of the base 'b' of the (probability) weight +// 'b^-i' for picking a literal with the break value 'i' (first column is +// the 'size', second the 'CB' value). + +static double cbvals[][2] = { + {0.0, 2.00}, {3.0, 2.50}, {4.0, 2.85}, {5.0, 3.70}, + {6.0, 5.10}, {7.0, 7.40}, // Adrian has '5.4', but '7.4' looks better. +}; + +static const int ncbvals = sizeof cbvals / sizeof cbvals[0]; + +// We interpolate the CB values for uniform random SAT formula to the non +// integer situation of average clause size by piecewise linear functions. +// +// y2 - y1 +// ------- * (x - x1) + y1 +// x2 - x1 +// +// where 'x' is the average size of clauses and 'y' the CB value. + +inline static double fitcbval (double size) { + int i = 0; + while (i + 2 < ncbvals && + (cbvals[i][0] > size || cbvals[i + 1][0] < size)) + i++; + const double x2 = cbvals[i + 1][0], x1 = cbvals[i][0]; + const double y2 = cbvals[i + 1][1], y1 = cbvals[i][1]; + const double dx = x2 - x1, dy = y2 - y1; + CADICAL_assert (dx); + const double res = dy * (size - x1) / dx + y1; + CADICAL_assert (res > 0); + return res; +} + +// Initialize the data structures for one local search round. + +WalkerFO::WalkerFO (Internal *i, double size, int64_t l) + : internal (i), random (internal->opts.seed), // global random seed + ticks (0), limit (l), best_trail_pos (-1) { + random += internal->stats.walk.count; // different seed every time + flips.reserve (i->max_var / 4); + best_values.resize (i->max_var + 1, 0); + tcounters.resize (i->max_var * 2 + 2); + + // This is the magic constant in ProbSAT (also called 'CB'), which we pick + // according to the average size every second invocation and otherwise + // just the default '2.0', which turns into the base '0.5'. + // + const bool use_size_based_cb = (internal->stats.walk.count & 1); + const double cb = use_size_based_cb ? fitcbval (size) : 2.0; + CADICAL_assert (cb); + const double base = 1 / cb; // scores are 'base^0,base^1,base^2,... + + double next = 1; + for (epsilon = next; next; next = epsilon * base) + table.push_back (epsilon = next); + + PHASE ("walk", internal->stats.walk.count, + "CB %.2f with inverse %.2f as base and table size %zd", cb, base, + table.size ()); +} + +// Add the literal to flip to the queue + +void WalkerFO::push_flipped (int flipped) { + LOG ("push literal %s on the flips", LOGLIT (flipped)); + CADICAL_assert (flipped); + if (best_trail_pos < 0) { + LOG ("not pushing flipped %s to already invalid trail", + LOGLIT (flipped)); + return; + } + + const size_t size_trail = flips.size (); + const size_t limit = internal->max_var / 4 + 1; + if (size_trail < limit) { + flips.push_back (flipped); + LOG ("pushed flipped %s to trail which now has size %zd", + LOGLIT (flipped), size_trail + 1); + return; + } + + if (best_trail_pos) { + LOG ("trail reached limit %zd but has best position %d", limit, + best_trail_pos); + save_walker_trail (true); + flips.push_back (flipped); + LOG ("pushed flipped %s to trail which now has size %zu", + LOGLIT (flipped), flips.size ()); + return; + } else { + LOG ("trail reached limit %zd without best position", limit); + flips.clear (); + LOG ("not pushing %s to invalidated trail", LOGLIT (flipped)); + best_trail_pos = -1; + LOG ("best trail position becomes invalid"); + } +} + +void WalkerFO::save_walker_trail (bool keep) { + CADICAL_assert (best_trail_pos != -1); + CADICAL_assert ((size_t) best_trail_pos <= flips.size ()); +#ifdef LOGGING + const size_t size_trail = flips.size (); +#endif + const int kept = flips.size () - best_trail_pos; + LOG ("saving %d values of flipped literals on trail of size %zd", + best_trail_pos, size_trail); + + const auto begin = flips.begin (); + const auto best = flips.begin () + best_trail_pos; + const auto end = flips.end (); + + auto it = begin; + for (; it != best; ++it) { + const int lit = *it; + CADICAL_assert (lit); + const signed char value = sign (lit); + const int idx = std::abs (lit); + best_values[idx] = value; + } + if (!keep) { + LOG ("no need to shift and keep remaining %u literals", kept); + return; + } + +#ifndef CADICAL_NDEBUG + for (auto v : internal->vars) { + if (internal->active (v)) + CADICAL_assert (best_values[v] == internal->phases.saved[v]); + } +#endif + LOG ("flushed %u literals %.0f%% from trail", best_trail_pos, + percent (best_trail_pos, size_trail)); + CADICAL_assert (it == best); + auto jt = begin; + for (; it != end; ++it, ++jt) { + CADICAL_assert (jt <= it); + CADICAL_assert (it < end); + *jt = *it; + } + + CADICAL_assert ((int) (end - jt) == best_trail_pos); + CADICAL_assert ((int) (jt - begin) == kept); + flips.resize (kept); + LOG ("keeping %u literals %.0f%% on trail", kept, + percent (kept, size_trail)); + LOG ("reset best trail position to 0"); + best_trail_pos = 0; +} + +// finally export_ the final minimum +void WalkerFO::save_final_minimum (int64_t old_init_minimum) { + CADICAL_assert (minimum <= old_init_minimum); +#ifdef CADICAL_NDEBUG + (void) old_init_minimum; +#endif + + if (!best_trail_pos || best_trail_pos == -1) + LOG ("minimum already saved"); + else + save_walker_trail (false); + + ++internal->stats.walk.improved; + for (auto v : internal->vars) { + if (best_values[v]) + internal->phases.saved[v] = best_values[v]; + else + CADICAL_assert (!internal->active (v)); + } + internal->copy_phases (internal->phases.prev); +} +// The scores are tabulated for faster computation (to avoid 'pow'). + +inline double WalkerFO::score (unsigned i) { + const double res = (i < table.size () ? table[i] : epsilon); + LOG ("break %u mapped to score %g", i, res); + return res; +} + +/*------------------------------------------------------------------------*/ + +unsigned WalkerFO::walk_full_occs_pick_clause () { + internal->require_mode (internal->WALK); + CADICAL_assert (!broken.empty ()); + int64_t size = broken.size (); + if (size > INT_MAX) + size = INT_MAX; + int pos = random.pick_int (0, size - 1); + unsigned res = broken[pos].counter_pos; + LOG (tclauses[res].clause, "picking random position %d", pos); + return res; +} + +/*------------------------------------------------------------------------*/ + +// Compute the number of clauses which would be become unsatisfied if 'lit' +// is flipped and set to false. This is called the 'break-count' of 'lit'. + +unsigned WalkerFO::walk_full_occs_break_value (int lit) { + + internal->require_mode (internal->WALK); + CADICAL_assert (internal->val (lit) > 0); + START (walkbreak); + + const uint64_t old = ticks; + unsigned res = 0; // The computed break-count of 'lit'. + ticks += + (1 + internal->cache_lines (occs (lit).size (), sizeof (Clause *))); + + for (const auto &w : occs (lit)) { + const unsigned ref = w.counter_pos; + CADICAL_assert (ref < tclauses.size ()); + res += (tclauses[ref].count == 1); + } + + internal->stats.ticks.walkbreak += ticks - old; + STOP (walkbreak); + return res; +} + +/*------------------------------------------------------------------------*/ + +// Given an unsatisfied clause 'c', in which we want to flip a literal, we +// first determine the exponential score based on the break-count of its +// literals and then sample the literals based on these scores. The CB +// value is smaller than one and thus the score is exponentially decreasing +// with the break-count increasing. The sampling works as in 'ProbSAT' and +// 'YalSAT' by summing up the scores and then picking a random limit in the +// range of zero to the sum, then summing up the scores again and picking +// the first literal which reaches the limit. Note, that during incremental +// SAT solving we can not flip assumed variables. Those are assigned at +// decision level one, while the other variables are assigned at two. + +int WalkerFO::walk_full_occs_pick_lit (Clause *c) { + START (walkpick); + LOG ("picking literal by break-count"); + CADICAL_assert (scores.empty ()); + const int64_t old = ++ticks; + double sum = 0; + int64_t propagations = 0; + for (const auto lit : *c) { + CADICAL_assert (internal->active (lit)); + if (internal->var (lit).level == 1) { + LOG ("skipping assumption %d for scoring", -lit); + continue; + } + propagations++; + unsigned tmp = walk_full_occs_break_value (-lit); + double score = this->score (tmp); + LOG ("literal %d break-count %u score %g", lit, tmp, score); + scores.push_back (score); + sum += score; + } + (void) propagations; // TODO unused? + LOG ("scored %zd literals", scores.size ()); + CADICAL_assert (!scores.empty ()); + CADICAL_assert (this->scores.size () <= (size_t) c->size); + const double lim = sum * random.generate_double (); + LOG ("score sum %g limit %g", sum, lim); + + const auto end = c->end (); + auto i = c->begin (); + auto j = scores.begin (); + int res; + for (;;) { + CADICAL_assert (i != end); + res = *i++; + if (internal->var (res).level > 1) + break; + LOG ("skipping assumption %d without score", -res); + } + sum = *j++; + while (sum <= lim && i != end) { + res = *i++; + if (internal->var (res).level == 1) { + LOG ("skipping assumption %d without score", -res); + continue; + } + sum += *j++; + } + scores.clear (); + LOG ("picking literal %d by break-count", res); + internal->stats.ticks.walkpick += ticks - old; + STOP (walkpick); + return res; +} + +/*------------------------------------------------------------------------*/ +void WalkerFO::make_clause (Tagged t) { + CADICAL_assert (t.counter_pos < tclauses.size ()); + // TODO invalidate position in 'unsatisfied' + auto count = tclauses[t.counter_pos].count++; + if (count) { + LOG (tclauses[t.counter_pos].clause, + "already made with counter %d at position %d", + tclauses[t.counter_pos].count, tclauses[t.counter_pos].pos); + CADICAL_assert (tclauses[t.counter_pos].clause == t.c); + CADICAL_assert (tclauses[t.counter_pos].pos == WalkerFO::invalid_position); + return; + } + LOG (tclauses[t.counter_pos].clause, + "make with counter %d at position %d", tclauses[t.counter_pos].count, + tclauses[t.counter_pos].pos); + CADICAL_assert (tclauses[t.counter_pos].pos != WalkerFO::invalid_position); + CADICAL_assert (tclauses[t.counter_pos].pos < broken.size ()); + ++ticks; + auto last = broken.back (); +#ifndef CADICAL_NDEBUG + CADICAL_assert (tclauses[t.counter_pos].clause == t.c); + CADICAL_assert (last.counter_pos < tclauses.size ()); + CADICAL_assert (tclauses[last.counter_pos].clause == last.c); +#endif + unsigned pos = tclauses[t.counter_pos].pos; + CADICAL_assert (pos < broken.size ()); + broken[pos] = last; + // the order is important + tclauses[last.counter_pos].pos = pos; + tclauses[t.counter_pos].pos = WalkerFO::invalid_position; + broken.pop_back (); +} + +void WalkerFO::make_clauses_along_occurrences (int lit) { + const auto &occs = this->occs (lit); + LOG ("making clauses with %s along %zu occurrences", LOGLIT (lit), + occs.size ()); + CADICAL_assert (internal->val (lit) > 0); + size_t made = 0; + ticks += (1 + internal->cache_lines (occs.size (), sizeof (Clause *))); + + for (auto c : occs) { +#if 0 + // only works if make is after break... but we don't want that + if (broken.empty()) { + LOG ("early abort: satisfiable!"); + return; + } +#endif + this->make_clause (c); + made++; + } + LOG ("made %zu clauses by flipping %d, still %zu broken", made, lit, + broken.size ()); + LOG ("made %zu clauses with flipped %s", made, LOGLIT (lit)); + (void) made; +} + +void WalkerFO::make_clauses_along_unsatisfied (int lit) { + LOG ("making clauses with %s along %zu unsatisfied", LOGLIT (lit), + this->broken.size ()); + CADICAL_assert (internal->val (lit) > 0); + size_t made = 0; + // TODO flush made clauses from 'unsatisfied' directly. + // TODO 'stats.make_visited++', 'made++' appropriately. + size_t j = 0; + const size_t size = this->broken.size (); + ticks += + (1 + internal->cache_lines (this->broken.size (), sizeof (Clause *))); + for (size_t i = 0, j = 0; i < size; ++i) { + CADICAL_assert (i >= j); + const auto &c = this->broken[i]; + Counter &d = this->tclauses[c.counter_pos]; + this->broken[j++] = this->broken[i]; + CADICAL_assert (d.pos != WalkerFO::invalid_position); + for (auto other : *d.clause) { + if (lit == other) { + ++made; + --j; + ++d.count; + LOG (d.clause, "made with count %d", d.count); + d.pos = WalkerFO::invalid_position; + break; + } + } + if (d.pos != WalkerFO::invalid_position) + LOG (d.clause, "still broken"); + CADICAL_assert (made + j == i + 1); // assertions holds after incrementing 'i' + } + CADICAL_assert (j <= size); + this->broken.resize (j); + LOG ("made %zu clauses with flipped %s", made, LOGLIT (lit)); + (void) made; +} + +void WalkerFO::make_clauses (int lit) { + START (walkflipWL); + const int64_t old = ticks; + // In babywalk this work because there are not counter + // if (this->occs(lit).size() > broken.size()) + // make_clauses_along_unsatisfied(lit); + // else + make_clauses_along_occurrences (lit); + internal->stats.ticks.walkflipWL += ticks - old; + STOP (walkflipWL); +} + +void WalkerFO::break_clauses (int lit) { + START (walkflipbroken); + const int64_t old = ticks; + LOG ("breaking clauses on %s", LOGLIT (lit)); + // Finally add all new unsatisfied (broken) clauses. + +#ifdef LOGGING + int64_t broken = 0; +#endif + const WalkerFO::TOccs &ws = occs (lit); + ticks += (1 + internal->cache_lines (ws.size (), sizeof (Clause *))); + + LOG ("trying to break %zd clauses", ws.size ()); + + for (const auto &w : ws) { + unsigned pos = w.counter_pos; + Counter &d = tclauses[pos]; +#ifndef CADICAL_NDEBUG + LOG (d.clause, "trying to break"); +#endif + if (--d.count) + continue; + d.pos = this->broken.size (); + ++ticks; + this->broken.push_back (w); +#ifdef LOGGING + broken++; +#endif + } + LOG ("broken %" PRId64 " clauses by flipping %d", broken, lit); + internal->stats.ticks.walkflipbroken += ticks - old; + STOP (walkflipbroken); +} + +void WalkerFO::walk_full_occs_flip_lit (int lit) { + + internal->require_mode (internal->WALK); + LOG ("flipping assign %d", lit); + CADICAL_assert (internal->val (lit) < 0); + const int64_t old = ticks; + + // First flip the literal value. + // + const int tmp = sign (lit); + const int idx = abs (lit); + internal->set_val (idx, tmp); + CADICAL_assert (internal->val (lit) > 0); + + make_clauses (lit); + break_clauses (-lit); + + if (!broken.empty ()) + check_all (); + internal->stats.ticks.walkflip += ticks - old; +} + +/*------------------------------------------------------------------------*/ + +// Check whether to save the current phases as new global minimum. + +inline void Internal::walk_full_occs_save_minimum (WalkerFO &walker) { + int64_t broken = walker.broken.size (); + if (broken >= walker.minimum) + return; + if (broken <= stats.walk.minimum) { + stats.walk.minimum = broken; + VERBOSE (3, "new global minimum %" PRId64 "", broken); + } else { + VERBOSE (3, "new walk minimum %" PRId64 "", broken); + } + + walker.minimum = broken; + +#ifndef CADICAL_NDEBUG + for (auto i : vars) { + const signed char tmp = vals[i]; + if (tmp) + phases.saved[i] = tmp; + } +#endif + + if (walker.best_trail_pos == -1) { + for (auto i : vars) { + const signed char tmp = vals[i]; + if (tmp) { + walker.best_values[i] = tmp; +#ifndef CADICAL_NDEBUG + CADICAL_assert (tmp == phases.saved[i]); +#endif + } + } + walker.best_trail_pos = 0; + } else { + walker.best_trail_pos = walker.flips.size (); + LOG ("new best trail position %u", walker.best_trail_pos); + } +} + +/*------------------------------------------------------------------------*/ + +int Internal::walk_full_occs_round (int64_t limit, bool prev) { + + stats.walk.count++; + + reset_watches (); + + // Remove all fixed variables first (assigned at decision level zero). + // + if (last.collect.fixed < stats.all.fixed) + garbage_collection (); + +#ifndef CADICAL_QUIET + // We want to see more messages during initial local search. + // + if (localsearching) { + CADICAL_assert (!force_phase_messages); + force_phase_messages = true; + } +#endif + + PHASE ("walk", stats.walk.count, + "random walk limit of %" PRId64 " propagations", limit); + + // First compute the average clause size for picking the CB constant. + // + double size = 0; + int64_t n = 0; + for (const auto c : clauses) { + if (c->garbage) + continue; + if (c->redundant) { + if (!opts.walkredundant) + continue; + if (!likely_to_be_kept_clause (c)) + continue; + } + size += c->size; + n++; + } + double average_size = relative (size, n); + + PHASE ("walk", stats.walk.count, + "%" PRId64 " clauses average size %.2f over %d variables", n, + average_size, active ()); + + // Instantiate data structures for this local search round. + // + WalkerFO walker (internal, average_size, limit); + + bool failed = false; // Inconsistent assumptions? + + level = 1; // Assumed variables assigned at level 1. + + if (assumptions.empty ()) { + LOG ("no assumptions so assigning all variables to decision phase"); + } else { + LOG ("assigning assumptions to their forced phase first"); + for (const auto lit : assumptions) { + signed char tmp = val (lit); + if (tmp > 0) + continue; + if (tmp < 0) { + LOG ("inconsistent assumption %d", lit); + failed = true; + break; + } + if (!active (lit)) + continue; + tmp = sign (lit); + const int idx = abs (lit); + LOG ("initial assign %d to assumption phase", tmp < 0 ? -idx : idx); + set_val (idx, tmp); + CADICAL_assert (level == 1); + var (idx).level = 1; + } + if (!failed) + LOG ("now assigning remaining variables to their decision phase"); + } + + level = 2; // All other non assumed variables assigned at level 2. + + if (!failed) { + + const bool target = opts.warmup ? false : stable || opts.target == 2; + for (auto idx : vars) { + if (!active (idx)) { + LOG ("skipping inactive variable %d", idx); + continue; + } + if (vals[idx]) { + CADICAL_assert (var (idx).level == 1); + LOG ("skipping assumed variable %d", idx); + continue; + } + int tmp = 0; + if (prev) + tmp = phases.prev[idx]; + if (!tmp) + tmp = sign (decide_phase (idx, target)); + CADICAL_assert (tmp == 1 || tmp == -1); + set_val (idx, tmp); + CADICAL_assert (level == 2); + var (idx).level = 2; + walker.best_values[idx] = tmp; + LOG ("initial assign %d to decision phase", tmp < 0 ? -idx : idx); + } + + LOG ("watching satisfied and registering broken clauses"); +#ifdef LOGGING + int64_t watched = 0; +#endif + for (const auto c : clauses) { + + if (c->garbage) + continue; + if (c->redundant) { + if (!opts.walkredundant) + continue; + if (!likely_to_be_kept_clause (c)) + continue; + } + + bool satisfiable = false; // contains not only assumptions + int satisfied = 0; // clause satisfied? + + int *lits = c->literals; + const int size = c->size; + + // Move to front satisfied literals and determine whether there + // is at least one (non-assumed) literal that can be flipped. + // + for (int i = 0; i < size; i++) { + const int lit = lits[i]; + CADICAL_assert (active (lit)); // Due to garbage collection. + if (val (lit) > 0) { + swap (lits[satisfied], lits[i]); + if (!satisfied++) + LOG ("first satisfying literal %d", lit); + } else if (!satisfiable && var (lit).level > 1) { + LOG ("non-assumption potentially satisfying literal %d", lit); + satisfiable = true; + } + } + + if (!satisfied && !satisfiable) { + LOG (c, "due to assumptions unsatisfiable"); + LOG ("stopping local search since assumptions falsify a clause"); + failed = true; + break; + } + + unsigned pos = walker.tclauses.size (); + walker.tclauses.push_back (Counter (satisfied, c)); + walker.connect_clause (c, pos); + + if (!satisfied) { + CADICAL_assert (satisfiable); // at least one non-assumed variable ... + LOG (c, "broken"); + walker.tclauses[pos].pos = walker.broken.size (); + walker.broken.push_back (Tagged (c, pos)); + } else { +#ifdef LOGGING + watched++; // to be able to compare the number with walk +#endif + } + } +#ifdef LOGGING + if (!failed) { + int64_t broken = walker.broken.size (); + int64_t total = watched + broken; + MSG ("watching %" PRId64 " clauses %.0f%% " + "out of %" PRId64 " (watched and broken)", + watched, percent (watched, total), total); + } +#endif + } + walker.check_all (); + int res; // Tells caller to continue with local search. + + if (!failed) { + + int64_t broken = walker.broken.size (); + int64_t initial_minimum = broken; + + PHASE ("walk", stats.walk.count, + "starting with %" PRId64 " unsatisfied clauses " + "(%.0f%% out of %" PRId64 ")", + broken, percent (broken, stats.current.irredundant), + stats.current.irredundant); + + walk_full_occs_save_minimum (walker); + CADICAL_assert (stats.walk.minimum <= walker.minimum); + + int64_t minimum = broken; +#ifndef CADICAL_QUIET + int64_t flips = 0; +#endif + while (!terminated_asynchronously () && !walker.broken.empty () && + walker.ticks < walker.limit) { +#ifndef CADICAL_QUIET + flips++; +#endif + stats.walk.flips++; + stats.walk.broken += broken; + unsigned pos = walker.walk_full_occs_pick_clause (); + Clause *c = walker.tclauses[pos].clause; + const int lit = walker.walk_full_occs_pick_lit (c); + walker.walk_full_occs_flip_lit (lit); + walker.push_flipped (lit); + broken = walker.broken.size (); + LOG ("now have %" PRId64 " broken clauses in total", broken); + if (broken >= minimum) + continue; + minimum = broken; + VERBOSE (3, "new phase minimum %" PRId64 " after %" PRId64 " flips", + minimum, flips); + walk_full_occs_save_minimum (walker); + } + + walker.save_final_minimum (initial_minimum); +#ifndef CADICAL_QUIET + if (minimum == initial_minimum) { + PHASE ("walk", internal->stats.walk.count, + "%sno improvement %" PRId64 "%s in %" PRId64 " flips and " + "%" PRId64 " ticks", + tout.bright_yellow_code (), minimum, tout.normal_code (), + flips, walker.ticks); + } else { + PHASE ("walk", internal->stats.walk.count, + "best phase minimum %" PRId64 " in %" PRId64 " flips and " + "%" PRId64 " ticks", + minimum, flips, walker.ticks); + } +#endif + + if (opts.profile >= 2) { + PHASE ("walk", stats.walk.count, "%.2f million ticks per second", + 1e-6 * + relative (walker.ticks, time () - profiles.walk.started)); + + PHASE ("walk", stats.walk.count, "%.2f thousand flips per second", + relative (1e-3 * flips, time () - profiles.walk.started)); + + } else { + PHASE ("walk", stats.walk.count, "%.2f ticks", 1e-6 * walker.ticks); + + PHASE ("walk", stats.walk.count, "%.2f thousand flips", 1e-3 * flips); + } + + if (minimum > 0) { + LOG ("minimum %" PRId64 " non-zero thus potentially continue", + minimum); + res = 0; + } else { + LOG ("minimum is zero thus stop local search"); + res = 10; + } + + } else { + + res = 20; + + PHASE ("walk", stats.walk.count, + "aborted due to inconsistent assumptions"); + } + + for (auto idx : vars) + if (active (idx)) + set_val (idx, 0); + + CADICAL_assert (level == 2); + level = 0; + + init_watches (); + connect_watches (); + +#ifndef CADICAL_QUIET + if (localsearching) { + CADICAL_assert (force_phase_messages); + force_phase_messages = false; + } +#endif + stats.ticks.walk += walker.ticks; + return res; +} + +void Internal::walk_full_occs () { + START_INNER_WALK (); + + backtrack (); + if (propagated < trail.size () && !propagate ()) { + LOG ("empty clause after root level propagation"); + learn_empty_clause (); + STOP_INNER_WALK (); + return; + } + + int res = 0; + if (opts.warmup) + res = warmup (); + if (res) { + LOG ("stopping walk due to warmup"); + STOP_INNER_WALK (); + return; + } + const int64_t ticks = stats.ticks.search[0] + stats.ticks.search[1]; + int64_t limit = ticks - last.walk.ticks; + VERBOSE (2, + "walk scheduling: last %" PRId64 " current %" PRId64 + " delta %" PRId64, + last.walk.ticks, ticks, limit); + last.walk.ticks = ticks; + limit *= 1e-3 * opts.walkeffort; + if (limit < opts.walkmineff) + limit = opts.walkmineff; + // local search is very cache friendly, so we actually really go over a + // lot of ticks + if (limit > 1e3 * opts.walkmaxeff) { + MSG ("reached maximum efficiency %" PRId64, limit); + limit = 1e3 * opts.walkmaxeff; + } + (void) walk_full_occs_round (limit, false); + STOP_INNER_WALK (); + CADICAL_assert (!unsat); +} + +} // namespace CaDiCaL + +ABC_NAMESPACE_IMPL_END diff --git a/src/sat/cadical/cadical_warmup.cpp b/src/sat/cadical/cadical_warmup.cpp new file mode 100644 index 000000000..9f2a06bb8 --- /dev/null +++ b/src/sat/cadical/cadical_warmup.cpp @@ -0,0 +1,404 @@ +#include "global.h" + +#include "internal.hpp" + +ABC_NAMESPACE_IMPL_START + +namespace CaDiCaL { + +// The idea of warmup is to reuse the strength of CDCL, namely +// propagating, before calling random walk that is not good at +// propagating long chains. Therefore, we propagate (ignoring all conflicts) +// discovered along the way. +// The asignmend is the same as the normal assignment however, it updates +// the target phases so that local search can pick them up later + +// specific warmup version with saving of the target. +inline void Internal::warmup_assign (int lit, Clause *reason) { + + CADICAL_assert (level); // no need to learn unit clauses here + require_mode (SEARCH); + + const int idx = vidx (lit); + CADICAL_assert (reason != external_reason); + CADICAL_assert (!vals[idx]); + CADICAL_assert (!flags (idx).eliminated () || reason == decision_reason); + CADICAL_assert (!searching_lucky_phases); + CADICAL_assert (lrat_chain.empty ()); + Var &v = var (idx); + int lit_level; + CADICAL_assert ( + !(reason == external_reason && + ((size_t) level <= assumptions.size () + (!!constraint.size ())))); + CADICAL_assert (reason); + CADICAL_assert (level || reason == decision_reason); + // we purely assign in order here + lit_level = level; + + v.level = lit_level; + v.trail = trail.size (); + v.reason = reason; + CADICAL_assert ((int) num_assigned < max_var); + CADICAL_assert (num_assigned == trail.size ()); + num_assigned++; + const signed char tmp = sign (lit); + phases.saved[idx] = tmp; + set_val (idx, tmp); + CADICAL_assert (val (lit) > 0); + CADICAL_assert (val (-lit) < 0); + + trail.push_back (lit); +#ifdef LOGGING + if (!lit_level) + LOG ("root-level unit assign %d @ 0", lit); + else + LOG (reason, "search assign %d @ %d", lit, lit_level); +#endif + + CADICAL_assert (watching ()); + const Watches &ws = watches (-lit); + if (!ws.empty ()) { + const Watch &w = ws[0]; +#ifndef WIN32 + __builtin_prefetch (&w, 0, 1); +#endif + } +} + +void Internal::warmup_propagate_beyond_conflict () { + + CADICAL_assert (!unsat); + + START (propagate); + CADICAL_assert (!ignore); + + int64_t before = propagated; + + while (propagated != trail.size ()) { + + const int lit = -trail[propagated++]; + LOG ("propagating %d", -lit); + Watches &ws = watches (lit); + + const const_watch_iterator eow = ws.end (); + watch_iterator j = ws.begin (); + const_watch_iterator i = j; + + while (i != eow) { + + const Watch w = *j++ = *i++; + const signed char b = val (w.blit); + + if (b > 0) + continue; // blocking literal satisfied + + if (w.binary ()) { + + // In principle we can ignore garbage binary clauses too, but that + // would require to dereference the clause pointer all the time with + // + // if (w.clause->garbage) { j--; continue; } // (*) + // + // This is too costly. It is however necessary to produce correct + // proof traces if binary clauses are traced to be deleted ('d ...' + // line) immediately as soon they are marked as garbage. Actually + // finding instances where this happens is pretty difficult (six + // parallel fuzzing jobs in parallel took an hour), but it does + // occur. Our strategy to avoid generating incorrect proofs now is + // to delay tracing the deletion of binary clauses marked as garbage + // until they are really deleted from memory. For large clauses + // this is not necessary since we have to access the clause anyhow. + // + // Thanks go to Mathias Fleury, who wanted me to explain why the + // line '(*)' above was in the code. Removing it actually really + // improved running times and thus I tried to find concrete + // instances where this happens (which I found), and then + // implemented the described fix. + + // Binary clauses are treated separately since they do not require + // to access the clause at all (only during conflict analysis, and + // there also only to simplify the code). + + if (b < 0) + // ignoring conflict + ++stats.warmup.conflicts; + else { + warmup_assign (w.blit, w.clause); + } + + } else { + CADICAL_assert (w.clause->size > 2); + + // The cache line with the clause data is forced to be loaded here + // and thus this first memory access below is the real hot-spot of + // the solver. Note, that this check is positive very rarely and + // thus branch prediction should be almost perfect here. + + if (w.clause->garbage) { + j--; + continue; + } + literal_iterator lits = w.clause->begin (); + const int other = lits[0] ^ lits[1] ^ lit; + const signed char u = val (other); + if (u > 0) + j[-1].blit = other; + else { + const int size = w.clause->size; + const const_literal_iterator end = lits + size; + const literal_iterator middle = lits + w.clause->pos; + literal_iterator k = middle; + signed char v = -1; + int r = 0; + while (k != end && (v = val (r = *k)) < 0) + k++; + if (v < 0) { + k = lits + 2; + CADICAL_assert (w.clause->pos <= size); + while (k != middle && (v = val (r = *k)) < 0) + k++; + } + + w.clause->pos = k - lits; // always save position + + CADICAL_assert (lits + 2 <= k), CADICAL_assert (k <= w.clause->end ()); + + if (v > 0) { + + // Replacement satisfied, so just replace 'blit'. + + j[-1].blit = r; + + } else if (!v) { + + // Found new unassigned replacement literal to be watched. + + LOG (w.clause, "unwatch %d in", lit); + + lits[0] = other; + lits[1] = r; + *k = lit; + + watch_literal (r, lit, w.clause); + + j--; // Drop this watch from the watch list of 'lit'. + + } else if (!u) { + + CADICAL_assert (v < 0); + + // The other watch is unassigned ('!u') and all other literals + // assigned to false (still 'v < 0'), thus we found a unit. + // + build_chain_for_units (other, w.clause, 0); + warmup_assign (other, w.clause); + } else { + CADICAL_assert (u < 0); + CADICAL_assert (v < 0); + + // ignoring conflict + ++stats.warmup.conflicts; + } + } + } + } + + if (j != i) { + ws.resize (j - ws.begin ()); + } + } + + CADICAL_assert (propagated == trail.size ()); + + stats.warmup.propagated += (trail.size () - before); + STOP (propagate); +} + +int Internal::warmup_decide () { + CADICAL_assert (!satisfied ()); + START (decide); + int res = 0; + if ((size_t) level < assumptions.size ()) { + const int lit = assumptions[level]; + CADICAL_assert (assumed (lit)); + const signed char tmp = val (lit); + if (tmp < 0) { + LOG ("assumption %d falsified", lit); + res = 20; + } else if (tmp > 0) { + LOG ("assumption %d already satisfied", lit); + new_trail_level (0); + LOG ("added pseudo decision level"); + } else { + LOG ("deciding assumption %d", lit); + search_assume_decision (lit); + } + } else if ((size_t) level == assumptions.size () && constraint.size ()) { + + int satisfied_lit = 0; // The literal satisfying the constrain. + int unassigned_lit = 0; // Highest score unassigned literal. + int previous_lit = 0; // Move satisfied literals to the front. + + const size_t size_constraint = constraint.size (); + +#ifndef CADICAL_NDEBUG + unsigned sum = 0; + for (auto lit : constraint) + sum += lit; +#endif + for (size_t i = 0; i != size_constraint; i++) { + + // Get literal and move 'constraint[i] = constraint[i-1]'. + + int lit = constraint[i]; + constraint[i] = previous_lit; + previous_lit = lit; + + const signed char tmp = val (lit); + if (tmp < 0) { + LOG ("constraint literal %d falsified", lit); + continue; + } + + if (tmp > 0) { + LOG ("constraint literal %d satisfied", lit); + satisfied_lit = lit; + break; + } + + CADICAL_assert (!tmp); + LOG ("constraint literal %d unassigned", lit); + + if (!unassigned_lit || better_decision (lit, unassigned_lit)) + unassigned_lit = lit; + } + + if (satisfied_lit) { + + constraint[0] = satisfied_lit; // Move satisfied to the front. + + LOG ("literal %d satisfies constraint and " + "is implied by assumptions", + satisfied_lit); + + new_trail_level (0); + LOG ("added pseudo decision level for constraint"); + notify_decision (); + + } else { + + // Just move all the literals back. If we found an unsatisfied + // literal then it will be satisfied (most likely) at the next + // decision and moved then to the first position. + + if (size_constraint) { + + for (size_t i = 0; i + 1 != size_constraint; i++) + constraint[i] = constraint[i + 1]; + + constraint[size_constraint - 1] = previous_lit; + } + + if (unassigned_lit) { + + LOG ("deciding %d to satisfy constraint", unassigned_lit); + search_assume_decision (unassigned_lit); + + } else { + + LOG ("failing constraint"); + unsat_constraint = true; + res = 20; + } + } + +#ifndef CADICAL_NDEBUG + for (auto lit : constraint) + sum -= lit; + CADICAL_assert (!sum); // Checksum of literal should not change! +#endif + + } else { + const bool target = (stable || opts.target == 2); + stats.warmup.decision++; + int idx = next_decision_variable (); + if (flags (idx).eliminated ()) + ++stats.warmup.dummydecision; + int decision = decide_phase (idx, target); + new_trail_level (decision); + warmup_assign (decision, decision_reason); + } + if (res) + marked_failed = false; + STOP (decide); + return res; +} + +int Internal::warmup () { + CADICAL_assert (!unsat); + CADICAL_assert (!level); + if (!opts.warmup) + return 0; + require_mode (WALK); + START (warmup); + ++stats.warmup.count; + int res = 0; + +#ifndef CADICAL_QUIET + const int64_t warmup_propagated = stats.warmup.propagated; + const int64_t decision = stats.warmup.decision; + const int64_t dummydecision = stats.warmup.dummydecision; +#endif + // first propagate assumptions in case we find a conflict. One + // subtle thing, if we find a conflict in the assumption, then we + // actually do need the notifications. Otherwise, we there should be + // no notification at all (not even the `backtrack ()` at the end). + const size_t assms_contraint_level = + assumptions.size () + !constraint.empty (); + while (!res && (size_t) level < assms_contraint_level && + num_assigned < (size_t) max_var) { + CADICAL_assert (num_assigned < (size_t) max_var); + res = warmup_decide (); + warmup_propagate_beyond_conflict (); + } + const bool no_backtrack_notification = (level == 0); + + // now we do not need any notification and can simply propagate + CADICAL_assert (propagated == trail.size ()); + CADICAL_assert (!private_steps); + private_steps = true; + + LOG ("propagating beyond conflicts to warm-up walk"); + while (!res && num_assigned < (size_t) max_var) { + CADICAL_assert (propagated == trail.size ()); + res = warmup_decide (); + warmup_propagate_beyond_conflict (); + LOG (lrat_chain, "during warmup with lrat chain:"); + } + CADICAL_assert (res || num_assigned == (size_t) max_var); +#ifndef CADICAL_QUIET + // constrains with empty levels break this + // CADICAL_assert (res || stats.warmup.propagated - warmup_propagated == + // (int64_t)num_assigned); + VERBOSE (3, + "warming-up needed %" PRIu64 " propagations including %" PRIu64 + " decisions (with %" PRIu64 " dummy ones)", + stats.warmup.propagated - warmup_propagated, + stats.warmup.decision - decision, + stats.warmup.dummydecision - dummydecision); +#endif + + // now we backtrack, notifying only if there was something to + // notify. + private_steps = no_backtrack_notification; + if (!res) + backtrack_without_updating_phases (); + private_steps = false; + STOP (warmup); + require_mode (WALK); + return res; +} + +} // namespace CaDiCaL + +ABC_NAMESPACE_IMPL_END diff --git a/src/sat/cadical/ccadical.h b/src/sat/cadical/ccadical.h index d4c2ce8f0..ef442e7a3 100644 --- a/src/sat/cadical/ccadical.h +++ b/src/sat/cadical/ccadical.h @@ -52,11 +52,14 @@ int ccadical_frozen (CCaDiCaL *, int lit); void ccadical_melt (CCaDiCaL *, int lit); int ccadical_simplify (CCaDiCaL *); int ccadical_vars (CCaDiCaL *); -int ccadical_reserve_difference (CCaDiCaL *, int number_of_vars); +int ccadical_declare_more_variables (CCaDiCaL *, int number_of_vars); +int ccadical_declare_one_more_variable (CCaDiCaL *); +void ccadical_phase (CCaDiCaL *wrapper, int lit); +void ccadical_unphase (CCaDiCaL *wrapper, int lit); // Extra -void ccadical_reserve(CCaDiCaL *, int min_max_var); +void ccadical_resize(CCaDiCaL *, int min_max_var); int ccadical_is_inconsistent(CCaDiCaL *); int ccadical_clauses(CCaDiCaL *); int ccadical_conflicts(CCaDiCaL *); diff --git a/src/sat/cadical/checker.hpp b/src/sat/cadical/checker.hpp index 8c351e326..40661581e 100644 --- a/src/sat/cadical/checker.hpp +++ b/src/sat/cadical/checker.hpp @@ -47,7 +47,7 @@ struct CheckerWatch { : blit (b), size (c->size), clause (c) {} }; -typedef vector CheckerWatcher; +typedef std::vector CheckerWatcher; /*------------------------------------------------------------------------*/ @@ -69,8 +69,8 @@ class Checker : public StatTracer { // and thus we access them by first mapping a literal to 'unsigned'. // static unsigned l2u (int lit); - vector watchers; // watchers of literals - vector marks; // mark bits of literals + std::vector watchers; // watchers of literals + std::vector marks; // mark bits of literals signed char &mark (int lit); CheckerWatcher &watcher (int lit); @@ -83,16 +83,16 @@ class Checker : public StatTracer { CheckerClause **clauses; // hash table of clauses CheckerClause *garbage; // linked list of garbage clauses - vector unsimplified; // original clause for reporting - vector simplified; // clause for sorting + std::vector unsimplified; // original clause for reporting + std::vector simplified; // clause for sorting - vector trail; // for propagation + std::vector trail; // for propagation unsigned next_to_propagate; // next to propagate on trail void enlarge_vars (int64_t idx); void import_literal (int lit); - void import_clause (const vector &); + void import_clause (const std::vector &); bool tautological (); static const unsigned num_nonces = 4; @@ -156,17 +156,18 @@ public: void connect_internal (Internal *i) override; - void add_original_clause (int64_t, bool, const vector &, + void add_original_clause (int64_t, bool, const std::vector &, bool = false) override; - void add_derived_clause (int64_t, bool, const vector &, - const vector &) override; - void delete_clause (int64_t, bool, const vector &) override; + void add_derived_clause (int64_t, bool, int, const std::vector &, + const std::vector &) override; + void delete_clause (int64_t, bool, const std::vector &) override; - void finalize_clause (int64_t, const vector &) override {} // skip - void report_status (int, int64_t) override {} // skip - void begin_proof (int64_t) override {} // skip - void add_assumption_clause (int64_t, const vector &, - const vector &) override; + void finalize_clause (int64_t, const std::vector &) override { + } // skip + void report_status (int, int64_t) override {} // skip + void begin_proof (int64_t) override {} // skip + void add_assumption_clause (int64_t, const std::vector &, + const std::vector &) override; void print_stats () override; void dump (); // for debugging purposes only }; diff --git a/src/sat/cadical/clause.hpp b/src/sat/cadical/clause.hpp index e5d0290f6..161b99199 100644 --- a/src/sat/cadical/clause.hpp +++ b/src/sat/cadical/clause.hpp @@ -31,6 +31,7 @@ typedef const int *const_literal_iterator; // memory but more importantly also requires another memory access and thus // is very costly. +#define USED_SIZE 5 struct Clause { union { int64_t id; // Used to create LRAT-style proofs @@ -41,6 +42,8 @@ struct Clause { // compactly in a contiguous memory arena. Otherwise, so almost all of // the time, 'id' is valid. See 'collect.cpp' for details. }; + unsigned used + : USED_SIZE; // resolved in conflict analysis since last 'reduce' bool conditioned : 1; // Tried for globally blocked clause elimination. bool covered : 1; // Already considered for covered clause elimination. bool enqueued : 1; // Enqueued on backward queue. @@ -56,9 +59,8 @@ struct Clause { bool subsume : 1; // not checked in last subsumption round bool swept : 1; // clause used to sweep equivalences bool flushed : 1; // garbage in proof deleted binaries - unsigned used : 8; // resolved in conflict analysis since last 'reduce' - bool vivified : 1; // clause already vivified - bool vivify : 1; // clause scheduled to be vivified + bool vivified : 1; // clause already vivified + bool vivify : 1; // clause scheduled to be vivified // The glucose level ('LBD' or short 'glue') is a heuristic value for the // expected usefulness of a learned clause, where smaller glue is consider diff --git a/src/sat/cadical/congruence.hpp b/src/sat/cadical/congruence.hpp index e0acf9b00..b36968da1 100644 --- a/src/sat/cadical/congruence.hpp +++ b/src/sat/cadical/congruence.hpp @@ -73,7 +73,7 @@ struct Internal; #define LD_MAX_ARITY 26 #define MAX_ARITY ((1 << LD_MAX_ARITY) - 1) -enum class Gate_Type { And_Gate, XOr_Gate, ITE_Gate }; +enum class Gate_Type : int8_t { And_Gate, XOr_Gate, ITE_Gate }; // Wrapper when we are looking for implication in if-then-else gates struct lit_implication { @@ -94,7 +94,7 @@ struct lit_equivalence { int second; Clause *first_clause; Clause *second_clause; - void check_invariant () { + void check_invariant () const { CADICAL_assert (second_clause); CADICAL_assert (first_clause); CADICAL_assert (std::find (begin (*first_clause), end (*first_clause), first) != @@ -155,7 +155,7 @@ struct ClauseSize { size_t size; Clause *clause; ClauseSize (int s, Clause *c) : size (s), clause (c) {} - ClauseSize (Clause *c): size (c->size), clause (c) {} + ClauseSize (Clause *c) : size (c->size), clause (c) {} ClauseSize () {} }; @@ -168,23 +168,38 @@ struct smaller_clause_size_rank { // There are many special cases for ITE gates and we have to keep track of // them as it is a gate property (rewriting might not make it obvious // anymore). -// a = (a ? t : e) results in no -t and no +e gate (a --> a = t == (-a v -a v t) & (-a v a v -t)) -// a = (-a ? t : e) results in no +t and no -e gate -// a = (c ? a : e) results in no t gate (none of them) -// a = (c ? t : a) results in no e gate (none of them) - -enum Special_ITE_GATE { +// a = (a ? t : e) results in no -t and no +e gate (a --> a = t == (-a v -a +// v t) & (-a v a v -t)) a = (-a ? t : e) results in no +t and no -e gate a +// = (c ? a : e) results in no t gate (none of them) a = (c ? t : a) results +// in no e gate (none of them) +// +// We also use it for AND gates: +// a = (a & b) +// false = (a & b) +// +// The latter version can only happen when converting ITE gates to AND +// gates. +// +// They have a peculiarity: being special can fix itself. To avoid +// one more special case, we rewrite the LHS in that case too to make sure +// it remains special. +// +enum Special_Gate { NORMAL = 0, NO_PLUS_THEN = (1 << 0), NO_NEG_THEN = (1 << 1), NO_THEN = NO_PLUS_THEN + NO_NEG_THEN, NO_PLUS_ELSE = (1 << 2), NO_NEG_ELSE = (1 << 3), + DEGENERATED_AND = (1 << 4), // a = (a & b) + DEGENERATED_AND_LHS_FALSE = (1 << 5), // false = (a & b) NO_ELSE = NO_PLUS_ELSE + NO_NEG_ELSE, COND_LHS = NO_NEG_THEN + NO_PLUS_ELSE, UCOND_LHS = NO_PLUS_THEN + NO_NEG_ELSE, }; +std::string special_gate_str (int8_t f); + inline bool ite_flags_no_then_clauses (int8_t flag) { return (flag & NO_THEN) == NO_THEN; } @@ -201,6 +216,18 @@ inline bool ite_flags_cond_lhs (int8_t flag) { return (flag & COND_LHS) == COND_LHS; } +// std::optional is C++17 sadly +struct my_dummy_optional { + LitClausePair content; + my_dummy_optional () : content (0, 0) {} + bool operator() () const { return content.current_lit; } + my_dummy_optional operator= (LitClausePair p) { + content = p; + return *this; + } + void reset () { content = LitClausePair (0, 0); } +}; + /*------------------------------------------------------------------------*/ // The core structure of this algorithm: the gate. It is composed of a @@ -245,19 +272,15 @@ struct Gate { bool indexed : 1; bool marked : 1; bool shrunken : 1; - size_t hash; // TODO remove this field (the C++ implementation is caching - // it anyway) vector pos_lhs_ids; - vector neg_lhs_ids; - bool degenerated_and_neg = false; // LRAT only relevant for AND Gates, neg lhs in RHS - bool degenerated_and_pos = false; // LRAT only relevant for AND Gates, pos lhs in RHS - int8_t degenerated_ite = Special_ITE_GATE::NORMAL; + my_dummy_optional neg_lhs_ids; + int8_t degenerated_gate = Special_Gate::NORMAL; vector rhs; size_t arity () const { return rhs.size (); } bool operator== (Gate const &lhs) { - return tag == lhs.tag && hash == lhs.hash && rhs == lhs.rhs; + return tag == lhs.tag && rhs == lhs.rhs; } }; @@ -279,9 +302,9 @@ struct CompactBinary { }; struct Hash { - Hash (std::array &ncs) : nonces (ncs) {} - std::array &nonces; - size_t operator() (const Gate *const g) const; + Hash (std::array &ncs) : nonces (ncs) {} + const std::array &nonces; + inline size_t operator() (const Gate *const g) const; }; struct Rewrite { @@ -297,13 +320,15 @@ struct Rewrite { struct Closure { Closure (Internal *i); + ~Closure () { delete dummy_search_gate; } + Gate *dummy_search_gate = nullptr; Internal *const internal; - vector extra_clauses; + vector extra_clauses; vector binaries; std::vector> offsetsize; bool full_watching = false; - std::array nonces; + std::array nonces; typedef unordered_set GatesTable; vector scheduled; @@ -358,6 +383,11 @@ struct Closure { int find_lrat_representative_with_marks (int lit); // representative in the union-find structure in the lazy equivalences int find_representative (int lit); + // representative in the union-find structure in the lazy equivalences. + // only useful if you do not care about proofs like during forward + // subsumption. + int find_representative_and_compress_no_proofs (int lit); + int find_representative_already_compressed (int lit); // find the representative and produce the binary clause representing the // normalization from the literal to the result. int find_representative_and_compress (int, bool update_eager = true); @@ -425,12 +455,17 @@ struct Closure { // chain. bool merge_literals_equivalence (int lit, int other, Clause *c1, Clause *c2); - bool merge_literals_lrat (Gate *g, Gate *h, int lit, int other, - const std::vector & = {}, - const std::vector & = {}); - bool merge_literals_lrat (int lit, int other, - const std::vector & = {}, - const std::vector & = {}); + bool merge_literals (Gate *g, Gate *h, int lit, int other, + const std::vector & = {}, + const std::vector & = {}); + bool merge_literals (int lit, int other, + const std::vector & = {}, + const std::vector & = {}); + // factoring out the merge w.r.t. both cases above + bool really_merge_literals (int lit, int other, int repr_lit, + int repr_other, + const std::vector & = {}, + const std::vector & = {}); // proof production vector lrat_chain_and_gate; @@ -458,11 +493,13 @@ struct Closure { // TODO: does nothing except pushing on the stack, remove! void push_id_on_chain (std::vector &chain, const std::vector &c); + void push_id_on_chain (std::vector &chain, + const my_dummy_optional &c); // TODO: does nothing except pushing on the stack, remove! void push_id_on_chain (std::vector &chain, Rewrite rewrite, int); void update_and_gate_build_lrat_chain ( Gate *g, Gate *h, std::vector &extra_reasons_lit, - std::vector &extra_reasons_ulit, bool remove_units = true); + std::vector &extra_reasons_ulit, bool remove_units = true); void update_and_gate_unit_build_lrat_chain ( Gate *g, int src, LRAT_ID id1, LRAT_ID id2, int dst, std::vector &extra_reasons_lit, @@ -528,17 +565,18 @@ struct Closure { Gate *find_remaining_and_gate (Clause *base_clause, int lhs); void extract_and_gates (); - Gate *find_and_lits (const vector &rhs, Gate *except = nullptr); - // rhs is sorted, so passing by copy - Gate *find_gate_lits (const vector &rhs, Gate_Type typ, - Gate *except = nullptr); - Gate *find_xor_lits (const vector &rhs); + Gate *find_and_lits (vector &rhs, Gate *except = nullptr); + Gate * + find_gate_lits (vector &rhs, + Gate_Type typ, // rhs unchanged but swapped back and forth + Gate *except = nullptr); + Gate *find_xor_lits (vector &rhs); // not const to normalize negations, also fixes the order of the LRAT Gate *find_ite_gate (Gate *, bool &); Gate *find_xor_gate (Gate *); void reset_xor_gate_extraction (); - void init_xor_gate_extraction (std::vector &candidates); + void init_xor_gate_extraction (std::vector &candidates); LRAT_ID check_and_add_to_proof_chain (vector &clause); void add_xor_matching_proof_chain (Gate *g, int lhs1, const vector &, @@ -557,7 +595,7 @@ struct Closure { void check_ite_implied (int lhs, int cond, int then_lit, int else_lit); void check_ite_gate_implied (Gate *g); void check_and_gate_implied (Gate *g); - void check_ite_lrat_reasons (Gate *g, bool = false); + void check_ite_lrat_reasons (Gate *g); void check_contained_module_rewriting (Clause *c, int lit, bool, int except); void delete_proof_chain (); @@ -598,17 +636,16 @@ struct Closure { void check_binary_implied (int a, int b); void check_implied (); - // learn units. You can delay units if you want to learn several at once before - // propagation. Otherwise, propagate! If you need propagation even if nothing is set, use the - // second parameter. + // learn units. You can delay units if you want to learn several at once + // before propagation. Otherwise, propagate! If you need propagation even + // if nothing is set, use the second parameter. // - // The function can also learn the empty clause if the unit is already set. Do not add the unit in - // the chain! + // The function can also learn the empty clause if the unit is already + // set. Do not add the unit in the chain! bool learn_congruence_unit (int unit, bool = false, bool = false); - bool fully_propagate (); + bool fully_propagate (); void learn_congruence_unit_falsifies_lrat_chain (Gate *g, int src, - int dst, - int clashing, + int dst, int clashing, int falsified, int unit); void learn_congruence_unit_when_lhs_set (Gate *g, int src, LRAT_ID id1, LRAT_ID id2, int dst); @@ -618,8 +655,12 @@ struct Closure { void subsume_clause (Clause *subsuming, Clause *subsumed); bool find_subsuming_clause (Clause *c); void produce_rewritten_clause_lrat_and_clean (vector &, + int execept_lhs = 0, + bool = true, bool = false); + void produce_rewritten_clause_lrat_and_clean (my_dummy_optional &, int execept_lhs = 0, bool = true); + // rewrite the clause using eager rewriting and rew1 and rew2, except for // 2 literals Usage: // - the except are used to ignore LHS of gates that have not and should @@ -629,10 +670,10 @@ struct Closure { // to be taken into account without being added to the eager rewriting // (yet) Clause *produce_rewritten_clause_lrat (Clause *c, int execept_lhs = 0, - bool remove_units = true, bool = true); + bool remove_units = true, + bool = false); void produce_rewritten_clause_lrat (vector &, - int execept_lhs = 0, - bool = true); + int execept_lhs = 0, bool = true); void compute_rewritten_clause_lrat_simple (Clause *c, int except); // variant where we update the indices after removing the tautologies and // remove the tautological clauses @@ -681,7 +722,8 @@ struct Closure { bool flip = 0); bool rewrite_ite_gate_to_and (Gate *g, int dst, int src, size_t c, - size_t d, int cond_lit_to_learn_if_degenerated); + size_t d, + int cond_lit_to_learn_if_degenerated); void produce_ite_merge_then_else_reasons ( Gate *g, int dst, int src, std::vector &reasons_implication, std::vector &reasons_back); @@ -699,10 +741,10 @@ struct Closure { // has length 3 bool simplify_ite_gate_to_and (Gate *g, size_t idx1, size_t idx2, int removed); - void - merge_ite_gate_same_then_else_lrat (std::vector &clauses, - std::vector &reasons_implication, - std::vector &reasons_back); + void merge_ite_gate_same_then_else_lrat ( + std::vector &clauses, + std::vector &reasons_implication, + std::vector &reasons_back); void simplify_ite_gate_then_else_set ( Gate *g, std::vector &reasons_implication, std::vector &reasons_back, size_t idx1, size_t idx2); diff --git a/src/sat/cadical/contract.hpp b/src/sat/cadical/contract.hpp index 4a3ba066a..d70e7b6ab 100644 --- a/src/sat/cadical/contract.hpp +++ b/src/sat/cadical/contract.hpp @@ -98,7 +98,9 @@ void require_solver_pointer_to_be_non_zero (const void *ptr, REQUIRE ((int) (LIT) && ((int) (LIT)) != INT_MIN, \ "invalid literal '%d'", (int) (LIT)); \ REQUIRE (external->is_valid_input ((int) (LIT)), \ - "extension variable %d defined by the solver", (int) (LIT)); \ + "extension variable %d defined by the solver \ + (try using `vars ()` or `set (factor, 0)`)", \ + (int) (LIT)); \ } while (0) #define REQUIRE_STEADY_STATE() \ diff --git a/src/sat/cadical/drattracer.hpp b/src/sat/cadical/drattracer.hpp index 8c2c023d5..cf722f6a2 100644 --- a/src/sat/cadical/drattracer.hpp +++ b/src/sat/cadical/drattracer.hpp @@ -3,6 +3,7 @@ #include "global.h" +#include "file.hpp" #include "tracer.hpp" ABC_NAMESPACE_CXX_HEADER_START @@ -21,8 +22,8 @@ class DratTracer : public FileTracer { void put_binary_lit (int external_lit); // support DRAT - void drat_add_clause (const vector &); - void drat_delete_clause (const vector &); + void drat_add_clause (const std::vector &); + void drat_delete_clause (const std::vector &); public: // own and delete 'file' @@ -32,15 +33,16 @@ public: void connect_internal (Internal *i) override; void begin_proof (int64_t) override {} // skip - void add_original_clause (int64_t, bool, const vector &, + void add_original_clause (int64_t, bool, const std::vector &, bool = false) override {} // skip - void add_derived_clause (int64_t, bool, const vector &, - const vector &) override; + void add_derived_clause (int64_t, bool, int, const std::vector &, + const std::vector &) override; - void delete_clause (int64_t, bool, const vector &) override; + void delete_clause (int64_t, bool, const std::vector &) override; - void finalize_clause (int64_t, const vector &) override {} // skip + void finalize_clause (int64_t, const std::vector &) override { + } // skip void report_status (int, int64_t) override {} // skip diff --git a/src/sat/cadical/external.hpp b/src/sat/cadical/external.hpp index 7618fb936..f17334ecf 100644 --- a/src/sat/cadical/external.hpp +++ b/src/sat/cadical/external.hpp @@ -6,6 +6,7 @@ /*------------------------------------------------------------------------*/ #include "range.hpp" +#include #include #include @@ -321,7 +322,32 @@ struct External { // We call it 'ival' as abbreviation for 'val' with 'int' return type to // avoid bugs due to using 'signed char tmp = val (lit)', which might turn // a negative value into a positive one (happened in 'extend'). + + // This is due to the IPASIR semantics which returns 'elit' if it is + // 'true' and '-elit' if it is 'false'. This is a bit confusing but has + // been standardized in IPASIR: // + // Consiert 'eidx = 13' and 'vals[13] == false' then '13' is 'false' in + // this terminology of the IPASIR interface. Accordingly we get + // + // ival (13) = -13 + // + // However and this is the confusing thing, as '-13' is true the 'ival' + // function should also return '-13': + // + // ival (-13) = -13 + // + // Now with '13' assumed 'true' so 'vals[13] = true' we similarly have + // + // ival (13) = 13 as '13' is true' + // + // ival (-13) = 13 as '-13' is false + // + // To summarize we can think of the IPASIR 'ipasir_val' function, which + // 'CaDiCaL' follows as returning the phase of the literal which is true + // under the current assignment no matter whether you give the positive + // literal or its negation and thus 'ival (lit) == ival (-lit))" + inline int ival (int elit) const { CADICAL_assert (elit != INT_MIN); int eidx = abs (elit); diff --git a/src/sat/cadical/flags.hpp b/src/sat/cadical/flags.hpp index 995438c7a..422111dea 100644 --- a/src/sat/cadical/flags.hpp +++ b/src/sat/cadical/flags.hpp @@ -36,13 +36,14 @@ struct Flags { // Variable flags. // unsigned char block : 2; // removed since last 'block' round (*) unsigned char skip : 2; // skip this literal as blocking literal - + bool backbone1, backbone0; // Bits for handling assumptions. // unsigned char assumed : 2; unsigned char failed : 2; // 0 if not part of failure // 1 if positive lit is in failure - // 2 if negated lit is in failure + // 2 if negated lit is in failure + bool factored_but_on_reconstruction_stack : 1; enum { UNUSED = 0, @@ -58,7 +59,8 @@ struct Flags { // Variable flags. // Initialized explicitly in 'Internal::init' through this function. // Flags () { - seen = keep = poison = removable = shrinkable = added = sweep = false; + seen = keep = poison = removable = shrinkable = added = sweep = + backbone1 = backbone0 = false; subsume = elim = ternary = true; block = 3u; skip = assumed = failed = marked_signed = factor = 0; @@ -81,6 +83,12 @@ struct Flags { // Variable flags. dst.subsume = subsume; dst.ternary = ternary; dst.block = block; + dst.sweep = sweep; + dst.backbone0 = backbone0; + dst.backbone1 = backbone1; + dst.added = added; + dst.factor = factor; + // seen, keep, poison, removable, shrinkable are unused } }; diff --git a/src/sat/cadical/frattracer.hpp b/src/sat/cadical/frattracer.hpp index cadfa3721..09125ca3b 100644 --- a/src/sat/cadical/frattracer.hpp +++ b/src/sat/cadical/frattracer.hpp @@ -3,6 +3,11 @@ #include "global.h" +#include "file.hpp" +#include "tracer.hpp" + +#include + ABC_NAMESPACE_CXX_HEADER_START namespace CaDiCaL { @@ -19,19 +24,19 @@ class FratTracer : public FileTracer { int64_t finalized, original; #endif - vector delete_ids; + std::vector delete_ids; void put_binary_zero (); void put_binary_lit (int external_lit); void put_binary_id (int64_t id, bool = false); // support FRAT - void frat_add_original_clause (int64_t, const vector &); - void frat_add_derived_clause (int64_t, const vector &); - void frat_add_derived_clause (int64_t, const vector &, - const vector &); - void frat_delete_clause (int64_t, const vector &); - void frat_finalize_clause (int64_t, const vector &); + void frat_add_original_clause (int64_t, const std::vector &); + void frat_add_derived_clause (int64_t, const std::vector &); + void frat_add_derived_clause (int64_t, const std::vector &, + const std::vector &); + void frat_delete_clause (int64_t, const std::vector &); + void frat_finalize_clause (int64_t, const std::vector &); public: // own and delete 'file' @@ -41,15 +46,15 @@ public: void connect_internal (Internal *i) override; void begin_proof (int64_t) override {} // skip - void add_original_clause (int64_t, bool, const vector &, + void add_original_clause (int64_t, bool, const std::vector &, bool = false) override; - void add_derived_clause (int64_t, bool, const vector &, - const vector &) override; + void add_derived_clause (int64_t, bool, int, const std::vector &, + const std::vector &) override; - void delete_clause (int64_t, bool, const vector &) override; + void delete_clause (int64_t, bool, const std::vector &) override; - void finalize_clause (int64_t, const vector &) override; + void finalize_clause (int64_t, const std::vector &) override; void report_status (int, int64_t) override {} // skip diff --git a/src/sat/cadical/idruptracer.hpp b/src/sat/cadical/idruptracer.hpp index 4ea478705..7d343cc5f 100644 --- a/src/sat/cadical/idruptracer.hpp +++ b/src/sat/cadical/idruptracer.hpp @@ -3,9 +3,10 @@ #include "global.h" -ABC_NAMESPACE_CXX_HEADER_START +#include "file.hpp" +#include "tracer.hpp" -class FileTracer; +ABC_NAMESPACE_CXX_HEADER_START namespace CaDiCaL { @@ -29,8 +30,8 @@ class IdrupTracer : public FileTracer { uint64_t num_clauses; // number of clauses in hash table uint64_t size_clauses; // size of clause hash table IdrupClause **clauses; // hash table of clauses - vector imported_clause; - vector assumptions; + std::vector imported_clause; + std::vector assumptions; static const unsigned num_nonces = 4; @@ -60,14 +61,14 @@ class IdrupTracer : public FileTracer { void put_binary_lit (int external_lit); void put_binary_id (int64_t id, bool = false); - void idrup_add_derived_clause (const vector &clause); - void idrup_delete_clause (int64_t id, const vector &clause); - void idrup_add_restored_clause (const vector &clause); - void idrup_add_original_clause (const vector &clause); - void idrup_conclude_and_delete (const vector &conclusion); + void idrup_add_derived_clause (const std::vector &clause); + void idrup_delete_clause (int64_t id, const std::vector &clause); + void idrup_add_restored_clause (const std::vector &clause); + void idrup_add_original_clause (const std::vector &clause); + void idrup_conclude_and_delete (const std::vector &conclusion); void idrup_report_status (int status); - void idrup_conclude_sat (const vector &model); - void idrup_conclude_unknown (const vector &trail); + void idrup_conclude_sat (const std::vector &model); + void idrup_conclude_unknown (const std::vector &trail); void idrup_solve_query (); public: @@ -75,18 +76,19 @@ public: ~IdrupTracer (); // proof section: - void add_derived_clause (int64_t, bool, const vector &, - const vector &) override; - void add_assumption_clause (int64_t, const vector &, - const vector &) override; - void weaken_minus (int64_t, const vector &) override; - void delete_clause (int64_t, bool, const vector &) override; - void add_original_clause (int64_t, bool, const vector &, + void add_derived_clause (int64_t, bool, int, const std::vector &, + const std::vector &) override; + void add_assumption_clause (int64_t, const std::vector &, + const std::vector &) override; + void weaken_minus (int64_t, const std::vector &) override; + void delete_clause (int64_t, bool, const std::vector &) override; + void add_original_clause (int64_t, bool, const std::vector &, bool = false) override; void report_status (int, int64_t) override; - void conclude_sat (const vector &) override; - void conclude_unsat (ConclusionType, const vector &) override; - void conclude_unknown (const vector &) override; + void conclude_sat (const std::vector &) override; + void conclude_unsat (ConclusionType, + const std::vector &) override; + void conclude_unknown (const std::vector &) override; void solve_query () override; void add_assumption (int) override; @@ -94,9 +96,9 @@ public: // skip void begin_proof (int64_t) override {} - void finalize_clause (int64_t, const vector &) override {} + void finalize_clause (int64_t, const std::vector &) override {} void strengthen (int64_t) override {} - void add_constraint (const vector &) override {} + void add_constraint (const std::vector &) override {} // logging and file io void connect_internal (Internal *i) override; diff --git a/src/sat/cadical/internal.hpp b/src/sat/cadical/internal.hpp index 53c698c58..a9104898f 100644 --- a/src/sat/cadical/internal.hpp +++ b/src/sat/cadical/internal.hpp @@ -23,6 +23,7 @@ #include #include #include +#include // Less common 'C' header. @@ -107,6 +108,7 @@ extern "C" { #include "veripbtracer.hpp" #include "version.hpp" #include "vivify.hpp" +#include "walk.hpp" #include "watch.hpp" // c headers @@ -123,6 +125,7 @@ using namespace std; struct Coveror; struct External; +struct WalkerFO; struct Walker; class Tracer; class FileTracer; @@ -161,6 +164,7 @@ struct Internal { TRANSRED = (1 << 15), VIVIFY = (1 << 16), WALK = (1 << 17), + BACKBONE = (1 << 18), }; bool in_mode (Mode m) const { return (mode & m) != 0; } @@ -275,6 +279,7 @@ struct Internal { vector sweep_schedule; // remember sweep varibles to reschedule bool sweep_incomplete; // sweep + uint64_t randomized_deciding; cadical_kitten *citten; @@ -313,7 +318,8 @@ struct Internal { Internal *internal; // proxy to 'this' in macros External *external; // proxy to 'external' buddy in 'Solver' - const unsigned max_used = 255; // must fix into the header of the clause! + static constexpr unsigned max_used = + 31; // must fit into the header of the clause! /*----------------------------------------------------------------------*/ // Asynchronous termination flag written by 'terminate' and read by @@ -626,9 +632,20 @@ struct Internal { CADICAL_assert (lit != blit); Watches &ws = watches (lit); ws.push_back (Watch (blit, c)); + CADICAL_assert (c->literals[0] == lit || c->literals[1] == lit); LOG (c, "watch %d blit %d in", lit, blit); } + // Watch literal 'lit' in clause with blocking literal 'blit'. + // Inlined here, since it occurs in the tight inner loop of 'propagate'. + // + inline void watch_binary_literal (int lit, int blit, Clause *c) { + CADICAL_assert (lit != blit); + Watches &ws = watches (lit); + ws.push_back (Watch (true, blit, c)); + LOG (c, "watch binary %d blit %d in", lit, blit); + } + // Add two watches to a clause. This is used initially during allocation // of a clause and during connecting back all watches after preprocessing. // @@ -720,6 +737,7 @@ struct Internal { void search_assign_driving (int lit, Clause *reason); void search_assign_external (int lit); void search_assume_decision (int decision); + static Clause *decision_reason; void assign_unit (int lit); int64_t cache_lines (size_t bytes) { return (bytes + 127) / 128; } int64_t cache_lines (size_t n, size_t bytes) { @@ -789,6 +807,7 @@ struct Internal { int otfs_find_backtrack_level (int &forced); Clause *on_the_fly_strengthen (Clause *conflict, int lit); void update_decision_rate_average (); + void lazy_external_propagator_out_of_order_clause (int &); void analyze (); void iterate (); // report learned unit clause @@ -820,6 +839,13 @@ struct Internal { void renotify_trail_after_ilb (); void renotify_trail_after_local_search (); void renotify_full_trail (); + + // adds the assigned literals to assigned + void renotify_full_trail_between_trail_pos (int start_level, + int end_level, + int propagator_level, + std::vector &assigned, + bool start_new_level); void connect_propagator (); void mark_garbage_external_forgettable (int64_t id); bool is_external_forgettable (int64_t id); @@ -829,6 +855,8 @@ struct Internal { #endif void recompute_tier (); + void decay_clauses_upon_incremental_clauses (); + void print_tier_usage_statistics (); // Use last learned clause to subsume some more. // void eagerly_subsume_recently_learned_clauses (Clause *); @@ -844,6 +872,8 @@ struct Internal { // void clear_phases (vector &); // reset argument to zero void copy_phases (vector &); // copy 'saved' to argument + void save_assigned_phases ( + vector &); // save assigned literals to argument // Resetting the saved phased in 'rephase.cpp'. // @@ -861,6 +891,7 @@ struct Internal { // Lucky feasible case checking. // int unlucky (int res); + int lucky_decide_assumptions (); bool lucky_propagate_discrepency (int); int trivially_false_satisfiable (); int trivially_true_satisfiable (); @@ -977,7 +1008,7 @@ struct Internal { void vivify_build_lrat (int, Clause *, std::vector> &); void vivify_chain_for_units (int lit, Clause *reason); - void vivify_strengthen (Clause *candidate); + void vivify_strengthen (Clause *candidate, int64_t &); void vivify_assign (int lit, Clause *); void vivify_assume (int lit); bool vivify_propagate (int64_t &); @@ -999,6 +1030,28 @@ struct Internal { // void transred (); + // backbone computation + // + void backbone_decision (int lit); + bool backbone_propagate (int64_t &); + void backbone_propagate2 (int64_t &); + unsigned compute_backbone (); + void backbone_unit_reassign ( + int lit); // only for reassigning already derived clauses! + void backbone_unit_assign ( + int lit); // only for reassigning already derived clauses! + void backbone_assign_any (int lit, Clause *reason); + void backbone_assign (int lit, Clause *reason); + void backbone_lrat_for_units (int lit, Clause *c); + unsigned compute_backbone_round (std::vector &candidates, + std::vector &units, + const int64_t ticks_limit, + int64_t &ticks, unsigned inconsistent); + void schedule_backbone_cands (std::vector &candidates); + void keep_backbone_candidates (const std::vector &candidates); + int backbone_analyze (Clause *, int64_t &); + void binary_clauses_backbone (); + // We monitor the maximum size and glue of clauses during 'reduce' and // thus can predict if a redundant extended clause is likely to be kept in // the next 'reduce' phase. These clauses are target of subsumption and @@ -1292,7 +1345,8 @@ struct Internal { bool run_factorization (int64_t limit); bool factor (); int get_new_extension_variable (); - Clause *new_factor_clause (); + Clause *new_factor_clause (int); + void adjust_scores_and_phases_of_fresh_variables (Factoring &); // instantiate // @@ -1347,13 +1401,27 @@ struct Internal { // ProbSAT/WalkSAT implementation called initially or from 'rephase'. // void walk_save_minimum (Walker &); - Clause *walk_pick_clause (Walker &); - unsigned walk_break_value (int lit); + ClauseOrBinary walk_pick_clause (Walker &); + unsigned walk_break_value (int lit, int64_t &ticks); + int walk_pick_lit (Walker &walker, ClauseOrBinary); int walk_pick_lit (Walker &, Clause *); - void walk_flip_lit (Walker &, int lit); + bool walk_flip_lit (Walker &, int lit); + int walk_pick_lit (Walker &walker, TaggedBinary c); int walk_round (int64_t limit, bool prev); void walk (); + int walk_full_occs_round (int64_t limit, bool prev); + void walk_full_occs (); + void walk_full_occs_save_minimum (WalkerFO &); + void make_clauses_along_occurrences (WalkerFO &walker, int lit); + void make_clauses_along_unsatisfied (WalkerFO &walker, int lit); + + // Warmup + inline void warmup_assign (int lit, Clause *reason); + void warmup_propagate_beyond_conflict (); + int warmup_decide (); + int warmup (); + // Detect strongly connected components in the binary implication graph // (BIG) and equivalent literal substitution (ELS) in 'decompose.cpp'. // @@ -1427,6 +1495,8 @@ struct Internal { // Part on picking the next decision in 'decide.cpp'. // bool satisfied (); + void start_random_sequence (); + int next_random_decision (); int next_decision_variable_on_queue (); int next_decision_variable_with_best_score (); int next_decision_variable (); @@ -1442,6 +1512,7 @@ struct Internal { void limit_conflicts (int); // Force conflict limit. void limit_preprocessing (int); // Enable 'n' preprocessing rounds. void limit_local_search (int); // Enable 'n' local search rounds. + void limit_ticks (int64_t); // Force ticks limit. // External versions can access limits by 'name'. // @@ -1478,8 +1549,8 @@ struct Internal { int already_solved (); int restore_clauses (); bool preprocess_round (int round); - void preprocess_quickly (); - int preprocess (); + void preprocess_quickly (bool always); + int preprocess (bool always); int local_search_round (int round); int local_search (); int lucky_phases (); @@ -1607,7 +1678,7 @@ struct Internal { } // Congruence closure - bool extract_gates (); + bool extract_gates (bool remove_units_before_run = false); // Parsing functions in 'parse.cpp'. // @@ -1856,6 +1927,12 @@ inline bool Internal::search_limits_hit () { return true; } + if (lim.ticks >= 0 && + stats.ticks.search[0] + stats.ticks.search[1] >= lim.ticks) { + LOG ("ticks limit %" PRId64 " reached", lim.ticks); + return true; + } + return false; } diff --git a/src/sat/cadical/lidruptracer.hpp b/src/sat/cadical/lidruptracer.hpp index 02cd33fcd..ec8352712 100644 --- a/src/sat/cadical/lidruptracer.hpp +++ b/src/sat/cadical/lidruptracer.hpp @@ -3,9 +3,10 @@ #include "global.h" -ABC_NAMESPACE_CXX_HEADER_START +#include "file.hpp" +#include "tracer.hpp" -class FileTracer; +ABC_NAMESPACE_CXX_HEADER_START namespace CaDiCaL { @@ -29,12 +30,12 @@ class LidrupTracer : public FileTracer { uint64_t num_clauses; // number of clauses in hash table uint64_t size_clauses; // size of clause hash table LidrupClause **clauses; // hash table of clauses - vector imported_clause; - vector assumptions; - vector imported_chain; - vector batch_weaken; - vector batch_delete; - vector batch_restore; + std::vector imported_clause; + std::vector assumptions; + std::vector imported_chain; + std::vector batch_weaken; + std::vector batch_delete; + std::vector batch_restore; static const unsigned num_nonces = 4; @@ -64,16 +65,19 @@ class LidrupTracer : public FileTracer { void put_binary_lit (int external_lit); void put_binary_id (int64_t id, bool = true); - void lidrup_add_derived_clause (int64_t id, const vector &clause, - const vector &chain); - void lidrup_delete_clause (int64_t id); //, const vector &clause); + void lidrup_add_derived_clause (int64_t id, + const std::vector &clause, + const std::vector &chain); void - lidrup_add_restored_clause (int64_t id); //, const vector &clause); - void lidrup_add_original_clause (int64_t id, const vector &clause); - void lidrup_conclude_and_delete (const vector &conclusion); + lidrup_delete_clause (int64_t id); //, const std::vector &clause); + void lidrup_add_restored_clause ( + int64_t id); //, const std::vector &clause); + void lidrup_add_original_clause (int64_t id, + const std::vector &clause); + void lidrup_conclude_and_delete (const std::vector &conclusion); void lidrup_report_status (int status); - void lidrup_conclude_sat (const vector &model); - void lidrup_conclude_unknown (const vector &trail); + void lidrup_conclude_sat (const std::vector &model); + void lidrup_conclude_unknown (const std::vector &trail); void lidrup_solve_query (); void lidrup_batch_weaken_restore_and_delete (); @@ -82,18 +86,19 @@ public: ~LidrupTracer (); // proof section: - void add_derived_clause (int64_t, bool, const vector &, - const vector &) override; - void add_assumption_clause (int64_t, const vector &, - const vector &) override; - void weaken_minus (int64_t, const vector &) override; - void delete_clause (int64_t, bool, const vector &) override; - void add_original_clause (int64_t, bool, const vector &, + void add_derived_clause (int64_t, bool, int, const std::vector &, + const std::vector &) override; + void add_assumption_clause (int64_t, const std::vector &, + const std::vector &) override; + void weaken_minus (int64_t, const std::vector &) override; + void delete_clause (int64_t, bool, const std::vector &) override; + void add_original_clause (int64_t, bool, const std::vector &, bool = false) override; void report_status (int, int64_t) override; - void conclude_sat (const vector &) override; - void conclude_unsat (ConclusionType, const vector &) override; - void conclude_unknown (const vector &) override; + void conclude_sat (const std::vector &) override; + void conclude_unsat (ConclusionType, + const std::vector &) override; + void conclude_unknown (const std::vector &) override; void solve_query () override; void add_assumption (int) override; @@ -101,9 +106,9 @@ public: // skip void begin_proof (int64_t) override {} - void finalize_clause (int64_t, const vector &) override {} + void finalize_clause (int64_t, const std::vector &) override {} void strengthen (int64_t) override {} - void add_constraint (const vector &) override {} + void add_constraint (const std::vector &) override {} // logging and file io void connect_internal (Internal *i) override; diff --git a/src/sat/cadical/limit.hpp b/src/sat/cadical/limit.hpp index 0f6923214..284a38b58 100644 --- a/src/sat/cadical/limit.hpp +++ b/src/sat/cadical/limit.hpp @@ -20,17 +20,21 @@ struct Limit { int64_t decisions; // decision limit if non-negative int64_t preprocessing; // limit on preprocessing rounds int64_t localsearch; // limit on local search rounds + int64_t ticks; // ticks limit if non-negative - int64_t compact; // conflict limit for next 'compact' - int64_t condition; // conflict limit for next 'condition' - int64_t elim; // conflict limit for next 'elim' - int64_t flush; // conflict limit for next 'flush' - int64_t inprobe; // conflict limit for next 'inprobe' - int64_t reduce; // conflict limit for next 'reduce' - int64_t rephase; // conflict limit for next 'rephase' - int64_t report; // report limit for header - int64_t restart; // conflict limit for next 'restart' - int64_t stabilize; // conflict/ticks limit for next 'stabilize' + int64_t compact; // conflict limit for next 'compact' + int64_t condition; // conflict limit for next 'condition' + int64_t elim; // conflict limit for next 'elim' + int64_t flush; // conflict limit for next 'flush' + int64_t inprobe; // conflict limit for next 'inprobe' + int64_t reduce; // conflict limit for next 'reduce' + int64_t rephase; // conflict limit for next 'rephase' + int64_t report; // report limit for header + int64_t restart; // conflict limit for next 'restart' + int64_t stabilize; // conflict/ticks limit for next 'stabilize' + int64_t incremental_decay; // conflict/ticks limit for next clause 'decay' + // for incremental clauses + int64_t random_decision; // randomized decision limit for conflicts int keptsize; // maximum kept size in 'reduce' int keptglue; // maximum kept glue in 'reduce' @@ -59,7 +63,7 @@ struct Delay { bool delay () { if (bypass) - return true; + return false; if (limit) { --limit; return true; @@ -91,7 +95,7 @@ struct Last { } transred; struct { int64_t ticks; - } sweep, vivify, probe; + } backbone, probe, sweep, vivify, walk; struct { int64_t fixed, subsumephases, marked; } elim; @@ -114,7 +118,11 @@ struct Last { struct { int64_t conflicts; int64_t ticks; + int64_t rephased; } stabilize; + struct { + int64_t last_id; + } incremental_decay; Last (); }; @@ -123,6 +131,7 @@ struct Inc { int64_t stabilize; // base ticks limit after first mode switch int64_t conflicts; // next conflict limit if non-negative int64_t decisions; // next decision limit if non-negative + int64_t ticks; // next ticks limit if non-negative int64_t preprocessing; // next preprocessing limit if non-negative int64_t localsearch; // next local search limit if non-negative Inc (); @@ -153,6 +162,9 @@ struct Inc { last.NAME.ticks = TICKS; \ const int64_t NEW_LIMIT = OLD_LIMIT + DELTA; \ LIMIT = NEW_LIMIT; \ + VERBOSE (2, \ + "new ticks limit %" PRId64 "= %" PRId64 " + %f * %" PRId64, \ + NEW_LIMIT, OLD_LIMIT, EFFORT, REFERENCE); \ } while (0) } // namespace CaDiCaL diff --git a/src/sat/cadical/lratchecker.hpp b/src/sat/cadical/lratchecker.hpp index 0e791e9f4..7765ec73b 100644 --- a/src/sat/cadical/lratchecker.hpp +++ b/src/sat/cadical/lratchecker.hpp @@ -4,6 +4,8 @@ #include "global.h" /*------------------------------------------------------------------------*/ +#include "tracer.hpp" +#include #include ABC_NAMESPACE_CXX_HEADER_START @@ -49,11 +51,11 @@ class LratChecker : public StatTracer { signed char &checked_lit (int lit); signed char &mark (int lit); - vector checked_lits; - vector marks; // mark bits of literals - unordered_map> clauses_to_reconstruct; - vector assumptions; - vector constraint; + std::vector checked_lits; + std::vector marks; // mark bits of literals + std::unordered_map> clauses_to_reconstruct; + std::vector assumptions; + std::vector constraint; bool concluded; uint64_t num_clauses; // number of clauses in hash table @@ -63,12 +65,12 @@ class LratChecker : public StatTracer { LratCheckerClause **clauses; // hash table of clauses LratCheckerClause *garbage; // linked list of garbage clauses - vector imported_clause; // original clause for reporting - vector assumption_clauses; + std::vector imported_clause; // original clause for reporting + std::vector assumption_clauses; void enlarge_vars (int64_t idx); void import_literal (int lit); - void import_clause (const vector &); + void import_clause (const std::vector &); static const unsigned num_nonces = 4; @@ -94,15 +96,16 @@ class LratChecker : public StatTracer { LratCheckerClause *new_clause (); void delete_clause (LratCheckerClause *); - bool check (vector); // check RUP - bool check_resolution (vector); // check resolution - bool check_blocked (vector); // check ER + bool check (std::vector); // check RUP + bool check_resolution (std::vector); // check resolution + bool check_blocked (std::vector); // check ER struct { int64_t added; // number of added clauses int64_t original; // number of added original clauses int64_t derived; // number of added derived clauses + int64_t rat; // number of added rat clauses int64_t deleted; // number of deleted clauses int64_t finalized; // number of finalized clauses @@ -124,40 +127,41 @@ public: void connect_internal (Internal *i) override; void begin_proof (int64_t) override; - void add_original_clause (int64_t, bool, const vector &, + void add_original_clause (int64_t, bool, const std::vector &, bool restore) override; - void restore_clause (int64_t, const vector &); + void restore_clause (int64_t, const std::vector &); // check the proof chain for the new clause and add it to the checker - void add_derived_clause (int64_t, bool, const vector &, - const vector &) override; + void add_derived_clause (int64_t, bool, int, const std::vector &, + const std::vector &) override; // check if the clause is present and delete it from the checker - void delete_clause (int64_t, bool, const vector &) override; + void delete_clause (int64_t, bool, const std::vector &) override; // check if the clause is present and delete it from the checker - void weaken_minus (int64_t, const vector &) override; + void weaken_minus (int64_t, const std::vector &) override; // check if the clause is present and delete it from the checker - void finalize_clause (int64_t, const vector &) override; + void finalize_clause (int64_t, const std::vector &) override; // check the proof chain of the assumption clause and delete it // immediately also check that they contain only assumptions and // constraints - void add_assumption_clause (int64_t, const vector &, - const vector &) override; + void add_assumption_clause (int64_t, const std::vector &, + const std::vector &) override; // mark lit as assumption void add_assumption (int) override; // mark lits as constraint - void add_constraint (const vector &) override; + void add_constraint (const std::vector &) override; void reset_assumptions () override; // check if all clauses have been deleted void report_status (int, int64_t) override; - void conclude_unsat (ConclusionType, const vector &) override; + void conclude_unsat (ConclusionType, + const std::vector &) override; void print_stats () override; void dump (); // for debugging purposes only diff --git a/src/sat/cadical/lrattracer.hpp b/src/sat/cadical/lrattracer.hpp index 9d8e92b6a..eeb7d8556 100644 --- a/src/sat/cadical/lrattracer.hpp +++ b/src/sat/cadical/lrattracer.hpp @@ -3,6 +3,9 @@ #include "global.h" +#include "file.hpp" +#include "tracer.hpp" + ABC_NAMESPACE_CXX_HEADER_START namespace CaDiCaL { @@ -17,15 +20,15 @@ class LratTracer : public FileTracer { int64_t added, deleted; #endif int64_t latest_id; - vector delete_ids; + std::vector delete_ids; void put_binary_zero (); void put_binary_lit (int external_lit); void put_binary_id (int64_t id); // support LRAT - void lrat_add_clause (int64_t, const vector &, - const vector &); + void lrat_add_clause (int64_t, const std::vector &, + const std::vector &); void lrat_delete_clause (int64_t); public: @@ -36,15 +39,16 @@ public: void connect_internal (Internal *i) override; void begin_proof (int64_t) override; - void add_original_clause (int64_t, bool, const vector &, + void add_original_clause (int64_t, bool, const std::vector &, bool = false) override {} // skip - void add_derived_clause (int64_t, bool, const vector &, - const vector &) override; + void add_derived_clause (int64_t, bool, int, const std::vector &, + const std::vector &) override; - void delete_clause (int64_t, bool, const vector &) override; + void delete_clause (int64_t, bool, const std::vector &) override; - void finalize_clause (int64_t, const vector &) override {} // skip + void finalize_clause (int64_t, const std::vector &) override { + } // skip void report_status (int, int64_t) override {} // skip diff --git a/src/sat/cadical/module.make b/src/sat/cadical/module.make index 74f5d23cb..52fbb3bed 100644 --- a/src/sat/cadical/module.make +++ b/src/sat/cadical/module.make @@ -4,6 +4,7 @@ src/sat/cadical/cadical_analyze.cpp \ src/sat/cadical/cadical_arena.cpp \ src/sat/cadical/cadical_assume.cpp \ src/sat/cadical/cadical_averages.cpp \ +src/sat/cadical/cadical_backbone.cpp \ src/sat/cadical/cadical_backtrack.cpp \ src/sat/cadical/cadical_backward.cpp \ src/sat/cadical/cadical_bins.cpp \ @@ -87,5 +88,7 @@ src/sat/cadical/cadical_veripbtracer.cpp \ src/sat/cadical/cadical_version.cpp \ src/sat/cadical/cadical_vivify.cpp \ src/sat/cadical/cadical_walk.cpp \ +src/sat/cadical/cadical_walk_full_occs.cpp \ +src/sat/cadical/cadical_warmup.cpp \ src/sat/cadical/cadical_watch.cpp \ src/sat/cadical/cadical_kitten.c diff --git a/src/sat/cadical/options.hpp b/src/sat/cadical/options.hpp index 1527da2e1..442c2d8e3 100644 --- a/src/sat/cadical/options.hpp +++ b/src/sat/cadical/options.hpp @@ -29,6 +29,11 @@ OPTION( arena, 1, 0, 1,0,0,1, "allocate clauses in arena") \ OPTION( arenacompact, 1, 0, 1,0,0,1, "keep clauses compact") \ OPTION( arenasort, 1, 0, 1,0,0,1, "sort clauses in arena") \ OPTION( arenatype, 3, 1, 3,0,0,1, "1=clause, 2=var, 3=queue") \ +OPTION( backbone, 1, 0, 2,0,0,1, "binary clause backbone") \ +OPTION( backboneeffort, 20, 0,1e5,0,0,1, "binary effort in per mile") \ +OPTION( backbonemaxrounds,1e3, 0,1e5,0,0,1, "backbone rounds limit") \ +OPTION( backbonerounds, 100, 0,1e5,0,0,1, "backbone rounds limit") \ +OPTION( backbonethresh, 5, 0,1e9,1,0,1, "delay if ticks smaller thresh*clauses") \ OPTION( binary, 1, 0, 1,0,0,1, "use binary proof format") \ OPTION( block, 0, 0, 1,0,1,1, "blocked clause elimination") \ OPTION( blockmaxclslim, 1e5, 1,2e9,2,0,1, "maximum clause size") \ @@ -60,7 +65,7 @@ OPTION( conditionint, 1e4, 1,2e9,0,0,1, "initial conflict interval") \ OPTION( conditionmaxeff, 1e7, 0,2e9,1,0,1, "maximum condition efficiency") \ OPTION( conditionmaxrat, 100, 1,2e9,1,0,1, "maximum clause variable ratio") \ OPTION( conditionmineff, 0, 0,2e9,1,0,1, "minimum condition efficiency") \ -OPTION( congruence, 1, 0, 1,0,0,1, "congruence closure") \ +OPTION( congruence, 1, 0, 1,0,1,1, "congruence closure") \ OPTION( congruenceand, 1, 0, 1,0,0,1, "extract AND gates") \ OPTION( congruenceandarity,1e6,2,5e7,0,0,1, "AND gate arity limit") \ OPTION( congruencebinaries,1, 0, 1,0,0,1, "extract binary and strengthen ternary clauses") \ @@ -113,12 +118,14 @@ OPTION( ematrailslow, 1e5, 1,2e9,0,0,1, "window slow trail") \ OPTION( exteagerreasons, 1, 0, 1,0,0,1, "eagerly ask for all reasons (0: only when needed)") \ OPTION( exteagerrecalc, 1, 0, 1,0,0,1, "after eagerly asking for reasons recalculate all levels (0: trust the external tool)") \ OPTION( externallrat, 0, 0, 1,0,0,1, "external lrat") \ -OPTION( factor, 1, 0, 1,0,1,1, "bounded variable addition") \ +OPTION( factor, 0, 0, 1,0,1,1, "bounded variable addition") \ OPTION( factorcandrounds, 2, 0,2e9,0,0,1, "candidates reduction rounds") \ +OPTION( factordelay, 4, 0, 12,0,0,1, "delay bounded variable addition between eliminations") \ OPTION( factoreffort, 50, 0,1e6,0,0,1, "relative effort per mille") \ OPTION( factoriniticks, 300, 1,1e6,0,0,1, "initial effort in millions") \ OPTION( factorsize, 5, 2,2e9,0,0,1, "clause size limit") \ OPTION( factorthresh, 7, 0,100,1,0,1, "delay if ticks smaller thresh*clauses") \ +OPTION( factorunbump, 1, 0, 1,0,1,1, "extension variable with lowest importance [1: as in kissat]") \ OPTION( fastelim, 1, 0, 1,0,1,1, "fast BVE during preprocessing") \ OPTION( fastelimbound, 8, 1,1e3,1,0,1, "fast BVE bound during preprocessing") \ OPTION( fastelimclslim, 1e2, 2,2e9,2,0,1, "fast BVE resolvent size limit") \ @@ -130,8 +137,9 @@ OPTION( flushint, 1e5, 1,2e9,0,0,1, "initial limit") \ OPTION( forcephase, 0, 0, 1,0,0,1, "always use initial phase") \ OPTION( frat, 0, 0, 2,0,0,1, "1=frat(lrat), 2=frat(drat)") \ OPTION( idrup, 0, 0, 1,0,0,1, "incremental proof format") \ -OPTION( ilb, 0, 0, 1,0,0,1, "ILB (incremental lazy backtrack)") \ -OPTION( ilbassumptions, 0, 0, 1,0,0,1, "trail reuse for assumptions (ILB-like)") \ +OPTION( ilb, 0, 0, 2,0,0,1, "ILB (incremental lazy backtrack) (0: no, 1: assumptions only, 2: everything)") \ +OPTION( incdecay, 1, 0, 4,0,0,1, "decay clauses when doing incremental clauses" ) \ +OPTION( incdecayint, 1e6, 1,2e9,0,0,1, "decay interval when doing incremental clauses" ) \ OPTION( inprobeint, 100, 1,2e9,0,0,1, "inprobing interval" ) \ OPTION( inprobing, 1, 0, 1,0,1,1, "enable probe inprocessing") \ OPTION( inprocessing, 1, 0, 1,0,1,1, "enable general inprocessing") \ @@ -143,7 +151,10 @@ OPTION( lidrup, 0, 0, 1,0,0,1, "linear incremental proof format") \ LOGOPT( log, 0, 0, 1,0,0,0, "enable logging") \ LOGOPT( logsort, 0, 0, 1,0,0,0, "sort logged clauses") \ OPTION( lrat, 0, 0, 1,0,0,1, "use LRAT proof format") \ -OPTION( lucky, 1, 0, 1,0,0,1, "search for lucky phases") \ +OPTION( lucky, 1, 0, 1,0,0,1, "lucky phases") \ +OPTION( luckyassumptions, 1, 0, 1,0,0,1, "lucky phases with assumptions") \ +OPTION( luckyearly, 1, 0, 1,0,0,1, "lucky phases before preprocessing") \ +OPTION( luckylate, 1, 0, 1,0,0,1, "lucky phases after preprocessing") \ OPTION( minimize, 1, 0, 1,0,0,1, "minimize learned clauses") \ OPTION( minimizedepth, 1e3, 0,1e3,0,0,1, "minimization depth") \ OPTION( minimizeticks, 1, 0, 1,0,0,1, "increment ticks in minimization") \ @@ -158,6 +169,12 @@ OPTION( probethresh, 0, 0,100,1,0,1, "delay if ticks smaller thresh*claus OPTION( profile, 2, 0, 4,0,0,0, "profiling level") \ QUTOPT( quiet, 0, 0, 1,0,0,0, "disable all messages") \ OPTION( radixsortlim, 32, 0,2e9,0,0,1, "radix sort limit") \ +OPTION( randec, 0, 0, 1,0,0,1, "random decisions") \ +OPTION( randecfocused, 1, 0, 1,0,0,1, "random decisions in focused mode") \ +OPTION( randecinit, 1e3, 2,2e9,0,0,1, "inital random decision interval") \ +OPTION( randecint, 500, 0,2e9,0,0,1, "random conflict length") \ +OPTION( randeclength, 10, 1,1e9,0,0,1, "length random decisions phases") \ +OPTION( randecstable, 0, 0, 1,0,0,1, "random decisions in stable mode") \ OPTION( realtime, 0, 0, 1,0,0,0, "real instead of process time") \ OPTION( recomputetier, 1, 0, 1,0,0,1, "recompute tiers") \ OPTION( reduce, 1, 0, 1,0,0,1, "reduce useless clauses") \ @@ -167,16 +184,18 @@ OPTION( reduceopt, 1, 0, 2,0,0,1, "0=prct,1=sqrt,2=max") \ OPTION( reducetarget, 75, 10,1e2,0,0,1, "reduce fraction in percent") \ OPTION( reducetier1glue, 2, 1,2e9,0,0,1, "glue of kept learned clauses") \ OPTION( reducetier2glue, 6, 1,2e9,0,0,1, "glue of tier two clauses") \ -OPTION( reluctant, 1024, 0,2e9,0,0,1, "reluctant doubling period") \ -OPTION( reluctantmax,1048576, 0,2e9,0,0,1, "reluctant doubling period") \ -OPTION( rephase, 1, 0, 1,0,0,1, "enable resetting phase") \ +OPTION( reluctant, 1, 0, 1,0,0,1, "stable reluctant doubling restarts") \ +OPTION( reluctantint, 1024, 0,2e9,0,0,1, "reluctant doubling period") \ +OPTION( reluctantmax,1048576, 0,2e9,0,0,1, "maximum reluctant doubling period") \ +OPTION( rephase, 1, 0, 2,0,0,1, "enable resetting phase (0=no,1=always,2=stable-only)") \ OPTION( rephaseint, 1e3, 1,2e9,0,0,1, "rephase interval") \ OPTION( report,reportdefault, 0, 1,0,0,1, "enable reporting") \ OPTION( reportall, 0, 0, 1,0,0,1, "report even if not successful") \ OPTION( reportsolve, 0, 0, 1,0,0,1, "use solving not process time") \ OPTION( restart, 1, 0, 1,0,0,1, "enable restarts") \ OPTION( restartint, 2, 1,2e9,0,0,1, "restart interval") \ -OPTION( restartmargin, 10, 0,1e2,0,0,1, "slow fast margin in percent") \ +OPTION( restartmarginfocused,10,0,25,0,0,1, "focused slow fast margin in percent") \ +OPTION( restartmarginstable ,25,0,25,0,0,1, "stable slow fast margin in percent") \ OPTION( restartreusetrail, 1, 0, 1,0,0,1, "enable trail reuse") \ OPTION( restoreall, 0, 0, 2,0,0,1, "restore all clauses (2=really)") \ OPTION( restoreflush, 0, 0, 1,0,0,1, "remove satisfied clauses") \ @@ -184,7 +203,7 @@ OPTION( reverse, 0, 0, 1,0,0,1, "reverse variable ordering") \ OPTION( score, 1, 0, 1,0,0,1, "use EVSIDS scores") \ OPTION( scorefactor, 950,500,1e3,0,0,1, "score factor per mille") \ OPTION( seed, 0, 0,2e9,0,0,1, "random seed") \ -OPTION( shrink, 3, 0, 3,0,0,1, "shrink conflict clause (1=only with binary, 2=minimize when pulling, 3=full)") \ +OPTION( shrink, 3, 0, 3,0,0,1, "shrink conflict clause (1=binary-only,2=minimize-on-pulling,3=full)") \ OPTION( shrinkreap, 1, 0, 1,0,0,1, "use a reap for shrinking") \ OPTION( shuffle, 0, 0, 1,0,0,1, "shuffle variables") \ OPTION( shufflequeue, 1, 0, 1,0,0,1, "shuffle variable queue") \ @@ -194,6 +213,7 @@ OPTION( stabilize, 1, 0, 1,0,0,1, "enable stabilizing phases") \ OPTION( stabilizeinit, 1e3, 1,2e9,0,0,1, "stabilizing interval") \ OPTION( stabilizeonly, 0, 0, 1,0,0,1, "only stabilizing phases") \ OPTION( stats, 0, 0, 1,0,0,1, "print all statistics at the end of the run") \ +OPTION( stubbornIOfocused, 0, 0, 1,0,0,1, "force phases to I/O in focused mode every once in a while (requires rephase==2)") \ OPTION( subsume, 1, 0, 1,0,1,1, "enable clause subsumption") \ OPTION( subsumebinlim, 1e4, 0,2e9,1,0,1, "watch list length limit") \ OPTION( subsumeclslim, 1e2, 0,2e9,2,0,1, "clause length limit") \ @@ -202,7 +222,7 @@ OPTION( subsumelimited, 1, 0, 1,0,0,1, "limit subsumption checks") \ OPTION( subsumemaxeff, 1e8, 0,2e9,1,0,1, "maximum subsuming efficiency") \ OPTION( subsumemineff, 0, 0,2e9,1,0,1, "minimum subsuming efficiency") \ OPTION( subsumeocclim, 1e2, 0,2e9,1,0,1, "watch list length limit") \ -OPTION( subsumestr, 1, 0, 1,0,0,1, "subsume strenghten") \ +OPTION( subsumestr, 1, 0, 1,0,0,1, "subsume strengthen") \ OPTION( sweep, 1, 0, 1,0,1,1, "enable SAT sweeping") \ OPTION( sweepclauses, 1024, 0,2e9,1,0,1, "environment clauses") \ OPTION( sweepcomplete, 0, 0, 1,0,0,1, "run SAT sweeping to completion") \ @@ -225,37 +245,41 @@ OPTION( ternaryocclim, 1e2, 1,2e9,2,0,1, "ternary occurrence limit") \ OPTION( ternaryrounds, 2, 1, 16,1,0,1, "maximum ternary rounds") \ OPTION( ternarythresh, 6, 0,100,1,0,1, "delay if ticks smaller thresh*clauses") \ OPTION( tier1limit, 50, 0,100,0,0,1, "limit of tier1 usage in percentage") \ +OPTION( tier1minglue, 0, 0,100,0,0,1, "lowest tier1 limit") \ OPTION( tier2limit, 90, 0,100,0,0,1, "limit of tier2 usage in percentage") \ +OPTION( tier2minglue, 0, 0,100,0,0,1, "lowest tier2 limit") \ OPTION( transred, 1, 0, 1,0,1,1, "transitive reduction of BIG") \ OPTION( transredeffort, 1e2, 1,1e5,1,0,1, "relative efficiency per mille") \ OPTION( transredmaxeff, 1e8, 0,2e9,1,0,1, "maximum efficiency") \ OPTION( transredmineff, 0, 0,2e9,1,0,1, "minimum efficiency") \ QUTOPT( verbose, 0, 0, 3,0,0,0, "more verbose messages") \ -OPTION( veripb, 0, 0, 4,0,0,1, "odd=checkdeletions, > 2=drat") \ +OPTION( veripb, 0, 0, 4,0,0,1, "odd=check-deletions, >2 drat") \ OPTION( vivify, 1, 0, 1,0,1,1, "vivification") \ OPTION( vivifycalctier, 0, 0, 1,0,0,1, "recalculate tier limits") \ OPTION( vivifydemote, 0, 0, 1,0,1,1, "demote irredundant or delete directly") \ OPTION( vivifyeffort, 50, 0,1e5,1,0,1, "overall efficiency per mille") \ OPTION( vivifyflush, 1, 0, 1,1,0,1, "flush subsumed before vivification rounds") \ OPTION( vivifyinst, 1, 0, 1,0,0,1, "instantiate last literal when vivify") \ -OPTION( vivifyirred, 1, 0, 1,0,1,1, "vivification irred") \ +OPTION( vivifyirred, 1, 0, 1,0,0,1, "vivification of irredundant clauses") \ OPTION( vivifyirredeff, 3, 1,100,1,0,1, "irredundant efficiency per mille") \ OPTION( vivifyonce, 0, 0, 2,0,0,1, "vivify once: 1=red, 2=red+irr") \ OPTION( vivifyretry, 0, 0, 5,0,0,1, "re-vivify clause if vivify was successful") \ OPTION( vivifyschedmax, 5e3, 10,2e9,0,0,1, "maximum schedule size") \ OPTION( vivifythresh, 20, 0,100,1,0,1, "delay if ticks smaller thresh*clauses") \ -OPTION( vivifytier1, 1, 0, 1,0,1,1, "vivification tier1") \ +OPTION( vivifytier1, 1, 0, 1,0,0,1, "vivification tier1") \ OPTION( vivifytier1eff, 4, 0,100,1,0,1, "relative tier1 effort") \ -OPTION( vivifytier2, 1, 0, 1,0,1,1, "vivification tier2") \ +OPTION( vivifytier2, 1, 0, 1,0,0,1, "vivification tier2") \ OPTION( vivifytier2eff, 2, 1,100,1,0,1, "relative tier2 effort") \ -OPTION( vivifytier3, 1, 0, 1,0,1,1, "vivification tier3") \ +OPTION( vivifytier3, 1, 0, 1,0,0,1, "vivification tier3") \ OPTION( vivifytier3eff, 1, 1,100,1,0,1, "relative tier3 effort") \ OPTION( walk, 1, 0, 1,0,0,1, "enable random walks") \ -OPTION( walkeffort, 20, 1,1e5,1,0,1, "relative efficiency per mille") \ -OPTION( walkmaxeff, 1e7, 0,2e9,1,0,1, "maximum efficiency") \ +OPTION( walkeffort, 80, 1,1e5,1,0,1, "relative efficiency per mille") \ +OPTION( walkfullocc, 0, 0, 1,1,0,1, "use Kissat's full occurrences instead of the single watched") \ +OPTION( walkmaxeff, 1e7, 0,2e9,1,0,1, "maximum efficiency (in 1e3 ticks)") \ OPTION( walkmineff, 0, 0,1e7,1,0,1, "minimum efficiency") \ OPTION( walknonstable, 1, 0, 1,0,0,1, "walk in non-stabilizing phase") \ OPTION( walkredundant, 0, 0, 1,0,0,1, "walk redundant clauses too") \ +OPTION( warmup, 1, 0, 1,0,0,1, "warmup before walk using propagation") \ // Note, keep an empty line right before this line because of the last '\'! // Also keep those single spaces after 'OPTION(' for proper sorting. diff --git a/src/sat/cadical/parse.hpp b/src/sat/cadical/parse.hpp index a37117f43..0e5feeb4a 100644 --- a/src/sat/cadical/parse.hpp +++ b/src/sat/cadical/parse.hpp @@ -4,6 +4,7 @@ #include "global.h" #include +#include #include ABC_NAMESPACE_CXX_HEADER_START @@ -34,6 +35,8 @@ class Parser { const char *parse_string (const char *str, char prev); const char *parse_positive_int (int &ch, int &res, const char *name); + const char *parse_positive_uint64_t (int &ch, uint64_t &res, + const char *name); const char *parse_lit (int &ch, int &lit, int &vars, int strict); const char *parse_dimacs_non_profiled (int &vars, int strict); const char *parse_solution_non_profiled (); diff --git a/src/sat/cadical/phases.hpp b/src/sat/cadical/phases.hpp index 98e54d439..08a1aed80 100644 --- a/src/sat/cadical/phases.hpp +++ b/src/sat/cadical/phases.hpp @@ -3,18 +3,19 @@ #include "global.h" +#include + ABC_NAMESPACE_CXX_HEADER_START namespace CaDiCaL { struct Phases { - vector best; // The current largest trail phase. - vector forced; // Forced through 'phase'. - vector min; // The current minimum unsatisfied phase. - vector prev; // Previous during local search. - vector saved; // The actual saved phase. - vector target; // The current target phase. + std::vector best; // The current largest trail phase. + std::vector forced; // Forced through 'phase'. + std::vector prev; // Previous during local search. + std::vector saved; // The actual saved phase. + std::vector target; // The current target phase. }; } // namespace CaDiCaL diff --git a/src/sat/cadical/profile.hpp b/src/sat/cadical/profile.hpp index f3799389b..c4fc2c219 100644 --- a/src/sat/cadical/profile.hpp +++ b/src/sat/cadical/profile.hpp @@ -51,6 +51,7 @@ struct Internal; PROFILE (analyze, 3) \ MROFILE (analyzestable, 4) \ MROFILE (analyzeunstable, 4) \ + PROFILE (backbone, 2) \ PROFILE (backward, 3) \ PROFILE (block, 2) \ PROFILE (bump, 4) \ @@ -107,7 +108,13 @@ struct Internal; PROFILE (transred, 3) \ PROFILE (unstable, 2) \ PROFILE (vivify, 2) \ - PROFILE (walk, 2) + PROFILE (walk, 2) \ + PROFILE (walkpick, 3) \ + PROFILE (walkbreak, 4) \ + PROFILE (walkflip, 3) \ + PROFILE (walkflipbroken, 4) \ + PROFILE (walkflipWL, 4) \ + PROFILE (warmup, 3) /*------------------------------------------------------------------------*/ @@ -173,11 +180,11 @@ struct Profiles { do { \ NON_CADICAL_QUIET_PROFILE_CODE (const double N = time (); \ const int L = internal->opts.profile;) \ - if (!preprocessing && !lookingahead) { \ + if (!internal->preprocessing && !internal->lookingahead) { \ NON_CADICAL_QUIET_PROFILE_CODE ( \ - if (stable && internal->profiles.stable.level <= L) \ + if (internal->stable && internal->profiles.stable.level <= L) \ internal->stop_profiling (internal->profiles.stable, N); \ - if (!stable && internal->profiles.unstable.level <= L) \ + if (!internal->stable && internal->profiles.unstable.level <= L) \ internal->stop_profiling (internal->profiles.unstable, N); \ if (internal->profiles.search.level <= L) \ internal->stop_profiling (internal->profiles.search, N);) \ @@ -197,20 +204,21 @@ struct Profiles { #define STOP_SIMPLIFIER(S, M) \ do { \ NON_CADICAL_QUIET_PROFILE_CODE ( \ - const double N = time (); const int L = internal->opts.profile; \ + const double N = internal->time (); \ + const int L = internal->opts.profile; \ if (internal->profiles.S.level <= L) \ internal->stop_profiling (internal->profiles.S, N); \ if (internal->profiles.simplify.level <= L) \ internal->stop_profiling (internal->profiles.simplify, N);) \ reset_mode (M); \ reset_mode (SIMPLIFY); \ - if (!preprocessing && !lookingahead) { \ + if (!internal->preprocessing && !internal->lookingahead) { \ NON_CADICAL_QUIET_PROFILE_CODE ( \ if (internal->profiles.search.level <= L) \ internal->start_profiling (internal->profiles.search, N); \ - if (stable && internal->profiles.stable.level <= L) \ + if (internal->stable && internal->profiles.stable.level <= L) \ internal->start_profiling (internal->profiles.stable, N); \ - if (!stable && internal->profiles.unstable.level <= L) \ + if (!internal->stable && internal->profiles.unstable.level <= L) \ internal->start_profiling (internal->profiles.unstable, N);) \ set_mode (SEARCH); \ } \ @@ -221,17 +229,18 @@ struct Profiles { #define START_INNER_WALK() \ do { \ - require_mode (SEARCH); \ - CADICAL_assert (!preprocessing); \ + require_mode (Mode::SEARCH); \ + CADICAL_assert (!internal->preprocessing); \ NON_CADICAL_QUIET_PROFILE_CODE ( \ - const double N = time (); const int L = internal->opts.profile; \ - if (stable && internal->profiles.stable.level <= L) \ + const double N = internal->time (); \ + const int L = internal->opts.profile; \ + if (internal->stable && internal->profiles.stable.level <= L) \ internal->stop_profiling (internal->profiles.stable, N); \ - if (!stable && internal->profiles.unstable.level <= L) \ + if (!internal->stable && internal->profiles.unstable.level <= L) \ internal->stop_profiling (internal->profiles.unstable, N); \ if (internal->profiles.walk.level <= L) \ internal->start_profiling (internal->profiles.walk, N);) \ - set_mode (WALK); \ + set_mode (Mode::WALK); \ } while (0) /*------------------------------------------------------------------------*/ @@ -239,16 +248,16 @@ struct Profiles { #define STOP_INNER_WALK() \ do { \ - require_mode (SEARCH); \ - CADICAL_assert (!preprocessing); \ + require_mode (Mode::SEARCH); \ + CADICAL_assert (!internal->preprocessing); \ reset_mode (WALK); \ NON_CADICAL_QUIET_PROFILE_CODE ( \ const double N = time (); const int L = internal->opts.profile; \ if (internal->profiles.walk.level <= L) \ internal->stop_profiling (internal->profiles.walk, N); \ - if (stable && internal->profiles.stable.level <= L) \ + if (internal->stable && internal->profiles.stable.level <= L) \ internal->start_profiling (internal->profiles.stable, N); \ - if (!stable && internal->profiles.unstable.level <= L) \ + if (!internal->stable && internal->profiles.unstable.level <= L) \ internal->start_profiling (internal->profiles.unstable, N); \ internal->profiles.walk.started = (N);) \ } while (0) @@ -258,10 +267,10 @@ struct Profiles { #define START_OUTER_WALK() \ do { \ - require_mode (SEARCH); \ - CADICAL_assert (!preprocessing); \ + require_mode (Mode::SEARCH); \ + CADICAL_assert (!internal->preprocessing); \ NON_CADICAL_QUIET_PROFILE_CODE (START (walk);) \ - set_mode (WALK); \ + set_mode (Mode::WALK); \ } while (0) /*------------------------------------------------------------------------*/ @@ -269,8 +278,8 @@ struct Profiles { #define STOP_OUTER_WALK() \ do { \ - require_mode (SEARCH); \ - CADICAL_assert (!preprocessing); \ + require_mode (Mode::SEARCH); \ + CADICAL_assert (!internal->preprocessing); \ reset_mode (WALK); \ NON_CADICAL_QUIET_PROFILE_CODE (STOP (walk);) \ } while (0) diff --git a/src/sat/cadical/proof.hpp b/src/sat/cadical/proof.hpp index 69be6b080..b97e57533 100644 --- a/src/sat/cadical/proof.hpp +++ b/src/sat/cadical/proof.hpp @@ -3,6 +3,9 @@ #include "global.h" +#include "tracer.hpp" +#include + ABC_NAMESPACE_CXX_HEADER_START namespace CaDiCaL { @@ -23,19 +26,20 @@ class Proof { Internal *internal; - vector clause; // of external literals - vector proof_chain; // LRAT style proof chain of clause - int64_t clause_id; // id of added clause + std::vector clause; // of external literals + std::vector proof_chain; // LRAT style proof chain of clause + int64_t clause_id; // id of added clause bool redundant; + int witness; // the 'tracers' - vector tracers; // tracers (ie checker) - vector file_tracers; // file tracers (ie LRAT tracer) + std::vector tracers; // tracers (ie checker) + std::vector file_tracers; // file tracers (ie LRAT tracer) void add_literal (int internal_lit); // add to 'clause' void add_literals (Clause *); // add to 'clause' - void add_literals (const vector &); // ditto + void add_literals (const std::vector &); // ditto void add_original_clause ( bool restore = false); // notify observers of original clauses @@ -57,35 +61,42 @@ public: void disconnect (Tracer *t); // Add original clauses to the proof (for online proof checking). // - void add_original_clause (int64_t, bool, const vector &); + void add_original_clause (int64_t, bool, const std::vector &); - void add_assumption_clause (int64_t, const vector &, - const vector &); - void add_assumption_clause (int64_t, int, const vector &); + void add_assumption_clause (int64_t, const std::vector &, + const std::vector &); + void add_assumption_clause (int64_t, int, const std::vector &); void add_assumption (int); - void add_constraint (const vector &); + void add_constraint (const std::vector &); void reset_assumptions (); // Add/delete original clauses to/from the proof using their original // external literals (from external->eclause) // - void add_external_original_clause (int64_t, bool, const vector &, + void add_external_original_clause (int64_t, bool, + const std::vector &, bool restore = false); - void delete_external_original_clause (int64_t, bool, const vector &); + void delete_external_original_clause (int64_t, bool, + const std::vector &); // Add derived (such as learned) clauses to the proof. // - void add_derived_empty_clause (int64_t, const vector &); - void add_derived_unit_clause (int64_t, int unit, const vector &); - void add_derived_clause (Clause *c, const vector &); - void add_derived_clause (int64_t, bool, const vector &, - const vector &); + void add_derived_empty_clause (int64_t, const std::vector &); + void add_derived_unit_clause (int64_t, int unit, + const std::vector &); + void add_derived_clause (Clause *c, const std::vector &); + void add_derived_clause (int64_t, bool, const std::vector &, + const std::vector &); + void add_derived_rat_clause (int64_t, bool, int, const std::vector &, + const std::vector &); + void add_derived_rat_clause (Clause *c, int w, + const std::vector &); // deletion of clauses. It comes in several variants, depending if the // clause should be restored or not - void delete_clause (int64_t, bool, const vector &); - void weaken_minus (int64_t, const vector &); - void weaken_plus (int64_t, const vector &); + void delete_clause (int64_t, bool, const std::vector &); + void weaken_minus (int64_t, const std::vector &); + void weaken_plus (int64_t, const std::vector &); void delete_unit_clause (int64_t id, const int lit); void delete_clause (Clause *); void weaken_minus (Clause *); @@ -94,21 +105,21 @@ public: void finalize_unit (int64_t, int); void finalize_external_unit (int64_t, int); - void finalize_clause (int64_t, const vector &c); + void finalize_clause (int64_t, const std::vector &c); void finalize_clause (Clause *); void report_status (int, int64_t); void begin_proof (int64_t); - void conclude_unsat (ConclusionType, const vector &); - void conclude_sat (const vector &model); - void conclude_unknown (const vector &trace); + void conclude_unsat (ConclusionType, const std::vector &); + void conclude_sat (const std::vector &model); + void conclude_unknown (const std::vector &trace); void solve_query (); // These two actually pretend to add and remove a clause. // void flush_clause (Clause *); // remove falsified literals - void strengthen_clause (Clause *, int, const vector &); - void otfs_strengthen_clause (Clause *, const vector &, - const vector &); + void strengthen_clause (Clause *, int, const std::vector &); + void otfs_strengthen_clause (Clause *, const std::vector &, + const std::vector &); void flush (); }; diff --git a/src/sat/cadical/queue.hpp b/src/sat/cadical/queue.hpp index 14e1f2da7..72b5561ac 100644 --- a/src/sat/cadical/queue.hpp +++ b/src/sat/cadical/queue.hpp @@ -61,6 +61,16 @@ struct Queue { last = idx; l.next = 0; } + + inline void bury (Links &links, int idx) { + Link &l = links[idx]; + if ((l.next = first)) + links[first].prev = idx; + else + last = idx; + first = idx; + l.prev = 0; + } }; } // namespace CaDiCaL diff --git a/src/sat/cadical/range.hpp b/src/sat/cadical/range.hpp index 7049dab42..55a95ed66 100644 --- a/src/sat/cadical/range.hpp +++ b/src/sat/cadical/range.hpp @@ -52,6 +52,7 @@ struct Clause; class Range { static unsigned inc (unsigned u) { return u + 1u; } + static unsigned dec (unsigned u) { return u - 1u; } class iterator { int idx; @@ -63,11 +64,36 @@ class Range { return a.idx != b.idx; } }; + + // Reverse iterator for iterating from max_var down to 1 + class reverse_iterator { + int idx; + + public: + reverse_iterator (int i) : idx (i) {} + void operator++ () { idx = dec (idx); } + const int &operator* () const { return idx; } + friend bool operator!= (const reverse_iterator &a, + const reverse_iterator &b) { + return a.idx != b.idx; + } + }; + int &n; public: + // forward iterator iterator begin () const { return CADICAL_assert (n >= 0), iterator (inc (0)); } iterator end () const { return CADICAL_assert (n >= 0), iterator (inc (n)); } + + // Reverse iteration methods + reverse_iterator rbegin () const { + return CADICAL_assert (n >= 0), reverse_iterator (n); + } + reverse_iterator rend () const { + return CADICAL_assert (n >= 0), reverse_iterator (0); + } + Range (int &m) : n (m) { CADICAL_assert (m >= 0); } }; diff --git a/src/sat/cadical/stats.hpp b/src/sat/cadical/stats.hpp index 4a6afa9c9..92a1ca0f3 100644 --- a/src/sat/cadical/stats.hpp +++ b/src/sat/cadical/stats.hpp @@ -21,6 +21,7 @@ struct Stats { int64_t conflicts = 0; // generated conflicts in 'propagate' int64_t decisions = 0; // number of decisions in 'decide' + int64_t searches = 0; // number of calls to solver 'solve' struct { int64_t cover = 0; // propagated during covered clause elimination @@ -29,16 +30,23 @@ struct Stats { int64_t search = 0; // propagated literals during search int64_t transred = 0; // propagated during transitive reduction int64_t vivify = 0; // propagated during vivification - int64_t walk = 0; // propagated during local search + int64_t backbone = 0; // propagated during backbones } propagations; struct { int64_t search[2] = {0}; + int64_t backbone = 0; int64_t factor = 0; int64_t probe = 0; int64_t sweep = 0; int64_t ternary = 0; int64_t vivify = 0; + int64_t walk = 0; + int64_t walkflip = 0; // ticks added to approximate walk + int64_t walkflipbroken = 0; // ticks added to approximate walk + int64_t walkflipWL = 0; // ticks added to approximate walk + int64_t walkbreak = 0; // ticks added to approximate walk + int64_t walkpick = 0; // ticks added to approximate walk } ticks; struct { @@ -120,11 +128,20 @@ struct Stats { int64_t walk = 0; // phases improved through random walked } rephased; + struct { + int64_t decision = 0; + int64_t dummydecision = 0; + int64_t conflicts = 0; + int64_t propagated = 0; + int64_t count = 0; + } warmup; + struct { int64_t count = 0; int64_t broken = 0; int64_t flips = 0; int64_t minimum = 0; + int64_t improved = 0; } walk; struct { @@ -232,21 +249,28 @@ struct Stats { int64_t decompositions = 0; // number of SCC + ELS int64_t vivifications = 0; // number of vivifications int64_t vivifychecks = 0; // checked clauses during vivification - int64_t vivifydecs = 0; // vivification decisions - int64_t vivifyreused = 0; // reused vivification decisions - int64_t vivifysched = 0; // scheduled clauses for vivification - int64_t vivifysubs = 0; // subsumed clauses during vivification - int64_t vivifysubred = 0; // subsumed clauses during vivification - int64_t vivifysubirr = 0; // subsumed clauses during vivification - int64_t vivifystrs = 0; // strengthened clauses during vivification - int64_t vivifystrirr = 0; // strengthened irredundant clause - int64_t vivifystred1 = 0; // strengthened redundant clause (1) - int64_t vivifystred2 = 0; // strengthened redundant clause (2) - int64_t vivifystred3 = 0; // strengthened redundant clause (3) - int64_t vivifyunits = 0; // units during vivification - int64_t vivifyimplied = 0; // implied during vivification - int64_t vivifyinst = 0; // instantiation during vivification - int64_t vivifydemote = 0; // demoting during vivification + int64_t vivifiedirred = + 0; // irredundant vivified clauses during vivification + int64_t vivifiedtier1 = 0; // tier-1 vivified clauses during vivification + int64_t vivifiedtier2 = 0; // tier-2 vivified clauses during vivification + int64_t vivifiedtier3 = 0; // tier-3 vivified clauses during vivification + int64_t vivifydecs = 0; // vivification decisions + int64_t vivifyflushed = + 0; // subsumed clauses during sorting in vivification + int64_t vivifyreused = 0; // reused vivification decisions + int64_t vivifysched = 0; // scheduled clauses for vivification + int64_t vivifysubs = 0; // subsumed clauses during vivification + int64_t vivifysubred = 0; // subsumed clauses during vivification + int64_t vivifysubirr = 0; // subsumed clauses during vivification + int64_t vivifystrs = 0; // strengthened clauses during vivification + int64_t vivifystrirr = 0; // strengthened irredundant clause + int64_t vivifystred1 = 0; // strengthened redundant clause (1) + int64_t vivifystred2 = 0; // strengthened redundant clause (2) + int64_t vivifystred3 = 0; // strengthened redundant clause (3) + int64_t vivifyunits = 0; // units during vivification + int64_t vivifyimplied = 0; // implied during vivification + int64_t vivifyinst = 0; // instantiation during vivification + int64_t vivifydemote = 0; // demoting during vivification int64_t transreds = 0; int64_t transitive = 0; struct { @@ -339,13 +363,24 @@ struct Stats { int64_t unused = 0; // number of unused variables int64_t active = 0; // number of active variables int64_t inactive = 0; // number of inactive variables + + int64_t incremental_decay = 0; + struct { + int64_t random_decisions = 0; // number of random decisions + int64_t random_decision_phases = + 0; // number of phases of random decision + } randec; + std::vector bump_used = {0, 0}; std::vector> used; // used clauses in focused mode struct { int64_t gates = 0; + int64_t and_gates = 0; int64_t ands = 0; + int64_t ite_gates = 0; int64_t ites = 0; + int64_t xor_gates = 0; int64_t xors = 0; int64_t units = 0; int64_t congruent = 0; @@ -353,6 +388,8 @@ struct Stats { int64_t unary_and = 0; int64_t unaries = 0; int64_t rewritten_ands = 0; + int64_t rewritten_xors = 0; + int64_t rewritten_ites = 0; int64_t simplified = 0; int64_t simplified_ands = 0; int64_t simplified_xors = 0; @@ -362,6 +399,13 @@ struct Stats { int64_t unary_ites = 0; } congruence; + struct { + int64_t rounds = 0; + int64_t units = 0; + int64_t phases = 0; + int64_t probes = 0; + } backbone; + Stats (); void print (Internal *); diff --git a/src/sat/cadical/terminal.hpp b/src/sat/cadical/terminal.hpp index a99346b7c..661f692a6 100644 --- a/src/sat/cadical/terminal.hpp +++ b/src/sat/cadical/terminal.hpp @@ -30,8 +30,8 @@ class Terminal { } void code (const char *str) { - if (!use_colors) - return; + // if (!use_colors) + // return; if (!connected) return; escape (); diff --git a/src/sat/cadical/tracer.hpp b/src/sat/cadical/tracer.hpp index d1a9f1137..b806cd8ff 100644 --- a/src/sat/cadical/tracer.hpp +++ b/src/sat/cadical/tracer.hpp @@ -40,9 +40,11 @@ public: // Notify the observer that a new clause has been derived. // Includes ID and whether the clause is redundant or irredundant // If antecedents are derived they will be included here. - // Arguments: ID, redundant, clause, antecedents + // If witness != 0 it is a RAT clause. + // Arguments: ID, redundant, clause, witness, antecedents // - virtual void add_derived_clause (int64_t, bool, const std::vector &, + virtual void add_derived_clause (int64_t, bool, int, + const std::vector &, const std::vector &) {} // Notify the observer that a clause is deleted. @@ -143,6 +145,13 @@ public: // will give the current trail as a vector. // virtual void conclude_unknown (const std::vector &) {} + + // Notify the observer that two literals are equivalent + // + // You receive literals, not variables. You can also get notified + // multiple times. You can also get notified of BVA variables, aka + // variables you did not declare. + virtual void notify_equivalence (int, int) {} }; /*--------------------------------------------------------------------------*/ diff --git a/src/sat/cadical/util.hpp b/src/sat/cadical/util.hpp index 3882c6ede..a048d1087 100644 --- a/src/sat/cadical/util.hpp +++ b/src/sat/cadical/util.hpp @@ -120,6 +120,20 @@ template static void enlarge_zero (vector &v, size_t N) { enlarge_init (v, N, (const T &) 0); } +// double the capacity until it fits. This is different from reserve +// that will allocate exactly the size requested, meaning that the +// amortized complexity is lost. +template static void reserve_at_least (vector &v, size_t N) { + if (N < v.capacity ()) + return; + size_t new_size = v.size (); + if (!new_size) + new_size = N; + while (new_size < N) + new_size *= 2; + v.reserve (new_size); +} + // Clean-up class for bad_alloc error safety. template struct DeferDeleteArray { diff --git a/src/sat/cadical/veripbtracer.hpp b/src/sat/cadical/veripbtracer.hpp index b9c47362f..1c9560ee4 100644 --- a/src/sat/cadical/veripbtracer.hpp +++ b/src/sat/cadical/veripbtracer.hpp @@ -3,9 +3,10 @@ #include "global.h" -ABC_NAMESPACE_CXX_HEADER_START +#include "file.hpp" +#include "tracer.hpp" -class FileTracer; +ABC_NAMESPACE_CXX_HEADER_START namespace CaDiCaL { @@ -54,18 +55,20 @@ class VeripbTracer : public FileTracer { #ifndef CADICAL_QUIET int64_t added, deleted; #endif - vector delete_ids; + std::vector delete_ids; void put_binary_zero (); void put_binary_lit (int external_lit); void put_binary_id (int64_t id, bool = false); // support veriPB + void veripb_add_derived_clause (int64_t, bool redundant, int witness, + const std::vector &clause); void veripb_add_derived_clause (int64_t, bool redundant, - const vector &clause, - const vector &chain); + const std::vector &clause, + const std::vector &chain); void veripb_add_derived_clause (int64_t, bool redundant, - const vector &clause); + const std::vector &clause); void veripb_begin_proof (int64_t reserved_ids); void veripb_delete_clause (int64_t id, bool redundant); void veripb_report_status (bool unsat, int64_t conflict_id); @@ -79,18 +82,19 @@ public: void connect_internal (Internal *i) override; void begin_proof (int64_t) override; - void add_original_clause (int64_t, bool, const vector &, + void add_original_clause (int64_t, bool, const std::vector &, bool = false) override {} // skip - void add_derived_clause (int64_t, bool, const vector &, - const vector &) override; + void add_derived_clause (int64_t, bool, int, const std::vector &, + const std::vector &) override; - void delete_clause (int64_t, bool, const vector &) override; - void finalize_clause (int64_t, const vector &) override {} // skip + void delete_clause (int64_t, bool, const std::vector &) override; + void finalize_clause (int64_t, const std::vector &) override { + } // skip void report_status (int, int64_t) override; - void weaken_minus (int64_t, const vector &) override; + void weaken_minus (int64_t, const std::vector &) override; void strengthen (int64_t) override; #ifndef CADICAL_QUIET diff --git a/src/sat/cadical/vivify.hpp b/src/sat/cadical/vivify.hpp index a5c6c0e10..58d2047ee 100644 --- a/src/sat/cadical/vivify.hpp +++ b/src/sat/cadical/vivify.hpp @@ -30,9 +30,7 @@ struct vivify_ref { // be able to iterate over them, but we provide the reference to them to // make sure that you do need to remember the order. struct Vivifier { - std::array, 4> refs_schedules; - std::vector &refs_schedule_tier1, &refs_schedule_tier2, - &refs_schedule_tier3, &refs_schedule_irred; + std::vector refs_schedule; std::array, 4> schedules; std::vector &schedule_tier1, &schedule_tier2, &schedule_tier3, &schedule_irred; @@ -44,21 +42,11 @@ struct Vivifier { int64_t ticks; std::vector> lrat_stack; Vivifier (Vivify_Mode mode_tier) - : refs_schedule_tier1 (refs_schedules[0]), - refs_schedule_tier2 (refs_schedules[1]), - refs_schedule_tier3 (refs_schedules[2]), - refs_schedule_irred (refs_schedules[3]), - schedule_tier1 (schedules[0]), schedule_tier2 (schedules[1]), + : schedule_tier1 (schedules[0]), schedule_tier2 (schedules[1]), schedule_tier3 (schedules[2]), schedule_irred (schedules[3]), tier (mode_tier) {} - void erase () { - erase_vector (refs_schedule_tier1); - erase_vector (refs_schedule_tier2); - erase_vector (refs_schedule_tier3); - erase_vector (refs_schedule_irred); - erase_vector (sorted); - } + void erase () { erase_vector (sorted); } }; } // namespace CaDiCaL diff --git a/src/sat/cadical/walk.hpp b/src/sat/cadical/walk.hpp new file mode 100644 index 000000000..cb71c15be --- /dev/null +++ b/src/sat/cadical/walk.hpp @@ -0,0 +1,97 @@ +#ifndef _walk_hpp_INCLUDED +#define _walk_hpp_INCLUDED + +#include "global.h" + +#include "clause.hpp" + +ABC_NAMESPACE_CXX_HEADER_START + +namespace CaDiCaL { +struct TaggedBinary { + int lit, other; +#if defined(LOGGING) || !defined(CADICAL_NDEBUG) + Clause *d; +#endif + TaggedBinary () + : lit (0), other (0) +#if defined(LOGGING) || !defined(CADICAL_NDEBUG) + , + d (nullptr) +#endif + { + CADICAL_assert (false); + }; + + TaggedBinary (Clause *c, int clit, int cother) + : lit (clit), other (cother) +#if defined(LOGGING) || !defined(CADICAL_NDEBUG) + , + d (c) +#endif + { +#ifdef LOGGING + CADICAL_assert (c->literals[0] == lit || c->literals[1] == lit); + CADICAL_assert (c->literals[0] == other || c->literals[1] == other); +#endif + +#ifndef LOGGING + (void) c; +#endif + } + + TaggedBinary (Clause *c) { + CADICAL_assert (c->size == 2); + lit = c->literals[0]; + other = c->literals[1]; +#if defined(LOGGING) || !defined(CADICAL_NDEBUG) + d = c; +#else + (void) c; +#endif + } +}; + +union clause_or_binary_raw { + Clause *clause; + TaggedBinary b; + clause_or_binary_raw () : clause (nullptr) {} +}; + +// We experimented with using +// +// using ClauseOrBinary = std::variant ; +// +// instead of hand-rolling our own below, but the performance cost on +// vlsat2_144_7585.cnf.xz with a conflict limit of 2M conflicts was a +// factor 4 with: +// +// c 12.76 6.96% walkflipbroken +// +// vs +// +// c 49.86 22.63 % walkflipbroken +// +// And this is without doing any but stuffing to make the structure +// fit into 64 bits. +struct ClauseOrBinary { + bool binary; + clause_or_binary_raw tagged; + ClauseOrBinary () : binary (false) { tagged.clause = nullptr; } + ClauseOrBinary (Clause *c) : binary (false) { tagged.clause = c; } + ClauseOrBinary (TaggedBinary &&c) : binary (true) { tagged.b = c; } + bool is_binary () const { return binary; } + Clause *clause () const { + CADICAL_assert (!binary); + return tagged.clause; + } + TaggedBinary &tagged_binary () { + CADICAL_assert (binary); + return tagged.b; + } +}; +} // namespace CaDiCaL + +ABC_NAMESPACE_CXX_HEADER_END + +#endif diff --git a/src/sat/cadical/watch.hpp b/src/sat/cadical/watch.hpp index 1acab094b..e854a7df5 100644 --- a/src/sat/cadical/watch.hpp +++ b/src/sat/cadical/watch.hpp @@ -6,6 +6,8 @@ #include #include +#include "clause.hpp" + ABC_NAMESPACE_CXX_HEADER_START namespace CaDiCaL { @@ -37,6 +39,9 @@ struct Watch { int size; Watch (int b, Clause *c) : clause (c), blit (b), size (c->size) {} + Watch (bool, int b, Clause *c) : clause (c), blit (b), size (2) { + CADICAL_assert (c->size == 2); + } Watch () {} bool binary () const { return size == 2; }