Merge pull request #464 from MyskYko/cadical-rel-2.2.0

update cadical to 2.2.0
This commit is contained in:
alanminko 2025-12-24 07:24:38 -08:00 committed by GitHub
commit f9e4430535
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
84 changed files with 6203 additions and 1895 deletions

View File

@ -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

View File

@ -7,6 +7,16 @@
#include <cstdio>
#include <vector>
/*========================================================================*/
// 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<int> &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<int> &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<int> &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<int> &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:

View File

@ -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*************************************************************

View File

@ -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

View File

@ -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

View File

@ -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<int> &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<int> &candidates,
std::vector<int> &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<int>::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<int> &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<int> 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

View File

@ -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);

View File

@ -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) {

View File

@ -561,7 +561,8 @@ void Checker::add_original_clause (int64_t id, bool, const vector<int> &c,
STOP (checking);
}
void Checker::add_derived_clause (int64_t id, bool, const vector<int> &c,
void Checker::add_derived_clause (int64_t id, bool, int,
const vector<int> &c,
const vector<int64_t> &) {
if (inconsistent)
return;
@ -627,7 +628,7 @@ void Checker::delete_clause (int64_t id, bool, const vector<int> &c) {
void Checker::add_assumption_clause (int64_t id, const vector<int> &c,
const vector<int64_t> &chain) {
add_derived_clause (id, true, c, chain);
add_derived_clause (id, true, 0, c, chain);
delete_clause (id, true, c);
}

View File

@ -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

View File

@ -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'.
//

File diff suppressed because it is too large Load Diff

View File

@ -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

View File

@ -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 ();

View File

@ -87,7 +87,7 @@ void DratTracer::drat_delete_clause (const vector<int> &clause) {
/*------------------------------------------------------------------------*/
void DratTracer::add_derived_clause (int64_t, bool,
void DratTracer::add_derived_clause (int64_t, bool, int,
const vector<int> &clause,
const vector<int64_t> &) {
if (file->closed ())

View File

@ -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 ();

View File

@ -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);

View File

@ -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,

View File

@ -1,6 +1,8 @@
#include "global.h"
#include "internal.hpp"
#include "util.hpp"
#include <cstdint>
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<int> &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);

View File

@ -2,6 +2,8 @@
#include "internal.hpp"
#include <algorithm>
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<int> &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<int> 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);

View File

@ -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++;

View File

@ -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));
}
}

View File

@ -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<int> &clause,
const vector<int64_t> &chain) {
if (file->closed ())

View File

@ -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<int> &clause,
const vector<int64_t> &) {
if (file->closed ())

View File

@ -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<int> &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 ();

View File

@ -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)) {

View File

@ -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<int> &clause,
const vector<int64_t> &chain) {
if (file->closed ())

View File

@ -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

View File

@ -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 ());

View File

@ -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<int> &c,
const vector<int64_t> &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<int> &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);
}

View File

@ -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<int> &clause,
const vector<int64_t> &chain) {
if (file->closed ())

View File

@ -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);

View File

@ -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);

View File

@ -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, "<num-clauses>");
err = parse_positive_uint64_t (ch, clauses, "<num-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, "<num-clauses>");
err = parse_positive_uint64_t (ch, clauses, "<num-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

View File

@ -8,8 +8,20 @@ namespace CaDiCaL {
void Internal::copy_phases (vector<signed char> &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<signed char> &dst) {
START (copy);
for (auto i : vars) {
if (vals[i])
dst[i] = vals[i];
}
STOP (copy);
}

View File

@ -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) {

View File

@ -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<int64_t> &chain) {
add_derived_clause ();
}
void Proof::add_derived_rat_clause (Clause *c, int w,
const vector<int64_t> &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<int> &c,
const vector<int64_t> &chain) {
LOG (c, "PROOF adding derived clause");
@ -306,6 +322,22 @@ void Proof::add_derived_clause (int64_t id, bool r, const vector<int> &c,
add_derived_clause ();
}
void Proof::add_derived_rat_clause (int64_t id, bool r, int l,
const vector<int> &c,
const vector<int64_t> &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<int> &c,
const vector<int64_t> &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 () {

View File

@ -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;

View File

@ -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)

View File

@ -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

View File

@ -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++) {

View File

@ -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;
}

View File

@ -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<int> &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 () {

View File

@ -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",

View File

@ -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) {}

View File

@ -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,

View File

@ -1,6 +1,8 @@
#include "global.h"
#include "internal.hpp"
#include "util.hpp"
#include <string>
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

View File

@ -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<int> &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<int> &clause,
const vector<int64_t> &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<int> &clause,
const vector<int64_t> &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);

View File

@ -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 =

View File

@ -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<Clause *> &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<Clause *> &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<Clause *> &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<int> &sorted,
Clause *conflict) {
@ -875,6 +802,7 @@ bool Internal::vivify_shrinkable (const std::vector<int> &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<int> &sorted, Clause *c,
std::vector<std::tuple<int, Clause *, bool>> &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<std::tuple<int, Clause *, bool>> &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<Clause *> &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<vivify_ref> &current_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<Clause *> &current_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;
}

View File

@ -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 <Clause*, TaggedBinary>;
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<Clause *> broken; // currently unsatisfied clauses
double epsilon; // smallest considered score
vector<double> table; // break value to score table
vector<double> 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<ClauseOrBinary> broken; // currently unsatisfied clauses
double epsilon; // smallest considered score
vector<double> table; // break value to score table
vector<double> scores; // scores of candidate literals
std::vector<int>
flips; // remember the flips compared to the last best saved model
int best_trail_pos;
int64_t minimum = INT64_MAX;
std::vector<signed char> best_values; // best model stored so far
double score (unsigned); // compute score from break count
#ifndef CADICAL_NDEBUG
std::vector<signed char> 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<int, 2> 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

View File

@ -0,0 +1,972 @@
#include "global.h"
#include "internal.hpp"
#include <cstdint>
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<Tagged> broken; // currently unsatisfied clauses
double epsilon; // smallest considered score
vector<double> table; // break value to score table
vector<double> scores; // scores of candidate literals
std::vector<int>
flips; // remember the flips compared to the last best saved model
int best_trail_pos;
int64_t minimum = INT64_MAX;
std::vector<signed char> best_values; // best model found so far
double score (unsigned); // compute score from break count
std::vector<Counter> tclauses;
std::vector<std::vector<Tagged>> tcounters;
using TOccs = std::vector<Tagged>;
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

View File

@ -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

View File

@ -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 *);

View File

@ -47,7 +47,7 @@ struct CheckerWatch {
: blit (b), size (c->size), clause (c) {}
};
typedef vector<CheckerWatch> CheckerWatcher;
typedef std::vector<CheckerWatch> 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<CheckerWatcher> watchers; // watchers of literals
vector<signed char> marks; // mark bits of literals
std::vector<CheckerWatcher> watchers; // watchers of literals
std::vector<signed char> 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<int> unsimplified; // original clause for reporting
vector<int> simplified; // clause for sorting
std::vector<int> unsimplified; // original clause for reporting
std::vector<int> simplified; // clause for sorting
vector<int> trail; // for propagation
std::vector<int> 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<int> &);
void import_clause (const std::vector<int> &);
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<int> &,
void add_original_clause (int64_t, bool, const std::vector<int> &,
bool = false) override;
void add_derived_clause (int64_t, bool, const vector<int> &,
const vector<int64_t> &) override;
void delete_clause (int64_t, bool, const vector<int> &) override;
void add_derived_clause (int64_t, bool, int, const std::vector<int> &,
const std::vector<int64_t> &) override;
void delete_clause (int64_t, bool, const std::vector<int> &) override;
void finalize_clause (int64_t, const vector<int> &) 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<int> &,
const vector<int64_t> &) override;
void finalize_clause (int64_t, const std::vector<int> &) 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<int> &,
const std::vector<int64_t> &) override;
void print_stats () override;
void dump (); // for debugging purposes only
};

View File

@ -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

View File

@ -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<LitClausePair> pos_lhs_ids;
vector<LitClausePair> 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<int> 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<int, 16> &ncs) : nonces (ncs) {}
std::array<int, 16> &nonces;
size_t operator() (const Gate *const g) const;
Hash (std::array<uint64_t, 16> &ncs) : nonces (ncs) {}
const std::array<uint64_t, 16> &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<Clause*> extra_clauses;
vector<Clause *> extra_clauses;
vector<CompactBinary> binaries;
std::vector<std::pair<size_t, size_t>> offsetsize;
bool full_watching = false;
std::array<int, 16> nonces;
std::array<uint64_t, 16> nonces;
typedef unordered_set<Gate *, Hash, GateEqualTo> GatesTable;
vector<bool> 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<LRAT_ID> & = {},
const std::vector<LRAT_ID> & = {});
bool merge_literals_lrat (int lit, int other,
const std::vector<LRAT_ID> & = {},
const std::vector<LRAT_ID> & = {});
bool merge_literals (Gate *g, Gate *h, int lit, int other,
const std::vector<LRAT_ID> & = {},
const std::vector<LRAT_ID> & = {});
bool merge_literals (int lit, int other,
const std::vector<LRAT_ID> & = {},
const std::vector<LRAT_ID> & = {});
// 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<LRAT_ID> & = {},
const std::vector<LRAT_ID> & = {});
// proof production
vector<LitClausePair> 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<LRAT_ID> &chain,
const std::vector<LitClausePair> &c);
void push_id_on_chain (std::vector<LRAT_ID> &chain,
const my_dummy_optional &c);
// TODO: does nothing except pushing on the stack, remove!
void push_id_on_chain (std::vector<LRAT_ID> &chain, Rewrite rewrite, int);
void update_and_gate_build_lrat_chain (
Gate *g, Gate *h, std::vector<LRAT_ID> &extra_reasons_lit,
std::vector<LRAT_ID> &extra_reasons_ulit, bool remove_units = true);
std::vector<LRAT_ID> &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<LRAT_ID> &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<int> &rhs, Gate *except = nullptr);
// rhs is sorted, so passing by copy
Gate *find_gate_lits (const vector<int> &rhs, Gate_Type typ,
Gate *except = nullptr);
Gate *find_xor_lits (const vector<int> &rhs);
Gate *find_and_lits (vector<int> &rhs, Gate *except = nullptr);
Gate *
find_gate_lits (vector<int> &rhs,
Gate_Type typ, // rhs unchanged but swapped back and forth
Gate *except = nullptr);
Gate *find_xor_lits (vector<int> &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<Clause*> &candidates);
void init_xor_gate_extraction (std::vector<Clause *> &candidates);
LRAT_ID check_and_add_to_proof_chain (vector<int> &clause);
void add_xor_matching_proof_chain (Gate *g, int lhs1,
const vector<LitClausePair> &,
@ -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<LitClausePair> &,
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<LitClausePair> &,
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<LRAT_ID> &reasons_implication,
std::vector<LRAT_ID> &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<LitClausePair> &clauses,
std::vector<LRAT_ID> &reasons_implication,
std::vector<LRAT_ID> &reasons_back);
void merge_ite_gate_same_then_else_lrat (
std::vector<LitClausePair> &clauses,
std::vector<LRAT_ID> &reasons_implication,
std::vector<LRAT_ID> &reasons_back);
void simplify_ite_gate_then_else_set (
Gate *g, std::vector<LRAT_ID> &reasons_implication,
std::vector<LRAT_ID> &reasons_back, size_t idx1, size_t idx2);

View File

@ -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() \

View File

@ -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<int> &);
void drat_delete_clause (const vector<int> &);
void drat_add_clause (const std::vector<int> &);
void drat_delete_clause (const std::vector<int> &);
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<int> &,
void add_original_clause (int64_t, bool, const std::vector<int> &,
bool = false) override {} // skip
void add_derived_clause (int64_t, bool, const vector<int> &,
const vector<int64_t> &) override;
void add_derived_clause (int64_t, bool, int, const std::vector<int> &,
const std::vector<int64_t> &) override;
void delete_clause (int64_t, bool, const vector<int> &) override;
void delete_clause (int64_t, bool, const std::vector<int> &) override;
void finalize_clause (int64_t, const vector<int> &) override {} // skip
void finalize_clause (int64_t, const std::vector<int> &) override {
} // skip
void report_status (int, int64_t) override {} // skip

View File

@ -6,6 +6,7 @@
/*------------------------------------------------------------------------*/
#include "range.hpp"
#include <cstdint>
#include <unordered_map>
#include <vector>
@ -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);

View File

@ -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
}
};

View File

@ -3,6 +3,11 @@
#include "global.h"
#include "file.hpp"
#include "tracer.hpp"
#include <vector>
ABC_NAMESPACE_CXX_HEADER_START
namespace CaDiCaL {
@ -19,19 +24,19 @@ class FratTracer : public FileTracer {
int64_t finalized, original;
#endif
vector<int64_t> delete_ids;
std::vector<int64_t> 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<int> &);
void frat_add_derived_clause (int64_t, const vector<int> &);
void frat_add_derived_clause (int64_t, const vector<int> &,
const vector<int64_t> &);
void frat_delete_clause (int64_t, const vector<int> &);
void frat_finalize_clause (int64_t, const vector<int> &);
void frat_add_original_clause (int64_t, const std::vector<int> &);
void frat_add_derived_clause (int64_t, const std::vector<int> &);
void frat_add_derived_clause (int64_t, const std::vector<int> &,
const std::vector<int64_t> &);
void frat_delete_clause (int64_t, const std::vector<int> &);
void frat_finalize_clause (int64_t, const std::vector<int> &);
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<int> &,
void add_original_clause (int64_t, bool, const std::vector<int> &,
bool = false) override;
void add_derived_clause (int64_t, bool, const vector<int> &,
const vector<int64_t> &) override;
void add_derived_clause (int64_t, bool, int, const std::vector<int> &,
const std::vector<int64_t> &) override;
void delete_clause (int64_t, bool, const vector<int> &) override;
void delete_clause (int64_t, bool, const std::vector<int> &) override;
void finalize_clause (int64_t, const vector<int> &) override;
void finalize_clause (int64_t, const std::vector<int> &) override;
void report_status (int, int64_t) override {} // skip

View File

@ -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<int> imported_clause;
vector<int> assumptions;
std::vector<int> imported_clause;
std::vector<int> 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<int> &clause);
void idrup_delete_clause (int64_t id, const vector<int> &clause);
void idrup_add_restored_clause (const vector<int> &clause);
void idrup_add_original_clause (const vector<int> &clause);
void idrup_conclude_and_delete (const vector<int64_t> &conclusion);
void idrup_add_derived_clause (const std::vector<int> &clause);
void idrup_delete_clause (int64_t id, const std::vector<int> &clause);
void idrup_add_restored_clause (const std::vector<int> &clause);
void idrup_add_original_clause (const std::vector<int> &clause);
void idrup_conclude_and_delete (const std::vector<int64_t> &conclusion);
void idrup_report_status (int status);
void idrup_conclude_sat (const vector<int> &model);
void idrup_conclude_unknown (const vector<int> &trail);
void idrup_conclude_sat (const std::vector<int> &model);
void idrup_conclude_unknown (const std::vector<int> &trail);
void idrup_solve_query ();
public:
@ -75,18 +76,19 @@ public:
~IdrupTracer ();
// proof section:
void add_derived_clause (int64_t, bool, const vector<int> &,
const vector<int64_t> &) override;
void add_assumption_clause (int64_t, const vector<int> &,
const vector<int64_t> &) override;
void weaken_minus (int64_t, const vector<int> &) override;
void delete_clause (int64_t, bool, const vector<int> &) override;
void add_original_clause (int64_t, bool, const vector<int> &,
void add_derived_clause (int64_t, bool, int, const std::vector<int> &,
const std::vector<int64_t> &) override;
void add_assumption_clause (int64_t, const std::vector<int> &,
const std::vector<int64_t> &) override;
void weaken_minus (int64_t, const std::vector<int> &) override;
void delete_clause (int64_t, bool, const std::vector<int> &) override;
void add_original_clause (int64_t, bool, const std::vector<int> &,
bool = false) override;
void report_status (int, int64_t) override;
void conclude_sat (const vector<int> &) override;
void conclude_unsat (ConclusionType, const vector<int64_t> &) override;
void conclude_unknown (const vector<int> &) override;
void conclude_sat (const std::vector<int> &) override;
void conclude_unsat (ConclusionType,
const std::vector<int64_t> &) override;
void conclude_unknown (const std::vector<int> &) 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<int> &) override {}
void finalize_clause (int64_t, const std::vector<int> &) override {}
void strengthen (int64_t) override {}
void add_constraint (const vector<int> &) override {}
void add_constraint (const std::vector<int> &) override {}
// logging and file io
void connect_internal (Internal *i) override;

View File

@ -23,6 +23,7 @@
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <variant>
// 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<int> 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<int> &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<signed char> &); // reset argument to zero
void copy_phases (vector<signed char> &); // copy 'saved' to argument
void save_assigned_phases (
vector<signed char> &); // 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<std::tuple<int, Clause *, bool>> &);
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<int> &candidates,
std::vector<int> &units,
const int64_t ticks_limit,
int64_t &ticks, unsigned inconsistent);
void schedule_backbone_cands (std::vector<int> &candidates);
void keep_backbone_candidates (const std::vector<int> &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;
}

View File

@ -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<int> imported_clause;
vector<int> assumptions;
vector<int64_t> imported_chain;
vector<int64_t> batch_weaken;
vector<int64_t> batch_delete;
vector<int64_t> batch_restore;
std::vector<int> imported_clause;
std::vector<int> assumptions;
std::vector<int64_t> imported_chain;
std::vector<int64_t> batch_weaken;
std::vector<int64_t> batch_delete;
std::vector<int64_t> 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<int> &clause,
const vector<int64_t> &chain);
void lidrup_delete_clause (int64_t id); //, const vector<int> &clause);
void lidrup_add_derived_clause (int64_t id,
const std::vector<int> &clause,
const std::vector<int64_t> &chain);
void
lidrup_add_restored_clause (int64_t id); //, const vector<int> &clause);
void lidrup_add_original_clause (int64_t id, const vector<int> &clause);
void lidrup_conclude_and_delete (const vector<int64_t> &conclusion);
lidrup_delete_clause (int64_t id); //, const std::vector<int> &clause);
void lidrup_add_restored_clause (
int64_t id); //, const std::vector<int> &clause);
void lidrup_add_original_clause (int64_t id,
const std::vector<int> &clause);
void lidrup_conclude_and_delete (const std::vector<int64_t> &conclusion);
void lidrup_report_status (int status);
void lidrup_conclude_sat (const vector<int> &model);
void lidrup_conclude_unknown (const vector<int> &trail);
void lidrup_conclude_sat (const std::vector<int> &model);
void lidrup_conclude_unknown (const std::vector<int> &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<int> &,
const vector<int64_t> &) override;
void add_assumption_clause (int64_t, const vector<int> &,
const vector<int64_t> &) override;
void weaken_minus (int64_t, const vector<int> &) override;
void delete_clause (int64_t, bool, const vector<int> &) override;
void add_original_clause (int64_t, bool, const vector<int> &,
void add_derived_clause (int64_t, bool, int, const std::vector<int> &,
const std::vector<int64_t> &) override;
void add_assumption_clause (int64_t, const std::vector<int> &,
const std::vector<int64_t> &) override;
void weaken_minus (int64_t, const std::vector<int> &) override;
void delete_clause (int64_t, bool, const std::vector<int> &) override;
void add_original_clause (int64_t, bool, const std::vector<int> &,
bool = false) override;
void report_status (int, int64_t) override;
void conclude_sat (const vector<int> &) override;
void conclude_unsat (ConclusionType, const vector<int64_t> &) override;
void conclude_unknown (const vector<int> &) override;
void conclude_sat (const std::vector<int> &) override;
void conclude_unsat (ConclusionType,
const std::vector<int64_t> &) override;
void conclude_unknown (const std::vector<int> &) 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<int> &) override {}
void finalize_clause (int64_t, const std::vector<int> &) override {}
void strengthen (int64_t) override {}
void add_constraint (const vector<int> &) override {}
void add_constraint (const std::vector<int> &) override {}
// logging and file io
void connect_internal (Internal *i) override;

View File

@ -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

View File

@ -4,6 +4,8 @@
#include "global.h"
/*------------------------------------------------------------------------*/
#include "tracer.hpp"
#include <cstdint>
#include <unordered_map>
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<signed char> checked_lits;
vector<signed char> marks; // mark bits of literals
unordered_map<int64_t, vector<int>> clauses_to_reconstruct;
vector<int> assumptions;
vector<int> constraint;
std::vector<signed char> checked_lits;
std::vector<signed char> marks; // mark bits of literals
std::unordered_map<int64_t, std::vector<int>> clauses_to_reconstruct;
std::vector<int> assumptions;
std::vector<int> 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<int> imported_clause; // original clause for reporting
vector<int64_t> assumption_clauses;
std::vector<int> imported_clause; // original clause for reporting
std::vector<int64_t> assumption_clauses;
void enlarge_vars (int64_t idx);
void import_literal (int lit);
void import_clause (const vector<int> &);
void import_clause (const std::vector<int> &);
static const unsigned num_nonces = 4;
@ -94,15 +96,16 @@ class LratChecker : public StatTracer {
LratCheckerClause *new_clause ();
void delete_clause (LratCheckerClause *);
bool check (vector<int64_t>); // check RUP
bool check_resolution (vector<int64_t>); // check resolution
bool check_blocked (vector<int64_t>); // check ER
bool check (std::vector<int64_t>); // check RUP
bool check_resolution (std::vector<int64_t>); // check resolution
bool check_blocked (std::vector<int64_t>); // 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<int> &,
void add_original_clause (int64_t, bool, const std::vector<int> &,
bool restore) override;
void restore_clause (int64_t, const vector<int> &);
void restore_clause (int64_t, const std::vector<int> &);
// check the proof chain for the new clause and add it to the checker
void add_derived_clause (int64_t, bool, const vector<int> &,
const vector<int64_t> &) override;
void add_derived_clause (int64_t, bool, int, const std::vector<int> &,
const std::vector<int64_t> &) override;
// check if the clause is present and delete it from the checker
void delete_clause (int64_t, bool, const vector<int> &) override;
void delete_clause (int64_t, bool, const std::vector<int> &) override;
// check if the clause is present and delete it from the checker
void weaken_minus (int64_t, const vector<int> &) override;
void weaken_minus (int64_t, const std::vector<int> &) override;
// check if the clause is present and delete it from the checker
void finalize_clause (int64_t, const vector<int> &) override;
void finalize_clause (int64_t, const std::vector<int> &) 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<int> &,
const vector<int64_t> &) override;
void add_assumption_clause (int64_t, const std::vector<int> &,
const std::vector<int64_t> &) override;
// mark lit as assumption
void add_assumption (int) override;
// mark lits as constraint
void add_constraint (const vector<int> &) override;
void add_constraint (const std::vector<int> &) 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<int64_t> &) override;
void conclude_unsat (ConclusionType,
const std::vector<int64_t> &) override;
void print_stats () override;
void dump (); // for debugging purposes only

View File

@ -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<int64_t> delete_ids;
std::vector<int64_t> 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<int> &,
const vector<int64_t> &);
void lrat_add_clause (int64_t, const std::vector<int> &,
const std::vector<int64_t> &);
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<int> &,
void add_original_clause (int64_t, bool, const std::vector<int> &,
bool = false) override {} // skip
void add_derived_clause (int64_t, bool, const vector<int> &,
const vector<int64_t> &) override;
void add_derived_clause (int64_t, bool, int, const std::vector<int> &,
const std::vector<int64_t> &) override;
void delete_clause (int64_t, bool, const vector<int> &) override;
void delete_clause (int64_t, bool, const std::vector<int> &) override;
void finalize_clause (int64_t, const vector<int> &) override {} // skip
void finalize_clause (int64_t, const std::vector<int> &) override {
} // skip
void report_status (int, int64_t) override {} // skip

View File

@ -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

View File

@ -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.

View File

@ -4,6 +4,7 @@
#include "global.h"
#include <cassert>
#include <cstdint>
#include <vector>
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 ();

View File

@ -3,18 +3,19 @@
#include "global.h"
#include <vector>
ABC_NAMESPACE_CXX_HEADER_START
namespace CaDiCaL {
struct Phases {
vector<signed char> best; // The current largest trail phase.
vector<signed char> forced; // Forced through 'phase'.
vector<signed char> min; // The current minimum unsatisfied phase.
vector<signed char> prev; // Previous during local search.
vector<signed char> saved; // The actual saved phase.
vector<signed char> target; // The current target phase.
std::vector<signed char> best; // The current largest trail phase.
std::vector<signed char> forced; // Forced through 'phase'.
std::vector<signed char> prev; // Previous during local search.
std::vector<signed char> saved; // The actual saved phase.
std::vector<signed char> target; // The current target phase.
};
} // namespace CaDiCaL

View File

@ -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)

View File

@ -3,6 +3,9 @@
#include "global.h"
#include "tracer.hpp"
#include <stdint.h>
ABC_NAMESPACE_CXX_HEADER_START
namespace CaDiCaL {
@ -23,19 +26,20 @@ class Proof {
Internal *internal;
vector<int> clause; // of external literals
vector<int64_t> proof_chain; // LRAT style proof chain of clause
int64_t clause_id; // id of added clause
std::vector<int> clause; // of external literals
std::vector<int64_t> proof_chain; // LRAT style proof chain of clause
int64_t clause_id; // id of added clause
bool redundant;
int witness;
// the 'tracers'
vector<Tracer *> tracers; // tracers (ie checker)
vector<FileTracer *> file_tracers; // file tracers (ie LRAT tracer)
std::vector<Tracer *> tracers; // tracers (ie checker)
std::vector<FileTracer *> 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<int> &); // ditto
void add_literals (const std::vector<int> &); // 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<int> &);
void add_original_clause (int64_t, bool, const std::vector<int> &);
void add_assumption_clause (int64_t, const vector<int> &,
const vector<int64_t> &);
void add_assumption_clause (int64_t, int, const vector<int64_t> &);
void add_assumption_clause (int64_t, const std::vector<int> &,
const std::vector<int64_t> &);
void add_assumption_clause (int64_t, int, const std::vector<int64_t> &);
void add_assumption (int);
void add_constraint (const vector<int> &);
void add_constraint (const std::vector<int> &);
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<int> &,
void add_external_original_clause (int64_t, bool,
const std::vector<int> &,
bool restore = false);
void delete_external_original_clause (int64_t, bool, const vector<int> &);
void delete_external_original_clause (int64_t, bool,
const std::vector<int> &);
// Add derived (such as learned) clauses to the proof.
//
void add_derived_empty_clause (int64_t, const vector<int64_t> &);
void add_derived_unit_clause (int64_t, int unit, const vector<int64_t> &);
void add_derived_clause (Clause *c, const vector<int64_t> &);
void add_derived_clause (int64_t, bool, const vector<int> &,
const vector<int64_t> &);
void add_derived_empty_clause (int64_t, const std::vector<int64_t> &);
void add_derived_unit_clause (int64_t, int unit,
const std::vector<int64_t> &);
void add_derived_clause (Clause *c, const std::vector<int64_t> &);
void add_derived_clause (int64_t, bool, const std::vector<int> &,
const std::vector<int64_t> &);
void add_derived_rat_clause (int64_t, bool, int, const std::vector<int> &,
const std::vector<int64_t> &);
void add_derived_rat_clause (Clause *c, int w,
const std::vector<int64_t> &);
// 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<int> &);
void weaken_minus (int64_t, const vector<int> &);
void weaken_plus (int64_t, const vector<int> &);
void delete_clause (int64_t, bool, const std::vector<int> &);
void weaken_minus (int64_t, const std::vector<int> &);
void weaken_plus (int64_t, const std::vector<int> &);
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<int> &c);
void finalize_clause (int64_t, const std::vector<int> &c);
void finalize_clause (Clause *);
void report_status (int, int64_t);
void begin_proof (int64_t);
void conclude_unsat (ConclusionType, const vector<int64_t> &);
void conclude_sat (const vector<int> &model);
void conclude_unknown (const vector<int> &trace);
void conclude_unsat (ConclusionType, const std::vector<int64_t> &);
void conclude_sat (const std::vector<int> &model);
void conclude_unknown (const std::vector<int> &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<int64_t> &);
void otfs_strengthen_clause (Clause *, const vector<int> &,
const vector<int64_t> &);
void strengthen_clause (Clause *, int, const std::vector<int64_t> &);
void otfs_strengthen_clause (Clause *, const std::vector<int> &,
const std::vector<int64_t> &);
void flush ();
};

View File

@ -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

View File

@ -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); }
};

View File

@ -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<uint64_t> bump_used = {0, 0};
std::vector<std::vector<uint64_t>> 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 *);

View File

@ -30,8 +30,8 @@ class Terminal {
}
void code (const char *str) {
if (!use_colors)
return;
// if (!use_colors)
// return;
if (!connected)
return;
escape ();

View File

@ -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<int> &,
virtual void add_derived_clause (int64_t, bool, int,
const std::vector<int> &,
const std::vector<int64_t> &) {}
// 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<int> &) {}
// 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) {}
};
/*--------------------------------------------------------------------------*/

View File

@ -120,6 +120,20 @@ template <class T> static void enlarge_zero (vector<T> &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 <class T> static void reserve_at_least (vector<T> &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 <typename T> struct DeferDeleteArray {

View File

@ -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<int64_t> delete_ids;
std::vector<int64_t> 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<int> &clause);
void veripb_add_derived_clause (int64_t, bool redundant,
const vector<int> &clause,
const vector<int64_t> &chain);
const std::vector<int> &clause,
const std::vector<int64_t> &chain);
void veripb_add_derived_clause (int64_t, bool redundant,
const vector<int> &clause);
const std::vector<int> &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<int> &,
void add_original_clause (int64_t, bool, const std::vector<int> &,
bool = false) override {} // skip
void add_derived_clause (int64_t, bool, const vector<int> &,
const vector<int64_t> &) override;
void add_derived_clause (int64_t, bool, int, const std::vector<int> &,
const std::vector<int64_t> &) override;
void delete_clause (int64_t, bool, const vector<int> &) override;
void finalize_clause (int64_t, const vector<int> &) override {} // skip
void delete_clause (int64_t, bool, const std::vector<int> &) override;
void finalize_clause (int64_t, const std::vector<int> &) override {
} // skip
void report_status (int, int64_t) override;
void weaken_minus (int64_t, const vector<int> &) override;
void weaken_minus (int64_t, const std::vector<int> &) override;
void strengthen (int64_t) override;
#ifndef CADICAL_QUIET

View File

@ -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<std::vector<vivify_ref>, 4> refs_schedules;
std::vector<vivify_ref> &refs_schedule_tier1, &refs_schedule_tier2,
&refs_schedule_tier3, &refs_schedule_irred;
std::vector<vivify_ref> refs_schedule;
std::array<std::vector<Clause *>, 4> schedules;
std::vector<Clause *> &schedule_tier1, &schedule_tier2, &schedule_tier3,
&schedule_irred;
@ -44,21 +42,11 @@ struct Vivifier {
int64_t ticks;
std::vector<std::tuple<int, Clause *, bool>> 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

97
src/sat/cadical/walk.hpp Normal file
View File

@ -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 <Clause*, TaggedBinary>;
//
// 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

View File

@ -6,6 +6,8 @@
#include <cassert>
#include <vector>
#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; }