mirror of https://github.com/YosysHQ/abc.git
update cadical to 2.2.0
This commit is contained in:
parent
64637b8395
commit
a625ef2edc
12
abclib.dsp
12
abclib.dsp
|
|
@ -2911,6 +2911,10 @@ SOURCE=.\src\sat\cadical\cadical_averages.cpp
|
|||
# End Source File
|
||||
# Begin Source File
|
||||
|
||||
SOURCE=.\src\sat\cadical\cadical_backbone.cpp
|
||||
# End Source File
|
||||
# Begin Source File
|
||||
|
||||
SOURCE=.\src\sat\cadical\cadical_backtrack.cpp
|
||||
# End Source File
|
||||
# Begin Source File
|
||||
|
|
@ -3243,6 +3247,14 @@ SOURCE=.\src\sat\cadical\cadical_walk.cpp
|
|||
# End Source File
|
||||
# Begin Source File
|
||||
|
||||
SOURCE=.\src\sat\cadical\cadical_walk_full_occs.cpp
|
||||
# End Source File
|
||||
# Begin Source File
|
||||
|
||||
SOURCE=.\src\sat\cadical\cadical_warmup.cpp
|
||||
# End Source File
|
||||
# Begin Source File
|
||||
|
||||
SOURCE=.\src\sat\cadical\cadical_watch.cpp
|
||||
# End Source File
|
||||
# Begin Source File
|
||||
|
|
|
|||
|
|
@ -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:
|
||||
|
|
|
|||
|
|
@ -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*************************************************************
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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) {
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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 ();
|
||||
|
|
|
|||
|
|
@ -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 ())
|
||||
|
|
|
|||
|
|
@ -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 ();
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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++;
|
||||
|
||||
|
|
|
|||
|
|
@ -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));
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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 ())
|
||||
|
|
|
|||
|
|
@ -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 ())
|
||||
|
|
|
|||
|
|
@ -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 ();
|
||||
|
|
|
|||
|
|
@ -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)) {
|
||||
|
|
|
|||
|
|
@ -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 ())
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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 ());
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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 ())
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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) {
|
||||
|
|
|
|||
|
|
@ -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 () {
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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++) {
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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 () {
|
||||
|
|
|
|||
|
|
@ -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",
|
||||
|
|
|
|||
|
|
@ -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) {}
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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 =
|
||||
|
|
|
|||
|
|
@ -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> ¤t_refs_schedule (Vivifier &vivifier) {
|
||||
switch (vivifier.tier) {
|
||||
case Vivify_Mode::TIER1:
|
||||
return vivifier.refs_schedule_tier1;
|
||||
break;
|
||||
case Vivify_Mode::TIER2:
|
||||
return vivifier.refs_schedule_tier2;
|
||||
break;
|
||||
case Vivify_Mode::TIER3:
|
||||
return vivifier.refs_schedule_tier3;
|
||||
break;
|
||||
default:
|
||||
return vivifier.refs_schedule_irred;
|
||||
break;
|
||||
}
|
||||
#if defined(WIN32) && !defined(__MINGW32__)
|
||||
__assume(false);
|
||||
#else
|
||||
__builtin_unreachable ();
|
||||
#endif
|
||||
return vivifier.refs_schedule;
|
||||
}
|
||||
|
||||
inline std::vector<Clause *> ¤t_schedule (Vivifier &vivifier) {
|
||||
|
|
@ -1580,19 +1518,16 @@ void Internal::vivify_round (Vivifier &vivifier, int64_t ticks_limit) {
|
|||
if (terminated_asynchronously ())
|
||||
return;
|
||||
|
||||
PHASE ("vivify", stats.vivifications,
|
||||
"starting %c vivification round ticks limit %" PRId64 "",
|
||||
vivifier.tag, ticks_limit);
|
||||
|
||||
PHASE ("vivify", stats.vivifications,
|
||||
"starting %c vivification round ticks limit %" PRId64 "",
|
||||
vivifier.tag, ticks_limit);
|
||||
|
||||
CADICAL_assert (watching ());
|
||||
|
||||
auto &refs_schedule = current_refs_schedule (vivifier);
|
||||
auto &schedule = current_schedule (vivifier);
|
||||
|
||||
PHASE ("vivify", stats.vivifications,
|
||||
"starting %c vivification round ticks limit %" PRId64
|
||||
" with %zu clauses",
|
||||
vivifier.tag, ticks_limit, schedule.size ());
|
||||
|
||||
CADICAL_assert (watching ());
|
||||
|
||||
int64_t ticks = 1 + schedule.size ();
|
||||
|
||||
// Sort candidates, with first to be tried candidate clause last, i.e.,
|
||||
|
|
@ -1609,7 +1544,7 @@ void Internal::vivify_round (Vivifier &vivifier, int64_t ticks_limit) {
|
|||
//
|
||||
|
||||
// first build the schedule with vivifier_refs
|
||||
auto end_schedule = end (schedule);
|
||||
const auto end_schedule = end (schedule);
|
||||
refs_schedule.resize (schedule.size ());
|
||||
std::transform (begin (schedule), end_schedule, begin (refs_schedule),
|
||||
[&] (Clause *c) { return create_ref (this, c); });
|
||||
|
|
@ -1629,8 +1564,7 @@ void Internal::vivify_round (Vivifier &vivifier, int64_t ticks_limit) {
|
|||
std::transform (begin (refs_schedule), end (refs_schedule),
|
||||
begin (schedule),
|
||||
[] (vivify_ref c) { return c.clause; });
|
||||
erase_vector (refs_schedule);
|
||||
LOG ("clause after sorting final:");
|
||||
refs_schedule.clear ();
|
||||
} else {
|
||||
// skip sorting but still put clauses with the vivify tag at the end to
|
||||
// be done first Kissat does this implicitely by going twice over all
|
||||
|
|
@ -1697,10 +1631,10 @@ void Internal::vivify_round (Vivifier &vivifier, int64_t ticks_limit) {
|
|||
for (auto c : schedule)
|
||||
c->vivify = true;
|
||||
#elif 1
|
||||
// if we have gone through all the leftovers, the current clauses are
|
||||
// leftovers for the next round
|
||||
if (!schedule.empty () && !schedule.front ()->vivify &&
|
||||
schedule.back ()->vivify)
|
||||
// if we have gone through all the leftovers (the next candidate
|
||||
// is not one), all the current clauses are leftovers for the next
|
||||
// round
|
||||
if (!schedule.empty () && !schedule.back ()->vivify)
|
||||
for (auto c : schedule)
|
||||
c->vivify = true;
|
||||
#else
|
||||
|
|
@ -1852,8 +1786,15 @@ bool Internal::vivify () {
|
|||
}
|
||||
int64_t init_ticks = 0;
|
||||
|
||||
// Refill the schedule every time. Unchecked clauses are 'saved' by
|
||||
// Refill the schedule every time. Unchecked clauses are 'saved' by
|
||||
// setting their 'vivify' bit, such that they can be tried next time.
|
||||
// There are two things to denote: the option 'vivifyonce' does what it is
|
||||
// supposed to do, and it works because the ticks are kept for the next
|
||||
// schedule. Also, we limit the size of the schedule to limit the cost of
|
||||
// sorting.
|
||||
//
|
||||
// TODO: After limiting, the cost we fixed some heuristics bug, so maybe
|
||||
// we could increase the limit.
|
||||
//
|
||||
// TODO: count against ticks.vivify directly instead of this unholy
|
||||
// shifting.
|
||||
|
|
@ -1880,9 +1821,9 @@ bool Internal::vivify () {
|
|||
}
|
||||
|
||||
if (!unsat && tier2effort) {
|
||||
erase_vector (
|
||||
vivifier.schedule_tier1); // save memory (well, not really as we
|
||||
// already reached the peak memory)
|
||||
// save memory (well, not really as we
|
||||
// already reached the peak memory)
|
||||
erase_vector (vivifier.schedule_tier1);
|
||||
if (limit < stats.ticks.vivify)
|
||||
limit = stats.ticks.vivify;
|
||||
const double effort = (total * tier2effort) / sumeffort;
|
||||
|
|
@ -1953,6 +1894,7 @@ bool Internal::vivify () {
|
|||
STOP_SIMPLIFIER (vivify, VIVIFY);
|
||||
|
||||
private_steps = false;
|
||||
CADICAL_assert (!ignore);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
@ -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 *);
|
||||
|
|
|
|||
|
|
@ -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
|
||||
};
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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() \
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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
|
||||
}
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
|
|
|
|||
|
|
@ -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 ();
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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 ();
|
||||
};
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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); }
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -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 *);
|
||||
|
|
|
|||
|
|
@ -30,8 +30,8 @@ class Terminal {
|
|||
}
|
||||
|
||||
void code (const char *str) {
|
||||
if (!use_colors)
|
||||
return;
|
||||
// if (!use_colors)
|
||||
// return;
|
||||
if (!connected)
|
||||
return;
|
||||
escape ();
|
||||
|
|
|
|||
|
|
@ -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) {}
|
||||
};
|
||||
|
||||
/*--------------------------------------------------------------------------*/
|
||||
|
|
|
|||
|
|
@ -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 {
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
@ -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; }
|
||||
|
|
|
|||
Loading…
Reference in New Issue