ACEC/hCaD_V2/src/cadical.hpp
2022-10-21 19:34:18 +08:00

892 lines
34 KiB
C++

#ifndef _cadical_hpp_INCLUDED
#define _cadical_hpp_INCLUDED
#include <cstdio>
#include <cstdint>
#include <vector>
namespace CaDiCaL {
/*========================================================================*/
// This provides the actual API of the CaDiCaL solver, which is implemented
// in the class 'Solver' below. Beside its constructor and destructor most
// important is the IPASIR part which you can find between 'BEGIN IPASIR'
// and 'END IPASIR' comments below. The following '[Example]' below might
// also be a good starting point to understand the API.
/*========================================================================*/
// [Example]
//
// The internal solver state follows the IPASIR API model used in the
// incremental track of the SAT competition. State transitions are
// triggered by member function calls, declared and described below.
//
// Consider the following code (from 'test/api/example.cpp') of API usage:
//
// CaDiCaL::Solver * solver = new CaDiCaL::Solver;
//
// // ------------------------------------------------------------------
// // Encode Problem and check without assumptions.
//
// enum { TIE = 1, SHIRT = 2 };
//
// solver->add (-TIE), solver->add (SHIRT), solver->add (0);
// solver->add (TIE), solver->add (SHIRT), solver->add (0);
// solver->add (-TIE), solver->add (-SHIRT), solver->add (0);
//
// int res = solver->solve (); // Solve instance.
// assert (res == 10); // Check it is 'SATISFIABLE'.
//
// res = solver->val (TIE); // Obtain assignment of 'TIE'.
// assert (res < 0); // Check 'TIE' assigned to 'false'.
//
// res = solver->val (SHIRT); // Obtain assignment of 'SHIRT'.
// assert (res > 0); // Check 'SHIRT' assigned to 'true'.
//
// // ------------------------------------------------------------------
// // Incrementally solve again under one assumption.
//
// solver->assume (TIE); // Now force 'TIE' to true.
//
// res = solver->solve (); // Solve again incrementally.
// assert (res == 20); // Check it is 'UNSATISFIABLE'.
//
// res = solver->failed (TIE); // Check 'TIE' responsible.
// assert (res); // Yes, 'TIE' in core.
//
// res = solver->failed (SHIRT); // Check 'SHIRT' responsible.
// assert (!res); // No, 'SHIRT' not in core.
//
// // ------------------------------------------------------------------
// // Incrementally solve once more under another assumption.
//
// solver->assume (-SHIRT); // Now force 'SHIRT' to false.
//
// res = solver->solve (); // Solve again incrementally.
// assert (res == 20); // Check it is 'UNSATISFIABLE'.
//
// res = solver->failed (TIE); // Check 'TIE' responsible.
// assert (!res); // No, 'TIE' not in core.
//
// res = solver->failed (-SHIRT); // Check '!SHIRT' responsible.
// assert (res); // Yes, '!SHIRT' in core.
//
// // ------------------------------------------------------------------
//
// delete solver;
/*========================================================================*/
// [States and Transitions]
//
// Compared to IPASIR we also use an 'ADDING' state in which the solver
// stays while adding non-zero literals until the clause is completed
// through adding a zero literal. The additional 'INITIALIZING',
// 'CONFIGURING' and 'DELETING' states are also not part of IPASIR but also
// useful for testing and debugging.
//
// We have the following transitions which are all synchronous except for
// the reentrant 'terminate' call:
//
// new
// INITIALIZING --------------------------> CONFIGURING
//
// set / trace
// CONFIGURING --------------------------> CONFIGURING
//
// add (non zero literal)
// VALID --------------------------> ADDING
//
// add (zero literal)
// VALID --------------------------> UNKNOWN
//
// assume (non zero literal)
// READY --------------------------> UNKNOWN
//
// solve
// READY --------------------------> SOLVING
//
// (internal)
// SOLVING --------------------------> READY
//
// val (non zero literal)
// SATISFIED --------------------------> SATISFIED
//
// failed (non zero literal )
// UNSATISFIED --------------------------> UNSATISFIED
//
// delete
// VALID --------------------------> DELETING
//
// where
//
// READY = CONFIGURING | UNKNOWN | SATISFIED | UNSATISFIED
// VALID = READY | ADDING
// INVALID = INITIALIZING | DELETING
//
// The 'SOLVING' state is only visible in different contexts, i.e., from
// another thread or from a signal handler. It is used to implement
// 'terminate'. Here is the only asynchronous transition:
//
// terminate (asynchronously)
// SOLVING -------------------------> UNKNOWN
//
// The important behaviour to remember is that adding or assuming a literal
// (immediately) destroys the satisfying assignment in the 'SATISFIED' state
// and vice versa resets all assumptions in the 'UNSATISFIED' state. This
// is exactly the behaviour required by the IPASIR interface.
//
// Furthermore, the model can only be queried through 'val' in the
// 'SATISFIED' state, while extracting failed assumptions with 'val' only in
// the 'UNSATISFIED' state. Solving can only be started in the 'UNKNOWN' or
// 'CONFIGURING' state or after the previous call to 'solve' yielded an
// 'UNKNOWN, 'SATISFIED' or 'UNSATISFIED' state.
//
// All literals have to be valid literals too, i.e., 32-bit integers
// different from 'INT_MIN'. If any of these requirements is violated the
// solver aborts with an 'API contract violation' message.
//
// HINT: If you do not understand why a contract is violated you can run
// 'mobical' on the failing API call trace. Point the environment variable
// 'CADICAL_API_TRACE' to the file where you want to save the trace during
// execution of your program linking against the library. You probably need
// for 'mobical' to use the option '--do-not-enforce-contracts' though to
// force running into the same contract violation.
//
// Additional API calls (like 'freeze' and 'melt') do not change the state
// of the solver and are all described below.
/*========================================================================*/
// States are represented by a bit-set in order to combine them.
enum State
{
INITIALIZING = 1, // during initialization (invalid)
CONFIGURING = 2, // configure options (with 'set')
UNKNOWN = 4, // ready to call 'solve'
ADDING = 8, // adding clause literals (zero missing)
SOLVING = 16, // while solving (within 'solve')
SATISFIED = 32, // satisfiable allows 'val'
UNSATISFIED = 64, // unsatisfiable allows 'failed'
DELETING = 128, // during and after deletion (invalid)
// These combined states are used to check contracts.
READY = CONFIGURING | UNKNOWN | SATISFIED | UNSATISFIED,
VALID = READY | ADDING,
INVALID = INITIALIZING | DELETING
};
/*------------------------------------------------------------------------*/
// Opaque classes needed in the API and declared in the same namespace.
class File;
struct Internal;
struct External;
/*------------------------------------------------------------------------*/
// Forward declaration of call-back classes. See bottom of this file.
class Learner;
class Terminator;
class ClauseIterator;
class WitnessIterator;
/*------------------------------------------------------------------------*/
class Solver {
public:
// ====== BEGIN IPASIR ===================================================
// This section implements the corresponding IPASIR functionality.
Solver ();
~Solver ();
static const char * signature (); // name of this library
// Core functionality as in the IPASIR incremental SAT solver interface.
// (recall 'READY = CONFIGURING | UNKNOWN | SATISFIED | UNSATISFIED').
// Further note that 'lit' is required to be different from 'INT_MIN' and
// different from '0' except for 'add'.
// Add valid literal to clause or zero to terminate clause.
//
// require (VALID) // recall 'VALID = READY | ADDING'
// if (lit) ensure (ADDING) // and thus VALID but not READY
// if (!lit) ensure (UNKNOWN) // and thus READY
//
void add (int lit);
// Assume valid non zero literal for next call to 'solve'. These
// assumptions are reset after the call to 'solve' as well as after
// returning from 'simplify' and 'lookahead.
//
// require (READY)
// ensure (UNKNOWN)
//
void assume (int lit);
// Try to solve the current formula. Returns
//
// 0 = UNSOLVED (limit reached or interrupted through 'terminate')
// 10 = SATISFIABLE
// 20 = UNSATISFIABLE
//
// require (READY)
// ensure (UNKNOWN | SATISFIED | UNSATISFIED)
//
// Note, that while in this call the solver actually transitions to state
// 'SOLVING', which however is only visible from a different context,
// i.e., from a different thread or from a signal handler. Only right
// before returning from this call it goes into a 'READY' state.
//
int solve ();
// Get value (-lit=false, lit=true) of valid non-zero literal.
//
// require (SATISFIED)
// ensure (SATISFIED)
//
int val (int lit);
// Determine whether the valid non-zero literal is in the core.
// Returns 'true' if the literal is in the core and 'false' otherwise.
// Note that the core does not have to be minimal.
//
// require (UNSATISFIED)
// ensure (UNSATISFIED)
//
bool failed (int lit);
// Add call-back which is checked regularly for termination. There can
// only be one terminator connected. If a second (non-zero) one is added
// the first one is implicitly disconnected.
//
// require (VALID)
// ensure (VALID)
//
void connect_terminator (Terminator * terminator);
void disconnect_terminator ();
// Add call-back which allows to export learned clauses.
//
// require (VALID)
// ensure (VALID)
//
void connect_learner (Learner * learner);
void disconnect_learner ();
// ====== END IPASIR =====================================================
//------------------------------------------------------------------------
// This function determines a good splitting literal. The result can be
// zero if the formula is proven to be satisfiable or unsatisfiable. This
// can then be checked by 'state ()'. If the formula is empty and
// the function is not able to determine satisfiability also zero is
// returned but the state remains unknown.
//
// require (READY)
// ensure (UNKNOWN|SATISFIED|UNSATISFIED)
//
int lookahead(void);
struct CubesWithStatus {
int status;
std::vector<std::vector<int>> cubes;
};
CubesWithStatus generate_cubes(int, int min_depth = 0);
void reset_assumptions();
// Return the current state of the solver as defined above.
//
const State & state () const { return _state; }
// Similar to 'state ()' but using the staddard competition exit codes of
// '10' for 'SATISFIABLE', '20' for 'UNSATISFIABLE' and '0' otherwise.
//
int status () const {
if (_state == SATISFIED) return 10;
else if (_state == UNSATISFIED) return 20;
else return 0;
}
/*----------------------------------------------------------------------*/
static const char * version (); // return version string
/*----------------------------------------------------------------------*/
// Copy 'this' into a fresh 'other'. The copy procedure is not a deep
// clone, but only copies irredundant clauses and units. It also makes
// sure that witness reconstruction works with the copy as with the
// original formula such that both solvers have the same models.
// Assumptions are not copied. Options however are copied as well as
// flags which remember the current state of variables in preprocessing.
//
// require (READY) // for 'this'
// ensure (READY) // for 'this'
//
// other.require (CONFIGURING)
// other.ensure (CONFIGURING | UNKNOWN)
//
void copy (Solver & other) const;
/*----------------------------------------------------------------------*/
// Variables are usually added and initialized implicitly whenever a
// 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.
//
// require (VALID | SOLVING)
// ensure (VALID | SOLVING)
//
int vars ();
// Increase the maximum variable index explicitly. This function makes
// sure that at least 'min_max_var' variables are initialized. Since it
// might need to reallocate tables, it destroys a satisfying assignment
// and has the same state transition and conditions as 'assume' etc.
//
// require (READY)
// ensure (UNKNOWN)
//
void reserve (int min_max_var);
#ifndef NTRACING
//------------------------------------------------------------------------
// This function can be used to write API calls to a file. The same
// format is used which 'mobical' can read, execute and also shrink
// through delta debugging.
//
// Tracing API calls can also be achieved by using the environment
// variable 'CADICAL_API_TRACE'. That alternative is useful if you do not
// want to change the source code using the solver, e.g., if you only have
// a binary with the solver linked in. However, that method only allows
// to trace one solver instance, while with the following function API
// tracing can be enabled for different solver instances individually.
//
// The solver will flush the file after every trace API call but does not
// close it during deletion. It remains owned by the user of the library.
//
// require (VALID)
// ensure (VALID)
//
void trace_api_calls (FILE * file);
#endif
//------------------------------------------------------------------------
// Option handling.
// Determine whether 'name' is a valid option name.
//
static bool is_valid_option (const char * name);
// Determine whether 'name' enables a specific preprocessing technique.
//
static bool is_preprocessing_option (const char * name);
// Determine whether 'arg' is a valid long option of the form '--<name>',
// '--<name>=<val>' or '--no-<name>' similar to 'set_long_option' below.
// Legal values are 'true', 'false', or '[-]<mantissa>[e<exponent>]'.
static bool is_valid_long_option (const char * arg);
// Get the current value of the option 'name'. If 'name' is invalid then
// zero is returned. Here '--...' arguments as invalid options.
//
int get (const char * name);
// Set the default verbose message prefix (default "c ").
//
void prefix (const char * verbose_message_prefix);
// Explicit version of setting an option. If the option '<name>' exists
// and '<val>' can be parsed then 'true' is returned. If the option value
// is out of range the actual value is computed as the closest (minimum or
// maximum) value possible, but still 'true' is returned.
//
// require (CONFIGURING)
// ensure (CONFIGURING)
//
// Thus options can only bet set right after initialization.
//
bool set (const char * name, int val);
// This function accepts options in command line syntax:
//
// '--<name>=<val>', '--<name>', or '--no-<name>'
//
// It actually calls the previous 'set' function after parsing 'arg'. The
// same values are expected as for 'is_valid_long_option' above and as
// with 'set' any value outside of the range of legal values for a
// particular option are set to either the minimum or maximum depending on
// which side of the valid interval they lie.
//
// require (CONFIGURING)
// ensure (CONFIGURING)
//
bool set_long_option (const char * arg);
// Determine whether 'name' is a valid configuration.
//
static bool is_valid_configuration (const char *);
// Overwrite (some) options with the forced values of the configuration.
// The result is 'true' iff the 'name' is a valid configuration.
//
// require (CONFIGURING)
// ensure (CONFIGURING)
//
bool configure (const char *);
// Increase preprocessing and inprocessing limits by '10^<val>'. Values
// below '0' are ignored and values above '9' are reduced to '9'.
//
// require (READY)
// ensure (READY)
//
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.
//
// require (READY)
// ensure (READY)
//
bool limit (const char * arg, int val);
bool is_valid_limit (const char * arg);
// The number of currently active variables and clauses can be queried by
// these functions. Variables become active if a clause is added with it.
// They become inactive if they are eliminated or fixed at the root level
// Clauses become inactive if they are satisfied, subsumed, eliminated.
// Redundant clauses are reduced regularly and thus the 'redundant'
// function is less useful.
//
// require (VALID)
// ensure (VALID)
//
int active () const; // Number of active variables.
int64_t redundant () const; // Number of active redundant clauses.
int64_t irredundant () const; // Number of active irredundant clauses.
//------------------------------------------------------------------------
// This function executes the given number of preprocessing rounds. It is
// 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
// propagation is performed, which both take some time.
//
// require (READY)
// ensure (UNKNOWN | SATISFIED | UNSATISFIED)
//
int simplify (int rounds = 3);
//------------------------------------------------------------------------
// Force termination of 'solve' asynchronously.
//
// require (SOLVING | READY)
// ensure (UNKNOWN) // actually not immediately (synchronously)
//
void terminate ();
//------------------------------------------------------------------------
// We have the following common reference counting functions, which avoid
// to restore clauses but require substantial user guidance. This was the
// only way to use inprocessing in incremental SAT solving in Lingeling
// (and before in MiniSAT's 'freeze' / 'thaw') and which did not use
// automatic clause restoring. In general this is slower than
// restoring clauses and should not be used.
//
// In essence the user freezes variables which potentially are still
// needed in clauses added or assumptions used after the next 'solve'
// call. As in Lingeling you can freeze a variable multiple times, but
// then have to melt it the same number of times again in order to enable
// variable eliminating on it etc. The arguments can be literals
// (negative indices) but conceptually variables are frozen.
//
// In the old way of doing things without restore you should not use a
// variable incrementally (in 'add' or 'assume'), which was used before
// and potentially could have been eliminated in a previous 'solve' call.
// This can lead to spurious satisfying assignment. In order to check
// this API contract one can use the 'checkfrozen' option. This has the
// drawback that restoring clauses implicitly would fail with a fatal
// error message even if in principle the solver could just restore
// clauses. Thus this option is disabled by default.
//
// See our SAT'19 paper [FazekasBiereScholl-SAT'19] for more details.
//
// require (VALID)
// ensure (VALID)
//
bool frozen (int lit) const;
void freeze (int lit);
void melt (int lit); // Also needs 'require (frozen (lit))'.
//------------------------------------------------------------------------
// Root level assigned variables can be queried with this function.
// It returns '1' if the literal is implied by the formula, '-1' if its
// negation is implied, or '0' if this is unclear at this point.
//
// require (VALID)
// ensure (VALID)
//
int fixed (int lit) const;
//------------------------------------------------------------------------
// Force the default decision phase of a variable to a certain value.
//
void phase (int lit);
void unphase (int lit);
//------------------------------------------------------------------------
// Enables clausal proof tracing in DRAT format and returns 'true' if
// successfully opened for writing. Writing proofs has to be enabled
// before calling 'solve', 'add' and 'dimacs', that is in state
// 'CONFIGURING'. Otherwise only partial proofs would be written.
//
// require (CONFIGURING)
// ensure (CONFIGURING)
//
bool trace_proof (FILE * file, const char * name); // Write DRAT proof.
bool trace_proof (const char * path); // Open & write proof.
// Flush proof trace file.
//
// require (VALID)
// ensure (VALID)
//
void flush_proof_trace ();
// Close proof trace early.
//
// require (VALID)
// ensure (VALID)
//
void close_proof_trace ();
//------------------------------------------------------------------------
static void usage (); // print usage information for long options
static void configurations (); // print configuration usage options
// require (!DELETING)
// ensure (!DELETING)
//
void statistics (); // print statistics
void resources (); // print resource usage (time and memory)
// require (VALID)
// ensure (VALID)
//
void options (); // print current option and value list
//------------------------------------------------------------------------
// Traverse irredundant clauses or the extension stack in reverse order.
//
// The return value is false if traversal is aborted early due to one of
// the visitor functions returning false. See description of the
// iterators below for more details on how to use these functions.
//
// require (VALID)
// ensure (VALID)
//
bool traverse_clauses (ClauseIterator &) const;
bool traverse_witnesses_backward (WitnessIterator &) const;
bool traverse_witnesses_forward (WitnessIterator &) const;
//------------------------------------------------------------------------
// Files with explicit path argument support compressed input and output
// if appropriate helper functions 'gzip' etc. are available. They are
// called through opening a pipe to an external command.
//
// If the 'strict' argument is zero then the number of variables and
// clauses specified in the DIMACS headers are ignored, i.e., the header
// 'p cnf 0 0' is always legal. If the 'strict' argument is larger '1'
// strict formatting of the header is required, i.e., single spaces
// everywhere and no trailing white space.
//
// Returns zero if successful and otherwise an error message.
//
// require (VALID)
// ensure (VALID)
//
const char * read_dimacs (FILE * file,
const char * name, int & vars, int strict = 1);
const char * read_dimacs (const char * path, int & vars, int strict = 1);
// The following routines work the same way but parse both DIMACS and
// INCCNF files (with 'p inccnf' header and 'a <cube>' lines). If the
// parser finds and 'p inccnf' header or cubes then '*incremental' is set
// to true and the cubes are stored in the given vector (each cube
// terminated by a zero).
const char * read_dimacs (FILE * file,
const char * name, int & vars, int strict,
bool & incremental, std::vector<int> & cubes);
const char * read_dimacs (const char * path, int & vars, int strict,
bool & incremental, std::vector<int> & cubes);
//------------------------------------------------------------------------
// Write current irredundant clauses and all derived unit clauses
// to a file in DIMACS format. Clauses on the extension stack are
// not included, nor any redundant clauses.
//
// The 'min_max_var' parameter gives a lower bound on the number '<vars>'
// of variables used in the DIMACS 'p cnf <vars> ...' header.
//
// Returns zero if successful and otherwise an error message.
//
// require (VALID)
// ensure (VALID)
//
const char * write_dimacs (const char * path, int min_max_var = 0);
// The extension stack for reconstruction a solution can be written too.
//
const char * write_extension (const char * path);
// Print build configuration to a file with prefix 'c '. If the file
// is '<stdout>' or '<stderr>' then terminal color codes might be used.
//
static void build (FILE * file, const char * prefix = "c ");
private:
//==== start of state ====================================================
State _state; // API states as discussed above.
/*----------------------------------------------------------------------*/
// The 'Solver' class is a 'facade' object for 'External'. It exposes the
// 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
// installed in a system, then only this header file has to be installed
// too, and still allows to compile and link against the library.
/*----------------------------------------------------------------------*/
// More precisely the CaDiCaL code is split into three layers:
//
// Solver: facade object providing the actual API of the solver
// External: communication layer between 'Solver' and 'Internal'
// Internal: the actual solver code
//
// The 'External' and 'Internal' layers are declared and implemented in
// the corresponding '{external,internal}.{hpp,cpp}' files (as expected),
// while the 'Solver' facade class is defined in 'cadical.hpp' (here) but
// implemented in 'solver.cpp'. The reason for this naming mismatch is,
// that we want to use 'cadical.hpp' for the library header (this header
// file) and call the binary of the stand alone SAT also 'cadical', which
// is more naturally implemented in 'cadical.cpp'.
//
// Separating 'External' from 'Internal' also allows us to map external
// literals to internal literals, which is useful with many fixed or
// eliminated variables (during 'compact' the internal variable range is
// reduced and external variables are remapped). Such an approach is also
// necessary, if we want to use extended resolution in the future (such as
// bounded variable addition).
//
Internal * internal; // Hidden internal solver.
External * external; // Hidden API to internal solver mapping.
#ifndef NTRACING
// The API calls to the solver can be traced by setting the environment
// variable 'CADICAL_API_TRACE' to point to the path of a file to which
// API calls are written. The same format is used which 'mobical' can
// read, execute and also shrink through delta debugging.
//
// 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
//
bool close_trace_api_file; // Close file if owned by solver it.
FILE * trace_api_file; // Also acts as flag that we are tracing.
static bool tracing_api_through_environment;
//===== end of state ====================================================
void trace_api_call (const char *) const;
void trace_api_call (const char *, int) const;
void trace_api_call (const char *, const char *, int) const;
#endif
void transition_to_unknown_state ();
//------------------------------------------------------------------------
// Used in the stand alone solver application 'App' and the model based
// tester 'Mobical'. So only these two classes need direct access to the
// otherwise more application specific functions listed here together with
// the internal DIMACS parser.
friend class App;
friend class Mobical;
friend class Parser;
// Read solution in competition format for debugging and testing.
//
// require (VALID)
// ensure (VALID)
//
const char * read_solution (const char * path);
// This gives warning messages for wrong 'printf' style format string usage.
// Apparently (on 'gcc 9' at least) the first argument is 'this' here.
// TODO: support for other compilers (beside 'gcc' and 'clang').
//
# define CADICAL_ATTRIBUTE_FORMAT(FORMAT_POSITION,VARIADIC_ARGUMENT_POSITION) \
__attribute__ ((format (printf, FORMAT_POSITION, VARIADIC_ARGUMENT_POSITION)))
// Messages in a common style.
//
// require (VALID | DELETING)
// ensure (VALID | DELETING)
//
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
CADICAL_ATTRIBUTE_FORMAT (2, 3);
// Explicit verbose level ('section' and 'message' use '0').
//
// require (VALID | DELETING)
// ensure (VALID | DELETING)
//
void verbose (int level, const char *, ...)
CADICAL_ATTRIBUTE_FORMAT (3, 4);
// Factoring out common code to both 'read_dimacs' functions above.
//
// require (VALID)
// ensure (VALID)
//
const char * read_dimacs (File *, int &, int strict,
bool * incremental = 0,
std::vector<int> * = 0);
// Factored out common code for 'solve', 'simplify' and 'lookahead'.
//
int call_external_solve_and_check_results (bool preprocess_only);
//------------------------------------------------------------------------
// Print DIMACS file to '<stdout>' for debugging and testing purposes,
// including derived units and assumptions. Since it will print in terms
// of internal literals it is otherwise not really useful. To write a
// DIMACS formula in terms of external variables use 'write_dimacs'.
//
// require (!INITIALIZING)
// ensure (!INITIALIZING)
//
void dump_cnf ();
friend struct DumpCall; // Mobical calls 'dump_cnf' in 'DumpCall::execute'
};
/*========================================================================*/
// Connected terminators are checked for termination regularly. If the
// 'terminate' function of the terminator returns true the solver is
// terminated synchronously as soon it calls this function.
class Terminator {
public:
virtual ~Terminator () { }
virtual bool terminate () = 0;
};
// 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.
class Learner {
public:
virtual ~Learner () { }
virtual bool learning (int size) = 0;
virtual void learn (int lit) = 0;
};
/*------------------------------------------------------------------------*/
// Allows to traverse all remaining irredundant clauses. Satisfied and
// eliminated clauses are not included, nor any derived units unless such
// 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.
class ClauseIterator {
public:
virtual ~ClauseIterator () { }
virtual bool clause (const std::vector<int> &) = 0;
};
/*------------------------------------------------------------------------*/
// Allows to traverse all clauses on the extension stack together with their
// witness cubes. If the solver is inconsistent, i.e., an empty clause is
// found and the formula is unsatisfiable, then nothing is traversed.
//
// The clauses traversed in 'traverse_clauses' together with the clauses on
// the extension stack are logically equivalent to the original clauses.
// See our SAT'19 paper for more details.
//
// The witness literals can be used to extend and fix an assignment on the
// remaining clauses to satisfy the clauses on the extension stack too.
//
// All derived units of non-frozen variables are included too.
//
// If 'witness' returns false traversal aborts early.
class WitnessIterator {
public:
virtual ~WitnessIterator () { }
virtual bool witness (const std::vector<int> & clause,
const std::vector<int> & witness) = 0;
};
/*------------------------------------------------------------------------*/
}
#endif