Revert addition of CaDiCaL

This reverts the upstream PR berkeley-abc/abc#382
This commit is contained in:
Martin Povišer 2025-03-11 20:15:41 +01:00
parent 43b9a4defe
commit 5ecc7c333c
164 changed files with 1 additions and 59697 deletions

View File

@ -28,7 +28,7 @@ MODULES := \
src/opt/cut src/opt/fxu src/opt/fxch src/opt/rwr src/opt/mfs src/opt/sim \
src/opt/ret src/opt/fret src/opt/res src/opt/lpk src/opt/nwk src/opt/rwt src/opt/rar \
src/opt/cgt src/opt/csw src/opt/dar src/opt/dau src/opt/dsc src/opt/sfm src/opt/sbd \
src/sat/bsat src/sat/xsat src/sat/satoko src/sat/csat src/sat/msat src/sat/psat src/sat/cnf src/sat/bmc src/sat/glucose src/sat/glucose2 src/sat/kissat src/sat/cadical \
src/sat/bsat src/sat/xsat src/sat/satoko src/sat/csat src/sat/msat src/sat/psat src/sat/cnf src/sat/bmc src/sat/glucose src/sat/glucose2 src/sat/kissat \
src/bool/bdc src/bool/deco src/bool/dec src/bool/kit src/bool/lucky \
src/bool/rsb src/bool/rpo \
src/proof/pdr src/proof/abs src/proof/live src/proof/ssc src/proof/int \

View File

@ -2886,374 +2886,6 @@ SOURCE=.\src\sat\kissat\watch.c
SOURCE=.\src\sat\kissat\weaken.c
# End Source File
# End Group
# Begin Group "cadical"
# PROP Default_Filter ""
# Begin Source File
SOURCE=.\src\sat\cadical\cadical_kitten.c
# End Source File
# Begin Source File
SOURCE=.\src\sat\cadical\cadical_analyze.cpp
# End Source File
# Begin Source File
SOURCE=.\src\sat\cadical\cadical_arena.cpp
# End Source File
# Begin Source File
SOURCE=.\src\sat\cadical\cadical_assume.cpp
# End Source File
# Begin Source File
SOURCE=.\src\sat\cadical\cadical_averages.cpp
# End Source File
# Begin Source File
SOURCE=.\src\sat\cadical\cadical_backtrack.cpp
# End Source File
# Begin Source File
SOURCE=.\src\sat\cadical\cadical_backward.cpp
# End Source File
# Begin Source File
SOURCE=.\src\sat\cadical\cadical_bins.cpp
# End Source File
# Begin Source File
SOURCE=.\src\sat\cadical\cadical_block.cpp
# End Source File
# Begin Source File
SOURCE=.\src\sat\cadical\cadical_ccadical.cpp
# End Source File
# Begin Source File
SOURCE=.\src\sat\cadical\cadical_checker.cpp
# End Source File
# Begin Source File
SOURCE=.\src\sat\cadical\cadical_clause.cpp
# End Source File
# Begin Source File
SOURCE=.\src\sat\cadical\cadical_collect.cpp
# End Source File
# Begin Source File
SOURCE=.\src\sat\cadical\cadical_compact.cpp
# End Source File
# Begin Source File
SOURCE=.\src\sat\cadical\cadical_condition.cpp
# End Source File
# Begin Source File
SOURCE=.\src\sat\cadical\cadical_config.cpp
# End Source File
# Begin Source File
SOURCE=.\src\sat\cadical\cadical_congruence.cpp
# End Source File
# Begin Source File
SOURCE=.\src\sat\cadical\cadical_constrain.cpp
# End Source File
# Begin Source File
SOURCE=.\src\sat\cadical\cadical_contract.cpp
# End Source File
# Begin Source File
SOURCE=.\src\sat\cadical\cadical_cover.cpp
# End Source File
# Begin Source File
SOURCE=.\src\sat\cadical\cadical_decide.cpp
# End Source File
# Begin Source File
SOURCE=.\src\sat\cadical\cadical_decompose.cpp
# End Source File
# Begin Source File
SOURCE=.\src\sat\cadical\cadical_deduplicate.cpp
# End Source File
# Begin Source File
SOURCE=.\src\sat\cadical\cadical_definition.cpp
# End Source File
# Begin Source File
SOURCE=.\src\sat\cadical\cadical_drattracer.cpp
# End Source File
# Begin Source File
SOURCE=.\src\sat\cadical\cadical_elim.cpp
# End Source File
# Begin Source File
SOURCE=.\src\sat\cadical\cadical_elimfast.cpp
# End Source File
# Begin Source File
SOURCE=.\src\sat\cadical\cadical_ema.cpp
# End Source File
# Begin Source File
SOURCE=.\src\sat\cadical\cadical_extend.cpp
# End Source File
# Begin Source File
SOURCE=.\src\sat\cadical\cadical_external.cpp
# End Source File
# Begin Source File
SOURCE=.\src\sat\cadical\cadical_external_propagate.cpp
# End Source File
# Begin Source File
SOURCE=.\src\sat\cadical\cadical_factor.cpp
# End Source File
# Begin Source File
SOURCE=.\src\sat\cadical\cadical_file.cpp
# End Source File
# Begin Source File
SOURCE=.\src\sat\cadical\cadical_flags.cpp
# End Source File
# Begin Source File
SOURCE=.\src\sat\cadical\cadical_flip.cpp
# End Source File
# Begin Source File
SOURCE=.\src\sat\cadical\cadical_format.cpp
# End Source File
# Begin Source File
SOURCE=.\src\sat\cadical\cadical_frattracer.cpp
# End Source File
# Begin Source File
SOURCE=.\src\sat\cadical\cadical_gates.cpp
# End Source File
# Begin Source File
SOURCE=.\src\sat\cadical\cadical_idruptracer.cpp
# End Source File
# Begin Source File
SOURCE=.\src\sat\cadical\cadical_instantiate.cpp
# End Source File
# Begin Source File
SOURCE=.\src\sat\cadical\cadical_internal.cpp
# End Source File
# Begin Source File
SOURCE=.\src\sat\cadical\cadical_ipasir.cpp
# End Source File
# Begin Source File
SOURCE=.\src\sat\cadical\cadical_lidruptracer.cpp
# End Source File
# Begin Source File
SOURCE=.\src\sat\cadical\cadical_limit.cpp
# End Source File
# Begin Source File
SOURCE=.\src\sat\cadical\cadical_logging.cpp
# End Source File
# Begin Source File
SOURCE=.\src\sat\cadical\cadical_lookahead.cpp
# End Source File
# Begin Source File
SOURCE=.\src\sat\cadical\cadical_lratchecker.cpp
# End Source File
# Begin Source File
SOURCE=.\src\sat\cadical\cadical_lrattracer.cpp
# End Source File
# Begin Source File
SOURCE=.\src\sat\cadical\cadical_lucky.cpp
# End Source File
# Begin Source File
SOURCE=.\src\sat\cadical\cadical_message.cpp
# End Source File
# Begin Source File
SOURCE=.\src\sat\cadical\cadical_minimize.cpp
# End Source File
# Begin Source File
SOURCE=.\src\sat\cadical\cadical_occs.cpp
# End Source File
# Begin Source File
SOURCE=.\src\sat\cadical\cadical_options.cpp
# End Source File
# Begin Source File
SOURCE=.\src\sat\cadical\cadical_parse.cpp
# End Source File
# Begin Source File
SOURCE=.\src\sat\cadical\cadical_phases.cpp
# End Source File
# Begin Source File
SOURCE=.\src\sat\cadical\cadical_probe.cpp
# End Source File
# Begin Source File
SOURCE=.\src\sat\cadical\cadical_profile.cpp
# End Source File
# Begin Source File
SOURCE=.\src\sat\cadical\cadical_proof.cpp
# End Source File
# Begin Source File
SOURCE=.\src\sat\cadical\cadical_propagate.cpp
# End Source File
# Begin Source File
SOURCE=.\src\sat\cadical\cadical_queue.cpp
# End Source File
# Begin Source File
SOURCE=.\src\sat\cadical\cadical_random.cpp
# End Source File
# Begin Source File
SOURCE=.\src\sat\cadical\cadical_reap.cpp
# End Source File
# Begin Source File
SOURCE=.\src\sat\cadical\cadical_reduce.cpp
# End Source File
# Begin Source File
SOURCE=.\src\sat\cadical\cadical_rephase.cpp
# End Source File
# Begin Source File
SOURCE=.\src\sat\cadical\cadical_report.cpp
# End Source File
# Begin Source File
SOURCE=.\src\sat\cadical\cadical_resources.cpp
# End Source File
# Begin Source File
SOURCE=.\src\sat\cadical\cadical_restart.cpp
# End Source File
# Begin Source File
SOURCE=.\src\sat\cadical\cadical_restore.cpp
# End Source File
# Begin Source File
SOURCE=.\src\sat\cadical\cadical_score.cpp
# End Source File
# Begin Source File
SOURCE=.\src\sat\cadical\cadical_shrink.cpp
# End Source File
# Begin Source File
SOURCE=.\src\sat\cadical\cadical_signal.cpp
# End Source File
# Begin Source File
SOURCE=.\src\sat\cadical\cadical_solution.cpp
# End Source File
# Begin Source File
SOURCE=.\src\sat\cadical\cadical_solver.cpp
# End Source File
# Begin Source File
SOURCE=.\src\sat\cadical\cadical_stable.cpp
# End Source File
# Begin Source File
SOURCE=.\src\sat\cadical\cadical_stats.cpp
# End Source File
# Begin Source File
SOURCE=.\src\sat\cadical\cadical_subsume.cpp
# End Source File
# Begin Source File
SOURCE=.\src\sat\cadical\cadical_sweep.cpp
# End Source File
# Begin Source File
SOURCE=.\src\sat\cadical\cadical_terminal.cpp
# End Source File
# Begin Source File
SOURCE=.\src\sat\cadical\cadical_ternary.cpp
# End Source File
# Begin Source File
SOURCE=.\src\sat\cadical\cadical_tier.cpp
# End Source File
# Begin Source File
SOURCE=.\src\sat\cadical\cadical_transred.cpp
# End Source File
# Begin Source File
SOURCE=.\src\sat\cadical\cadical_unstable.cpp
# End Source File
# Begin Source File
SOURCE=.\src\sat\cadical\cadical_util.cpp
# End Source File
# Begin Source File
SOURCE=.\src\sat\cadical\cadical_var.cpp
# End Source File
# Begin Source File
SOURCE=.\src\sat\cadical\cadical_veripbtracer.cpp
# End Source File
# Begin Source File
SOURCE=.\src\sat\cadical\cadical_version.cpp
# End Source File
# Begin Source File
SOURCE=.\src\sat\cadical\cadical_vivify.cpp
# End Source File
# Begin Source File
SOURCE=.\src\sat\cadical\cadical_walk.cpp
# End Source File
# Begin Source File
SOURCE=.\src\sat\cadical\cadical_watch.cpp
# End Source File
# Begin Source File
SOURCE=.\src\sat\cadical\cadicalSolver.c
# End Source File
# Begin Source File
SOURCE=.\src\sat\cadical\cadicalTest.c
# End Source File
# End Group
# End Group
# Begin Group "opt"

View File

@ -56155,9 +56155,6 @@ int Abc_CommandAbc9Test( Abc_Frame_t * pAbc, int argc, char ** argv )
}
}
extern void cadical_solver_test();
cadical_solver_test();
return 0;
extern void kissat_solver_test();
kissat_solver_test();
return 0;

View File

@ -1,28 +0,0 @@
MIT License
Copyright (c) 2016-2021 Armin Biere, Johannes Kepler University Linz, Austria
Copyright (c) 2020-2021 Mathias Fleury, Johannes Kepler University Linz, Austria
Copyright (c) 2020-2021 Nils Froleyks, Johannes Kepler University Linz, Austria
Copyright (c) 2022-2024 Katalin Fazekas, Vienna University of Technology, Austria
Copyright (c) 2021-2024 Armin Biere, University of Freiburg, Germany
Copyright (c) 2021-2024 Mathias Fleury, University of Freiburg, Germany
Copyright (c) 2023-2024 Florian Pollitt, University of Freiburg, Germany
Copyright (c) 2024-2024 Tobias Faller, University of Freiburg, Germany
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

View File

@ -1 +0,0 @@
2.2.0-rc1

View File

@ -1,111 +0,0 @@
#ifndef _arena_hpp_INCLUDED
#define _arena_hpp_INCLUDED
#include "global.h"
ABC_NAMESPACE_CXX_HEADER_START
namespace CaDiCaL {
// This memory allocation arena provides fixed size pre-allocated memory for
// the moving garbage collector 'copy_non_garbage_clauses' in 'collect.cpp'
// to hold clauses which should survive garbage collection.
// The advantage of using a pre-allocated arena is that the allocation order
// of the clauses can be adapted in such a way that clauses watched by the
// same literal are allocated consecutively. This improves locality during
// propagation and thus is more cache friendly. A similar technique is
// implemented in MiniSAT and Glucose and gives substantial speed-up in
// propagations per second even though it might even almost double peek
// memory usage. Note that in MiniSAT this arena is actually required for
// MiniSAT to be able to use 32 bit clauses references instead of 64 bit
// pointers. This would restrict the maximum number of clauses and thus is
// a restriction we do not want to use anymore.
// New learned clauses are allocated in CaDiCaL outside of this arena and
// moved to the arena during garbage collection. The additional 'to' space
// required for such a moving garbage collector is only allocated for those
// clauses surviving garbage collection, which usually needs much less
// memory than all clauses. The net effect is that in our implementation
// the moving garbage collector using this arena only needs roughly 50% more
// memory than allocating the clauses directly. Both implementations can be
// compared by varying the 'opts.arenatype' option (which also controls the
// allocation order of clauses during moving them).
// The standard sequence of using the arena is as follows:
//
// Arena arena;
// ...
// arena.prepare (bytes);
// q1 = arena.copy (p1, bytes1);
// ...
// qn = arena.copy (pn, bytesn);
// CADICAL_assert (bytes1 + ... + bytesn <= bytes);
// arena.swap ();
// ...
// if (!arena.contains (q)) delete q;
// ...
// arena.prepare (bytes);
// q1 = arena.copy (p1, bytes1);
// ...
// qn = arena.copy (pn, bytesn);
// CADICAL_assert (bytes1 + ... + bytesn <= bytes);
// arena.swap ();
// ...
//
// One has to be really careful with 'qi' references to arena memory.
struct Internal;
class Arena {
Internal *internal;
struct {
char *start, *top, *end;
} from, to;
public:
Arena (Internal *);
~Arena ();
// Prepare 'to' space to hold that amount of memory. Precondition is that
// the 'to' space is empty. The following sequence of 'copy' operations
// can use as much memory in sum as pre-allocated here.
//
void prepare (size_t bytes);
// Does the memory pointed to by 'p' belong to this arena? More precisely
// to the 'from' space, since that is the only one remaining after 'swap'.
//
bool contains (void *p) const {
char *c = (char *) p;
return (from.start <= c && c < from.top) ||
(to.start <= c && c < to.top);
}
// Allocate that amount of memory in 'to' space. This assumes the 'to'
// space has been prepared to hold enough memory with 'prepare'. Then
// copy the memory pointed to by 'p' of size 'bytes'. Note that it does
// not matter whether 'p' is in 'from' or allocated outside of the arena.
//
char *copy (const char *p, size_t bytes) {
char *res = to.top;
to.top += bytes;
CADICAL_assert (to.top <= to.end);
memcpy (res, p, bytes);
return res;
}
// Completely delete 'from' space and then replace 'from' by 'to' (by
// pointer swapping). Everything previously allocated (in 'from') and not
// explicitly copied to 'to' with 'copy' becomes invalid.
//
void swap ();
};
} // namespace CaDiCaL
ABC_NAMESPACE_CXX_HEADER_END
#endif

View File

@ -1,43 +0,0 @@
#ifndef _averages_hpp_INCLUDED
#define _averages_hpp_INCLUDED
#include "global.h"
#include "ema.hpp" // alphabetically after 'averages.hpp'
ABC_NAMESPACE_CXX_HEADER_START
namespace CaDiCaL {
struct Averages {
int64_t swapped;
struct {
struct {
EMA fast; // average fast (small window) moving glucose level
EMA slow; // average slow (large window) moving glucose level
} glue;
struct {
EMA fast; // average fast (small window) moving trail level
EMA slow; // average slow (large window) moving trail level
} trail;
EMA decisions;
EMA size; // average learned clause size
EMA jump; // average (potential non-chronological) back-jump level
EMA level; // average back track level after conflict
} current, saved;
Averages () : swapped (0) {}
};
} // namespace CaDiCaL
ABC_NAMESPACE_CXX_HEADER_END
#endif

View File

@ -1,28 +0,0 @@
#ifndef _bins_hpp_INCLUDED
#define _bins_hpp_INCLUDED
#include "global.h"
#include "util.hpp" // Alphabetically after 'bins'.
ABC_NAMESPACE_CXX_HEADER_START
namespace CaDiCaL {
using namespace std;
struct Bin {
int lit;
int64_t id;
};
typedef vector<Bin> Bins;
inline void shrink_bins (Bins &bs) { shrink_vector (bs); }
inline void erase_bins (Bins &bs) { erase_vector (bs); }
} // namespace CaDiCaL
ABC_NAMESPACE_CXX_HEADER_END
#endif

View File

@ -1,43 +0,0 @@
#ifndef _block_hpp_INCLUDED
#define _block_hpp_INCLUDED
#include "global.h"
#include "heap.hpp" // Alphabetically after 'block.hpp'.
ABC_NAMESPACE_CXX_HEADER_START
namespace CaDiCaL {
struct Internal;
struct block_more_occs_size {
Internal *internal;
block_more_occs_size (Internal *i) : internal (i) {}
bool operator() (unsigned a, unsigned b);
};
typedef heap<block_more_occs_size> BlockSchedule;
class Blocker {
friend struct Internal;
vector<struct Clause *> candidates;
vector<struct Clause *> reschedule;
BlockSchedule schedule;
Blocker (Internal *i) : schedule (block_more_occs_size (i)) {}
void erase () {
erase_vector (candidates);
erase_vector (reschedule);
schedule.erase ();
}
};
} // namespace CaDiCaL
ABC_NAMESPACE_CXX_HEADER_END
#endif

File diff suppressed because it is too large Load Diff

View File

@ -1,305 +0,0 @@
/**CFile****************************************************************
FileName [cadicalSolver.c]
SystemName [ABC: Logic synthesis and verification system.]
PackageName [SAT solver CaDiCaL by Armin Biere, University of Freiburg]
Synopsis [https://github.com/arminbiere/cadical]
Author [Integrated into ABC by Yukio Miyasaka]
Affiliation [UC Berkeley]
Date [Ver. 1.0. Started - June 20, 2005.]
Revision [$Id: cadicalSolver.c,v 1.00 2005/06/20 00:00:00 alanmi Exp $]
***********************************************************************/
#include "ccadical.h"
#include "cadicalSolver.h"
ABC_NAMESPACE_IMPL_START
////////////////////////////////////////////////////////////////////////
/// DECLARATIONS ///
////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////
/// FUNCTION DEFINITIONS ///
////////////////////////////////////////////////////////////////////////
/**Function*************************************************************
Synopsis [allocate solver]
Description []
SideEffects []
SeeAlso []
***********************************************************************/
cadical_solver* cadical_solver_new(void) {
cadical_solver* s = (cadical_solver*)malloc(sizeof(cadical_solver));
s->p = (void*)ccadical_init();
s->nVars = 0;
s->vAssumptions = NULL;
s->vCore = NULL;
return s;
}
/**Function*************************************************************
Synopsis [delete solver]
Description []
SideEffects []
SeeAlso []
***********************************************************************/
void cadical_solver_delete(cadical_solver* s) {
ccadical_release((CCaDiCaL*)s->p);
if(s->vAssumptions) {
Vec_IntFree(s->vAssumptions);
}
if(s->vCore) {
Vec_IntFree(s->vCore);
}
free(s);
}
/**Function*************************************************************
Synopsis [add clause]
Description [cadical takes x and -x as a literal for a variable x > 0,
where 0 is an indicator of the end of a clause.
since variables start from 0 in abc, a variable v is
translated into v + 1 in cadical.]
SideEffects []
SeeAlso []
***********************************************************************/
int cadical_solver_addclause(cadical_solver* s, int* begin, int* end) {
for(;begin != end; begin++) {
if(*begin & 1) {
ccadical_add((CCaDiCaL*)s->p, -(1 + ((*begin) >> 1)));
} else {
ccadical_add((CCaDiCaL*)s->p, 1 + ((*begin) >> 1) );
}
}
ccadical_add((CCaDiCaL*)s->p, 0);
return !ccadical_is_inconsistent((CCaDiCaL*)s->p);
}
/**Function*************************************************************
Synopsis [solve with resource limits]
Description [assumptions and inspection limits are not supported.]
SideEffects []
SeeAlso []
***********************************************************************/
int cadical_solver_solve(cadical_solver* s, int* begin, int* end, ABC_INT64_T nConfLimit, ABC_INT64_T nInsLimit, ABC_INT64_T nConfLimitGlobal, ABC_INT64_T nInsLimitGlobal) {
// inspection limits are not supported
assert(nInsLimit == 0);
assert(nInsLimitGlobal == 0);
// set conflict limits
if(nConfLimit)
ccadical_limit((CCaDiCaL*)s->p, "conflicts", nConfLimit);
if(nConfLimitGlobal && (nConfLimit == 0 || nConfLimit > nConfLimitGlobal))
ccadical_limit((CCaDiCaL*)s->p, "conflicts", nConfLimitGlobal);
// assumptions
if(begin != end) {
// save
if(s->vAssumptions == NULL) {
s->vAssumptions = Vec_IntAllocArrayCopy(begin, end - begin);
} else {
Vec_IntClear(s->vAssumptions);
Vec_IntGrow(s->vAssumptions, end - begin);
Vec_IntPushArray(s->vAssumptions, begin, end - begin);
}
// assume
for(;begin != end; begin++) {
if(*begin & 1) {
ccadical_assume((CCaDiCaL*)s->p, -(1 + ((*begin) >> 1)));
} else {
ccadical_assume((CCaDiCaL*)s->p, 1 + ((*begin) >> 1) );
}
}
}
// solve
int res = ccadical_solve((CCaDiCaL*)s->p);
// translate this cadical return value into a corresponding ABC status value
switch(res) {
case 0: // UNDETERMINED
return 0;
case 10: // SATISFIABLE
return 1;
case 20: // UNSATISFIABLE
return -1;
default:
assert(0);
}
return 0;
}
/**Function*************************************************************
Synopsis [get unsat core]
Description [following minisat, return number of literals in core,
which consists of responsible assumptions, negated.
array will be freed by solver.]
SideEffects []
SeeAlso []
***********************************************************************/
int cadical_solver_final(cadical_solver* s, int** ppArray) {
int v, i;
if(s->vCore == NULL) {
s->vCore = Vec_IntAlloc(Vec_IntSize(s->vAssumptions));
} else {
Vec_IntClear(s->vCore);
}
Vec_IntForEachEntry(s->vAssumptions, v, i) {
int failed;
if(v & 1) {
failed = ccadical_failed((CCaDiCaL*)s->p, -(1 + (v >> 1)));
} else {
failed = ccadical_failed((CCaDiCaL*)s->p, 1 + (v >> 1) );
}
if(failed) {
Vec_IntPush(s->vCore, Abc_LitNot(v));
}
}
*ppArray = Vec_IntArray(s->vCore);
return Vec_IntSize(s->vCore);
}
/**Function*************************************************************
Synopsis [get number of variables]
Description [emulated using "nVars".]
SideEffects []
SeeAlso []
***********************************************************************/
int cadical_solver_nvars(cadical_solver* s) {
return s->nVars;
}
/**Function*************************************************************
Synopsis [add new variable]
Description [emulated using "nVars".]
SideEffects []
SeeAlso []
***********************************************************************/
int cadical_solver_addvar(cadical_solver* s) {
return s->nVars++;
}
/**Function*************************************************************
Synopsis [set number of variables]
Description [not only emulate with "nVars" but also reserve memory.]
SideEffects []
SeeAlso []
***********************************************************************/
void cadical_solver_setnvars(cadical_solver* s,int n) {
s->nVars = n;
ccadical_reserve((CCaDiCaL*)s->p, n);
}
/**Function*************************************************************
Synopsis [get value of variable]
Description [cadical returns x (true) or -x (false) for a variable x.
note a variable v was translated into v + 1 in cadical.]
SideEffects []
SeeAlso []
***********************************************************************/
int cadical_solver_get_var_value(cadical_solver* s, int v) {
return ccadical_val((CCaDiCaL*)s->p, v + 1) > 0;
}
/**Function*************************************************************
Synopsis [Solves the given CNF using cadical.]
Description []
SideEffects []
SeeAlso []
***********************************************************************/
Vec_Int_t * cadical_solve_cnf( Cnf_Dat_t * pCnf, char * pArgs, int nConfs, int nTimeLimit, int fSat, int fUnsat, int fPrintCex, int fVerbose )
{
abctime clk = Abc_Clock();
Vec_Int_t * vRes = NULL;
int i, * pBeg, * pEnd, RetValue;
if ( fVerbose )
printf( "CNF stats: Vars = %6d. Clauses = %7d. Literals = %8d. ", pCnf->nVars, pCnf->nClauses, pCnf->nLiterals );
cadical_solver *pSat = cadical_solver_new();
cadical_solver_setnvars(pSat, pCnf->nVars);
assert(cadical_solver_nvars(pSat) == pCnf->nVars);
Cnf_CnfForClause( pCnf, pBeg, pEnd, i ) {
if ( !cadical_solver_addclause(pSat, pBeg, pEnd) )
{
assert( 0 ); // if it happens, can return 1 (unsatisfiable)
return NULL;
}
}
RetValue = cadical_solver_solve(pSat, NULL, NULL, 0, 0, 0, 0);
if ( RetValue == 1 )
printf( "Result: Satisfiable. " );
else if ( RetValue == -1 )
printf( "Result: Unsatisfiable. " );
else
printf( "Result: Undecided. " );
if ( RetValue == 1 ) {
vRes = Vec_IntAlloc( pCnf->nVars );
for ( i = 0; i < pCnf->nVars; i++ )
Vec_IntPush( vRes, cadical_solver_get_var_value(pSat, i) );
}
cadical_solver_delete(pSat);
Abc_PrintTime( 1, "Time", Abc_Clock() - clk );
return vRes;
}
////////////////////////////////////////////////////////////////////////
/// END OF FILE ///
////////////////////////////////////////////////////////////////////////
ABC_NAMESPACE_IMPL_END

View File

@ -1,76 +0,0 @@
/**CFile****************************************************************
FileName [cadicalSolver.h]
SystemName [ABC: Logic synthesis and verification system.]
PackageName [SAT solver CaDiCaL by Armin Biere, University of Freiburg]
Synopsis [https://github.com/arminbiere/cadical]
Author [Integrated into ABC by Yukio Miyasaka]
Affiliation [UC Berkeley]
Date [Ver. 1.0. Started - June 20, 2005.]
Revision [$Id: cadicalSolver.h,v 1.00 2005/06/20 00:00:00 alanmi Exp $]
***********************************************************************/
#ifndef ABC_SAT_CADICAL_SOLVER_H_
#define ABC_SAT_CADICAL_SOLVER_H_
////////////////////////////////////////////////////////////////////////
/// INCLUDES ///
////////////////////////////////////////////////////////////////////////
#include "aig/gia/gia.h"
#include "sat/cnf/cnf.h"
////////////////////////////////////////////////////////////////////////
/// PARAMETERS ///
////////////////////////////////////////////////////////////////////////
ABC_NAMESPACE_HEADER_START
////////////////////////////////////////////////////////////////////////
/// BASIC TYPES ///
////////////////////////////////////////////////////////////////////////
typedef struct cadical_solver_ cadical_solver;
struct cadical_solver_
{
void* p;
int nVars;
Vec_Int_t* vAssumptions;
Vec_Int_t* vCore;
};
////////////////////////////////////////////////////////////////////////
/// MACRO DEFINITIONS ///
////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////
/// FUNCTION DECLARATIONS ///
////////////////////////////////////////////////////////////////////////
extern cadical_solver* cadical_solver_new(void);
extern void cadical_solver_delete(cadical_solver* s);
extern int cadical_solver_addclause(cadical_solver* s, int* begin, int* end);
extern int cadical_solver_solve(cadical_solver* s, int* begin, int* end, ABC_INT64_T nConfLimit, ABC_INT64_T nInsLimit, ABC_INT64_T nConfLimitGlobal, ABC_INT64_T nInsLimitGlobal);
extern int cadical_solver_final(cadical_solver* s, int** ppArray);
extern int cadical_solver_nvars(cadical_solver* s);
extern int cadical_solver_addvar(cadical_solver* s);
extern void cadical_solver_setnvars(cadical_solver* s,int n);
extern int cadical_solver_get_var_value(cadical_solver* s, int v);
extern Vec_Int_t * cadical_solve_cnf( Cnf_Dat_t * pCnf, char * pArgs, int nConfs, int nTimeLimit, int fSat, int fUnsat, int fPrintCex, int fVerbose );
ABC_NAMESPACE_HEADER_END
#endif
////////////////////////////////////////////////////////////////////////
/// END OF FILE ///
////////////////////////////////////////////////////////////////////////

View File

@ -1,210 +0,0 @@
/**CFile****************************************************************
FileName [cadicalTest.c]
SystemName [ABC: Logic synthesis and verification system.]
PackageName []
Synopsis []
Author [Alan Mishchenko]
Affiliation [UC Berkeley]
Date [Ver. 1.0. Started - June 20, 2005.]
Revision [$Id: cadicalTest.c,v 1.00 2005/06/20 00:00:00 alanmi Exp $]
***********************************************************************/
#include "cadicalSolver.h"
ABC_NAMESPACE_IMPL_START
////////////////////////////////////////////////////////////////////////
/// DECLARATIONS ///
////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////
/// FUNCTION DEFINITIONS ///
////////////////////////////////////////////////////////////////////////
void cadical_solver_test() {
int RetValue;
int Lits[3];
// test 1
{
cadical_solver *pSat = cadical_solver_new();
int a = cadical_solver_addvar(pSat);
int b = cadical_solver_addvar(pSat);
int c = cadical_solver_addvar(pSat);
assert(cadical_solver_nvars(pSat) == 3);
Lits[0] = Abc_Var2Lit(a, 0);
Lits[1] = Abc_Var2Lit(b, 0);
Lits[2] = Abc_Var2Lit(c, 0);
printf("adding (a, b, c)\n");
RetValue = cadical_solver_addclause(pSat, Lits, Lits + 3);
assert(RetValue);
Lits[0] = Abc_Var2Lit(a, 0);
Lits[1] = Abc_Var2Lit(b, 1);
printf("adding (a, !b)\n");
RetValue = cadical_solver_addclause(pSat, Lits, Lits + 2);
assert(RetValue);
Lits[0] = Abc_Var2Lit(a, 1);
printf("adding (!a)\n");
RetValue = cadical_solver_addclause(pSat, Lits, Lits + 1);
assert(RetValue);
RetValue = cadical_solver_solve(pSat, NULL, NULL, 0, 0, 0, 0);
printf("solved: %d\n", RetValue);
assert(RetValue == 1);
int a_val = cadical_solver_get_var_value(pSat, a);
int b_val = cadical_solver_get_var_value(pSat, b);
int c_val = cadical_solver_get_var_value(pSat, c);
printf("a = %d, b = %d, c = %d\n", a_val, b_val, c_val);
assert(a_val == 0);
assert(b_val == 0);
assert(c_val == 1);
cadical_solver_delete(pSat);
printf("test 1 passed\n");
}
// test 2
{
cadical_solver *pSat = cadical_solver_new();
cadical_solver_setnvars(pSat, 2);
assert(cadical_solver_nvars(pSat) == 2);
Lits[0] = Abc_Var2Lit(0, 0);
Lits[1] = Abc_Var2Lit(1, 0);
printf("adding (x0, x1)\n");
RetValue = cadical_solver_addclause(pSat, Lits, Lits + 2);
assert(RetValue);
Lits[0] = Abc_Var2Lit(0, 0);
Lits[1] = Abc_Var2Lit(1, 1);
printf("adding (x0, !x1)\n");
RetValue = cadical_solver_addclause(pSat, Lits, Lits + 2);
assert(RetValue);
Lits[0] = Abc_Var2Lit(0, 1);
Lits[1] = Abc_Var2Lit(1, 1);
printf("adding (!x0, !x1)\n");
RetValue = cadical_solver_addclause(pSat, Lits, Lits + 2);
assert(RetValue);
RetValue = cadical_solver_solve(pSat, NULL, NULL, 0, 0, 0, 0);
printf("solved: %d\n", RetValue);
assert(RetValue == 1);
printf("x0 = %d, x1 = %d\n", cadical_solver_get_var_value(pSat, 0), cadical_solver_get_var_value(pSat, 1));
assert(cadical_solver_get_var_value(pSat, 0) == 1);
assert(cadical_solver_get_var_value(pSat, 1) == 0);
cadical_solver_delete(pSat);
printf("test 2 passed\n");
}
// test 3
{
cadical_solver *pSat = cadical_solver_new();
cadical_solver_setnvars(pSat, 3);
assert(cadical_solver_nvars(pSat) == 3);
Lits[0] = Abc_Var2Lit(0, 1);
Lits[1] = Abc_Var2Lit(1, 0);
Lits[2] = Abc_Var2Lit(2, 1);
printf("adding (!x0, x1, !x2)\n");
RetValue = cadical_solver_addclause(pSat, Lits, Lits + 3);
assert(RetValue);
Lits[0] = Abc_Var2Lit(0, 0);
printf("adding (x0)\n");
RetValue = cadical_solver_addclause(pSat, Lits, Lits + 1);
assert(RetValue);
Lits[0] = Abc_Var2Lit(1, 1);
printf("adding (!x1)\n");
RetValue = cadical_solver_addclause(pSat, Lits, Lits + 1);
assert(RetValue);
Lits[0] = Abc_Var2Lit(2, 0);
printf("adding (x2)\n");
RetValue = cadical_solver_addclause(pSat, Lits, Lits + 1);
RetValue = cadical_solver_solve(pSat, NULL, NULL, 0, 0, 0, 0);
printf("solved: %d\n", RetValue);
assert(RetValue == -1);
cadical_solver_delete(pSat);
printf("test 3 passed\n");
}
// test 4
{
cadical_solver *pSat = cadical_solver_new();
cadical_solver_setnvars(pSat, 3);
assert(cadical_solver_nvars(pSat) == 3);
Lits[0] = Abc_Var2Lit(0, 1);
Lits[1] = Abc_Var2Lit(1, 0);
Lits[2] = Abc_Var2Lit(2, 1);
printf("adding (!x0, x1, !x2)\n");
RetValue = cadical_solver_addclause(pSat, Lits, Lits + 3);
assert(RetValue);
Lits[0] = Abc_Var2Lit(0, 0);
printf("adding (x0)\n");
RetValue = cadical_solver_addclause(pSat, Lits, Lits + 1);
assert(RetValue);
Lits[0] = Abc_Var2Lit(1, 1);
printf("assume (!x1)\n");
RetValue = cadical_solver_solve(pSat, Lits, Lits + 1, 0, 0, 0, 0);
printf("solved: %d\n", RetValue);
assert(RetValue == 1);
printf("x0 = %d, x1 = %d, x2 = %d\n", cadical_solver_get_var_value(pSat, 0), cadical_solver_get_var_value(pSat, 1), cadical_solver_get_var_value(pSat, 2));
assert(cadical_solver_get_var_value(pSat, 0) == 1);
assert(cadical_solver_get_var_value(pSat, 1) == 0);
assert(cadical_solver_get_var_value(pSat, 2) == 0);
cadical_solver_delete(pSat);
printf("test 4 passed\n");
}
// test 5
{
cadical_solver *pSat = cadical_solver_new();
cadical_solver_setnvars(pSat, 3);
assert(cadical_solver_nvars(pSat) == 3);
Lits[0] = Abc_Var2Lit(0, 1);
Lits[1] = Abc_Var2Lit(1, 0);
Lits[2] = Abc_Var2Lit(2, 1);
printf("adding (!x0, x1, !x2)\n");
RetValue = cadical_solver_addclause(pSat, Lits, Lits + 3);
assert(RetValue);
Lits[0] = Abc_Var2Lit(0, 1);
Lits[1] = Abc_Var2Lit(1, 1);
Lits[2] = Abc_Var2Lit(2, 1);
printf("adding (!x0, !x1, !x2)\n");
RetValue = cadical_solver_addclause(pSat, Lits, Lits + 3);
assert(RetValue);
Lits[0] = Abc_Var2Lit(0, 0);
printf("adding (x0)\n");
RetValue = cadical_solver_addclause(pSat, Lits, Lits + 1);
assert(RetValue);
Lits[0] = Abc_Var2Lit(1, 0);
Lits[1] = Abc_Var2Lit(2, 0);
printf("assume (x1, x2)\n");
RetValue = cadical_solver_solve(pSat, Lits, Lits + 2, 0, 0, 0, 0);
printf("solved: %d\n", RetValue);
assert(RetValue == -1);
int *pCore;
int nSize = cadical_solver_final(pSat, &pCore);
printf("core: ");
for(int i = 0; i < nSize; i++) {
if(i) {
printf(", ");
}
if(Abc_LitIsCompl(pCore[i])) {
printf("!");
}
printf("x%d", Abc_Lit2Var(pCore[i]));
}
printf("\n");
int neg_x2_in_core = 0;
for(int i = 0; i < nSize; i++) {
if(Abc_LitIsCompl(pCore[i]) && Abc_Lit2Var(pCore[i]) == 2) {
neg_x2_in_core = 1;
}
}
assert(neg_x2_in_core);
cadical_solver_delete(pSat);
printf("test 5 passed\n");
}
}
////////////////////////////////////////////////////////////////////////
/// END OF FILE ///
////////////////////////////////////////////////////////////////////////
ABC_NAMESPACE_IMPL_END

File diff suppressed because it is too large Load Diff

View File

@ -1,36 +0,0 @@
#include "global.h"
#include "internal.hpp"
ABC_NAMESPACE_IMPL_START
namespace CaDiCaL {
Arena::Arena (Internal *i) {
memset (this, 0, sizeof *this);
internal = i;
}
Arena::~Arena () {
delete[] from.start;
delete[] to.start;
}
void Arena::prepare (size_t bytes) {
LOG ("preparing 'to' space of arena with %zd bytes", bytes);
CADICAL_assert (!to.start);
to.top = to.start = new char[bytes];
to.end = to.start + bytes;
}
void Arena::swap () {
delete[] from.start;
LOG ("delete 'from' space of arena with %zd bytes",
(size_t) (from.end - from.start));
from = to;
to.start = to.top = to.end = 0;
}
} // namespace CaDiCaL
ABC_NAMESPACE_IMPL_END

View File

@ -1,613 +0,0 @@
#include "global.h"
#include "internal.hpp"
#include "options.hpp"
ABC_NAMESPACE_IMPL_START
namespace CaDiCaL {
// Failed literal handling as pioneered by MiniSAT. This first function
// adds an assumption literal onto the assumption stack.
void Internal::assume (int lit) {
if (level && !opts.ilbassumptions)
backtrack ();
else if (val (lit) < 0)
backtrack (max (0, var (lit).level - 1));
Flags &f = flags (lit);
const unsigned char bit = bign (lit);
if (f.assumed & bit) {
LOG ("ignoring already assumed %d", lit);
return;
}
LOG ("assume %d", lit);
f.assumed |= bit;
assumptions.push_back (lit);
freeze (lit);
}
// for LRAT we actually need to implement recursive DFS
// for non-lrat use BFS. TODO: maybe derecursify to avoid stack overflow
//
void Internal::assume_analyze_literal (int lit) {
CADICAL_assert (lit);
Flags &f = flags (lit);
if (f.seen)
return;
f.seen = true;
analyzed.push_back (lit);
Var &v = var (lit);
CADICAL_assert (val (lit) < 0);
if (v.reason == external_reason) {
v.reason = wrapped_learn_external_reason_clause (-lit);
CADICAL_assert (v.reason || !v.level);
}
CADICAL_assert (v.reason != external_reason);
if (!v.level) {
int64_t id = unit_id (-lit);
lrat_chain.push_back (id);
return;
}
if (v.reason) {
CADICAL_assert (v.level);
LOG (v.reason, "analyze reason");
for (const auto &other : *v.reason) {
assume_analyze_literal (other);
}
lrat_chain.push_back (v.reason->id);
return;
}
CADICAL_assert (assumed (-lit));
LOG ("failed assumption %d", -lit);
clause.push_back (lit);
}
void Internal::assume_analyze_reason (int lit, Clause *reason) {
CADICAL_assert (reason);
CADICAL_assert (lrat_chain.empty ());
CADICAL_assert (reason != external_reason);
CADICAL_assert (lrat);
for (const auto &other : *reason)
if (other != lit)
assume_analyze_literal (other);
lrat_chain.push_back (reason->id);
}
// Find all failing assumptions starting from the one on the assumption
// stack with the lowest decision level. This goes back to MiniSAT and is
// called 'analyze_final' there.
void Internal::failing () {
START (analyze);
LOG ("analyzing failing assumptions");
CADICAL_assert (analyzed.empty ());
CADICAL_assert (clause.empty ());
CADICAL_assert (lrat_chain.empty ());
CADICAL_assert (!marked_failed);
CADICAL_assert (!conflict_id);
if (!unsat_constraint) {
// Search for failing assumptions in the (internal) assumption stack.
// There are in essence three cases: (1) An assumption is falsified on
// the root-level and then 'failed_unit' is set to that assumption, (2)
// two clashing assumptions are assumed and then 'failed_clashing' is
// set to the second assumed one, or otherwise (3) there is a failing
// assumption 'first_failed' with minimum (non-zero) decision level
// 'failed_level'.
int failed_unit = 0;
int failed_clashing = 0;
int first_failed = 0;
int failed_level = INT_MAX;
int efailed = 0;
for (auto &elit : external->assumptions) {
int lit = external->e2i[abs (elit)];
if (elit < 0)
lit = -lit;
if (val (lit) >= 0)
continue;
const Var &v = var (lit);
if (!v.level) {
failed_unit = lit;
efailed = elit;
break;
}
if (failed_clashing)
continue;
if (v.reason == external_reason) {
Var &ev = var (lit);
ev.reason = learn_external_reason_clause (-lit);
if (!ev.reason) {
ev.level = 0;
failed_unit = lit;
efailed = elit;
break;
}
ev.level = 0;
// Recalculate assignment level
for (const auto &other : *ev.reason) {
if (other == -lit)
continue;
CADICAL_assert (val (other));
int tmp = var (other).level;
if (tmp > ev.level)
ev.level = tmp;
}
if (!ev.level) {
failed_unit = lit;
efailed = elit;
break;
}
}
CADICAL_assert (v.reason != external_reason);
if (!v.reason) {
failed_clashing = lit;
efailed = elit;
} else if (!first_failed || v.level < failed_level) {
first_failed = lit;
efailed = elit;
failed_level = v.level;
}
}
CADICAL_assert (clause.empty ());
// Get the 'failed' assumption from one of the three cases.
int failed;
if (failed_unit)
failed = failed_unit;
else if (failed_clashing)
failed = failed_clashing;
else
failed = first_failed;
CADICAL_assert (failed);
CADICAL_assert (efailed);
// In any case mark literal 'failed' as failed assumption.
{
Flags &f = flags (failed);
const unsigned bit = bign (failed);
CADICAL_assert (!(f.failed & bit));
f.failed |= bit;
}
// First case (1).
if (failed_unit) {
CADICAL_assert (failed == failed_unit);
LOG ("root-level falsified assumption %d", failed);
if (proof) {
if (lrat) {
unsigned eidx = (efailed > 0) + 2u * (unsigned) abs (efailed);
CADICAL_assert ((size_t) eidx < external->ext_units.size ());
const int64_t id = external->ext_units[eidx];
if (id) {
lrat_chain.push_back (id);
} else {
int64_t id = unit_id (-failed_unit);
lrat_chain.push_back (id);
}
}
proof->add_assumption_clause (++clause_id, -efailed, lrat_chain);
conclusion.push_back (clause_id);
lrat_chain.clear ();
}
goto DONE;
}
// Second case (2).
if (failed_clashing) {
CADICAL_assert (failed == failed_clashing);
LOG ("clashing assumptions %d and %d", failed, -failed);
Flags &f = flags (-failed);
const unsigned bit = bign (-failed);
CADICAL_assert (!(f.failed & bit));
f.failed |= bit;
if (proof) {
vector<int> clash = {externalize (failed), externalize (-failed)};
proof->add_assumption_clause (++clause_id, clash, lrat_chain);
conclusion.push_back (clause_id);
}
goto DONE;
}
// Fall through to third case (3).
LOG ("starting with assumption %d falsified on minimum decision level "
"%d",
first_failed, failed_level);
CADICAL_assert (first_failed);
CADICAL_assert (failed_level > 0);
// The 'analyzed' stack serves as working stack for a BFS through the
// implication graph until decisions, which are all assumptions, or
// units are reached. This is simpler than corresponding code in
// 'analyze'.
{
LOG ("failed assumption %d", first_failed);
Flags &f = flags (first_failed);
CADICAL_assert (!f.seen);
f.seen = true;
CADICAL_assert (f.failed & bign (first_failed));
analyzed.push_back (-first_failed);
clause.push_back (-first_failed);
}
} else {
// unsat_constraint
// The assumptions necessary to fail each literal in the constraint are
// collected.
for (auto lit : constraint) {
lit *= -1;
CADICAL_assert (lit != INT_MIN);
flags (lit).seen = true;
analyzed.push_back (lit);
}
}
{
// used for unsat_constraint lrat
vector<vector<int64_t>> constraint_chains;
vector<vector<int>> constraint_clauses;
vector<int> sum_constraints;
vector<int> econstraints;
for (auto &elit : external->constraint) {
int lit = external->e2i[abs (elit)];
if (elit < 0)
lit = -lit;
if (!lit)
continue;
Flags &f = flags (lit);
if (f.seen)
continue;
if (std::find (econstraints.begin (), econstraints.end (), elit) !=
econstraints.end ())
continue;
econstraints.push_back (elit);
}
// no LRAT do bfs as it was before
if (!lrat) {
size_t next = 0;
while (next < analyzed.size ()) {
const int lit = analyzed[next++];
CADICAL_assert (val (lit) > 0);
Var &v = var (lit);
if (!v.level)
continue;
if (v.reason == external_reason) {
v.reason = wrapped_learn_external_reason_clause (lit);
if (!v.reason) {
v.level = 0;
continue;
}
}
CADICAL_assert (v.reason != external_reason);
if (v.reason) {
CADICAL_assert (v.level);
LOG (v.reason, "analyze reason");
for (const auto &other : *v.reason) {
Flags &f = flags (other);
if (f.seen)
continue;
f.seen = true;
CADICAL_assert (val (other) < 0);
analyzed.push_back (-other);
}
} else {
CADICAL_assert (assumed (lit));
LOG ("failed assumption %d", lit);
clause.push_back (-lit);
Flags &f = flags (lit);
const unsigned bit = bign (lit);
CADICAL_assert (!(f.failed & bit));
f.failed |= bit;
}
}
clear_analyzed_literals ();
} else if (!unsat_constraint) { // LRAT for case (3)
CADICAL_assert (clause.size () == 1);
const int lit = clause[0];
Var &v = var (lit);
CADICAL_assert (v.reason);
if (v.reason == external_reason) { // does this even happen?
v.reason = wrapped_learn_external_reason_clause (lit);
}
CADICAL_assert (v.reason != external_reason);
if (v.reason)
assume_analyze_reason (lit, v.reason);
else {
int64_t id = unit_id (lit);
lrat_chain.push_back (id);
}
for (auto &lit : clause) {
Flags &f = flags (lit);
const unsigned bit = bign (-lit);
if (!(f.failed & bit))
f.failed |= bit;
}
clear_analyzed_literals ();
} else { // LRAT for unsat_constraint
CADICAL_assert (clause.empty ());
clear_analyzed_literals ();
for (auto lit : constraint) {
// make sure nothing gets marked failed twice
// also might shortcut the case where
// lrat_chain is empty because clause is tautological
CADICAL_assert (lit != INT_MIN);
assume_analyze_literal (lit);
vector<int64_t> empty;
vector<int> empty2;
constraint_chains.push_back (empty);
constraint_clauses.push_back (empty2);
for (auto ign : clause) {
constraint_clauses.back ().push_back (ign);
Flags &f = flags (ign);
const unsigned bit = bign (-ign);
if (!(f.failed & bit)) {
sum_constraints.push_back (ign);
CADICAL_assert (!(f.failed & bit));
f.failed |= bit;
}
}
clause.clear ();
clear_analyzed_literals ();
for (auto p : lrat_chain) {
constraint_chains.back ().push_back (p);
}
lrat_chain.clear ();
}
for (auto &lit : sum_constraints)
clause.push_back (lit);
}
clear_analyzed_literals ();
// Doing clause minimization here does not do anything because
// the clause already contains only one literal of each level
// and minimization can never reduce the number of levels
VERBOSE (1, "found %zd failed assumptions %.0f%%", clause.size (),
percent (clause.size (), assumptions.size ()));
// We do not actually need to learn this clause, since the conflict is
// forced already by some other clauses. There is also no bumping
// of variables nor clauses necessary. But we still want to check
// correctness of the claim that the determined subset of failing
// assumptions are a high-level core or equivalently their negations
// form a unit-implied clause.
//
if (!unsat_constraint) {
external->check_learned_clause ();
if (proof) {
vector<int> eclause;
for (auto &lit : clause)
eclause.push_back (externalize (lit));
proof->add_assumption_clause (++clause_id, eclause, lrat_chain);
conclusion.push_back (clause_id);
}
} else {
CADICAL_assert (!lrat || (constraint.size () == constraint_clauses.size () &&
constraint.size () == constraint_chains.size ()));
for (auto p = constraint.rbegin (); p != constraint.rend (); p++) {
const auto &lit = *p;
if (lrat) {
clause.clear ();
for (auto &ign : constraint_clauses.back ())
clause.push_back (ign);
constraint_clauses.pop_back ();
}
clause.push_back (-lit);
external->check_learned_clause ();
if (proof) {
if (lrat) {
for (auto p : constraint_chains.back ()) {
lrat_chain.push_back (p);
}
constraint_chains.pop_back ();
LOG (lrat_chain, "assume proof chain with constraints");
}
vector<int> eclause;
for (auto &lit : clause)
eclause.push_back (externalize (lit));
proof->add_assumption_clause (++clause_id, eclause, lrat_chain);
conclusion.push_back (clause_id);
lrat_chain.clear ();
}
clause.pop_back ();
}
if (proof) {
for (auto &elit : econstraints) {
if (lrat) {
unsigned eidx = (elit > 0) + 2u * (unsigned) abs (elit);
CADICAL_assert ((size_t) eidx < external->ext_units.size ());
const int64_t id = external->ext_units[eidx];
if (id) {
lrat_chain.push_back (id);
} else {
int lit = external->e2i[abs (elit)];
if (elit < 0)
lit = -lit;
int64_t id = unit_id (-lit);
lrat_chain.push_back (id);
}
}
proof->add_assumption_clause (++clause_id, -elit, lrat_chain);
conclusion.push_back (clause_id);
lrat_chain.clear ();
}
}
}
lrat_chain.clear ();
clause.clear ();
}
DONE:
STOP (analyze);
}
bool Internal::failed (int lit) {
if (!marked_failed) {
if (!conflict_id)
failing ();
marked_failed = true;
}
conclude_unsat ();
Flags &f = flags (lit);
const unsigned bit = bign (lit);
return (f.failed & bit) != 0;
}
void Internal::conclude_unsat () {
if (!proof || concluded)
return;
concluded = true;
if (!marked_failed) {
CADICAL_assert (conclusion.empty ());
if (!conflict_id)
failing ();
marked_failed = true;
}
ConclusionType con;
if (conflict_id)
con = CONFLICT;
else if (unsat_constraint)
con = CONSTRAINT;
else
con = ASSUMPTIONS;
proof->conclude_unsat (con, conclusion);
}
void Internal::reset_concluded () {
if (proof)
proof->reset_assumptions ();
if (concluded) {
LOG ("reset concluded");
concluded = false;
}
if (conflict_id) {
CADICAL_assert (conclusion.size () == 1);
return;
}
conclusion.clear ();
}
// Add the start of each incremental phase (leaving the state
// 'UNSATISFIABLE' actually) we reset all assumptions.
void Internal::reset_assumptions () {
for (const auto &lit : assumptions) {
Flags &f = flags (lit);
const unsigned char bit = bign (lit);
f.assumed &= ~bit;
f.failed &= ~bit;
melt (lit);
}
LOG ("cleared %zd assumptions", assumptions.size ());
assumptions.clear ();
marked_failed = true;
}
struct sort_assumptions_positive_rank {
Internal *internal;
// Decision level could be 'INT_MAX' and thus 'level + 1' could overflow.
// Therefore we carefully have to use 'unsigned' for levels below.
const unsigned max_level;
sort_assumptions_positive_rank (Internal *s)
: internal (s), max_level (s->level + 1u) {}
typedef uint64_t Type;
// Set assumptions first, then sorted by position on the trail
// unset literals are sorted by literal value.
Type operator() (const int &a) const {
const int val = internal->val (a);
const bool assigned = (val != 0);
const Var &v = internal->var (a);
uint64_t res = (assigned ? (unsigned) v.level : max_level);
res <<= 32;
res |= (assigned ? v.trail : abs (a));
return res;
}
};
struct sort_assumptions_smaller {
Internal *internal;
sort_assumptions_smaller (Internal *s) : internal (s) {}
bool operator() (const int &a, const int &b) const {
return sort_assumptions_positive_rank (internal) (a) <
sort_assumptions_positive_rank (internal) (b);
}
};
// Sort the assumptions by the current position on the trail and backtrack
// 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;
MSORT (opts.radixsortlim, assumptions.begin (), assumptions.end (),
sort_assumptions_positive_rank (this),
sort_assumptions_smaller (this));
unsigned max_level = 0;
// assumptions are sorted by level, with unset at the end
for (auto lit : assumptions) {
if (val (lit))
max_level = var (lit).level;
else
break;
}
const unsigned size = min (level + 1u, max_level + 1);
CADICAL_assert ((size_t) level == control.size () - 1);
LOG (assumptions, "sorted assumptions");
int target = 0;
for (unsigned i = 1, j = 0; i < size;) {
const Level &l = control[i];
const int lit = l.decision;
const int alit = assumptions[j];
const int lev = i;
target = lev;
if (val (alit) > 0 &&
var (alit).level < lev) { // we can ignore propagated assumptions
LOG ("ILB skipping propagation %d", alit);
++j;
continue;
}
if (!lit) { // skip fake decisions
target = lev - 1;
break;
}
++i, ++j;
CADICAL_assert (var (lit).level == lev);
if (l.decision == alit) {
continue;
}
target = lev - 1;
LOG ("first different literal %d on the trail and %d from the "
"assumptions",
lit, alit);
break;
}
if (target < level)
backtrack (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
stats.assumptionsreused += level;
}
} // namespace CaDiCaL
ABC_NAMESPACE_IMPL_END

View File

@ -1,40 +0,0 @@
#include "global.h"
#include "internal.hpp"
ABC_NAMESPACE_IMPL_START
namespace CaDiCaL {
void Internal::init_averages () {
LOG ("initializing averages");
INIT_EMA (averages.current.jump, opts.emajump);
INIT_EMA (averages.current.level, opts.emalevel);
INIT_EMA (averages.current.size, opts.emasize);
INIT_EMA (averages.current.glue.fast, opts.emagluefast);
INIT_EMA (averages.current.glue.slow, opts.emaglueslow);
INIT_EMA (averages.current.decisions, opts.emadecisions);
INIT_EMA (averages.current.trail.fast, opts.ematrailfast);
INIT_EMA (averages.current.trail.slow, opts.ematrailslow);
CADICAL_assert (!averages.swapped);
}
void Internal::swap_averages () {
LOG ("saving current averages");
swap (averages.current, averages.saved);
if (!averages.swapped)
init_averages ();
else
LOG ("swapping in previously saved averages");
averages.swapped++;
}
} // namespace CaDiCaL
ABC_NAMESPACE_IMPL_END

View File

@ -1,179 +0,0 @@
#include "global.h"
#include "internal.hpp"
ABC_NAMESPACE_IMPL_START
namespace CaDiCaL {
// The global assignment stack can only be (partially) reset through
// 'backtrack' which is the only function using 'unassign' (inlined and thus
// local to this file). It turns out that 'unassign' does not need a
// specialization for 'probe' nor 'vivify' and thus it is shared.
inline void Internal::unassign (int lit) {
CADICAL_assert (val (lit) > 0);
set_val (lit, 0);
int idx = vidx (lit);
LOG ("unassign %d @ %d", lit, var (idx).level);
num_assigned--;
// In the standard EVSIDS variable decision heuristic of MiniSAT, we need
// to push variables which become unassigned back to the heap.
//
if (!scores.contains (idx))
scores.push_back (idx);
// For VMTF we need to update the 'queue.unassigned' pointer in case this
// variable sits after the variable to which 'queue.unassigned' currently
// points. See our SAT'15 paper for more details on this aspect.
//
if (queue.bumped < btab[idx])
update_queue_unassigned (idx);
}
/*------------------------------------------------------------------------*/
// Update the current target maximum assignment and also the very best
// assignment. Whether a trail produces a conflict is determined during
// propagation. Thus that all functions in the 'search' loop after
// propagation can assume that 'no_conflict_until' is valid. If a conflict
// is found then the trail before the last decision is used (see the end of
// 'propagate'). During backtracking we can then save this largest
// propagation conflict free assignment. It is saved as both 'target'
// assignment for picking decisions in 'stable' mode and if it is the
// largest ever such assignment also as 'best' assignment. This 'best'
// assignment can then be used in future stable decisions after the next
// 'rephase_best' overwrites saved phases with it.
void Internal::update_target_and_best () {
bool reset = (rephased && stats.conflicts > last.rephase.conflicts);
if (reset) {
target_assigned = 0;
if (rephased == 'B')
best_assigned = 0; // update it again
}
if (no_conflict_until > target_assigned) {
copy_phases (phases.target);
target_assigned = no_conflict_until;
LOG ("new target trail level %zu", target_assigned);
}
if (no_conflict_until > best_assigned) {
copy_phases (phases.best);
best_assigned = no_conflict_until;
LOG ("new best trail level %zu", best_assigned);
}
if (reset) {
report (rephased);
rephased = 0;
}
}
/*------------------------------------------------------------------------*/
void Internal::backtrack (int new_level) {
CADICAL_assert (new_level <= level);
if (new_level == level)
return;
update_target_and_best ();
backtrack_without_updating_phases (new_level);
}
void Internal::backtrack_without_updating_phases (int new_level) {
CADICAL_assert (new_level <= level);
if (new_level == level)
return;
stats.backtracks++;
CADICAL_assert (num_assigned == trail.size ());
const size_t assigned = control[new_level + 1].trail;
LOG ("backtracking to decision level %d with decision %d and trail %zd",
new_level, control[new_level].decision, assigned);
const size_t end_of_trail = trail.size ();
size_t i = assigned, j = i;
#ifdef LOGGING
int unassigned = 0;
#endif
int reassigned = 0;
notify_backtrack (new_level);
if (external_prop && !external_prop_is_lazy && !private_steps &&
notified > assigned) {
LOG ("external propagator is notified about some unassignments (trail: "
"%zd, notified: %zd).",
trail.size (), notified);
notified = assigned;
}
while (i < end_of_trail) {
int lit = trail[i++];
Var &v = var (lit);
if (v.level > new_level) {
unassign (lit);
#ifdef LOGGING
unassigned++;
#endif
} else {
// This is the essence of the SAT'18 paper on chronological
// 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);
#ifdef LOGGING
if (!v.level)
LOG ("reassign %d @ 0 unit clause %d", lit, lit);
else
LOG (v.reason, "reassign %d @ %d", lit, v.level);
#endif
trail[j] = lit;
v.trail = j++;
reassigned++;
}
}
trail.resize (j);
LOG ("unassigned %d literals %.0f%%", unassigned,
percent (unassigned, unassigned + reassigned));
LOG ("reassigned %d literals %.0f%%", reassigned,
percent (reassigned, unassigned + reassigned));
if (propagated > assigned)
propagated = assigned;
if (propagated2 > assigned)
propagated2 = assigned;
if (no_conflict_until > assigned)
no_conflict_until = assigned;
propergated = 0; // Always go back to root-level.
CADICAL_assert (notified <= assigned + reassigned);
if (reassigned) {
notify_assignments ();
}
control.resize (new_level + 1);
level = new_level;
if (tainted_literal) {
CADICAL_assert (opts.ilb);
if (!val (tainted_literal)) {
tainted_literal = 0;
}
}
CADICAL_assert (num_assigned == trail.size ());
}
} // namespace CaDiCaL
ABC_NAMESPACE_IMPL_END

View File

@ -1,237 +0,0 @@
#include "global.h"
#include "internal.hpp"
ABC_NAMESPACE_IMPL_START
namespace CaDiCaL {
/*------------------------------------------------------------------------*/
// Provide eager backward subsumption for resolved clauses.
// The eliminator maintains a queue of clauses that are new and have to be
// checked to subsume or strengthen other (longer or same size) clauses.
void Eliminator::enqueue (Clause *c) {
if (!internal->opts.elimbackward)
return;
if (c->enqueued)
return;
LOG (c, "backward enqueue");
backward.push (c);
c->enqueued = true;
}
Clause *Eliminator::dequeue () {
if (backward.empty ())
return 0;
Clause *res = backward.front ();
backward.pop ();
CADICAL_assert (res->enqueued);
res->enqueued = false;
LOG (res, "backward dequeue");
return res;
}
Eliminator::~Eliminator () {
while (dequeue ())
;
}
/*------------------------------------------------------------------------*/
void Internal::elim_backward_clause (Eliminator &eliminator, Clause *c) {
CADICAL_assert (opts.elimbackward);
CADICAL_assert (!c->redundant);
if (c->garbage)
return;
LOG (c, "attempting backward subsumption and strengthening with");
size_t len = UINT_MAX;
unsigned size = 0;
int best = 0;
bool satisfied = false;
CADICAL_assert (mini_chain.empty ());
for (const auto &lit : *c) {
const signed char tmp = val (lit);
if (tmp > 0) {
satisfied = true;
break;
}
if (tmp < 0)
continue;
size_t l = occs (lit).size ();
LOG ("literal %d occurs %zd times", lit, l);
if (l < len)
best = lit, len = l;
mark (lit);
size++;
}
if (satisfied) {
LOG ("clause actually already satisfied");
elim_update_removed_clause (eliminator, c);
mark_garbage (c);
} else if (len > (size_t) opts.elimocclim) {
LOG ("skipping backward subsumption due to too many occurrences");
} else {
CADICAL_assert (len);
LOG ("literal %d has smallest number of occurrences %zd", best, len);
LOG ("marked %d literals in clause of size %d", size, c->size);
for (auto &d : occs (best)) {
if (d == c)
continue;
if (d->garbage)
continue;
if ((unsigned) d->size < size)
continue;
int negated = 0;
unsigned found = 0;
satisfied = false;
for (const auto &lit : *d) {
signed char tmp = val (lit);
if (tmp > 0) {
satisfied = true;
break;
}
if (tmp < 0)
continue;
tmp = marked (lit);
if (!tmp)
continue;
if (tmp < 0) {
if (negated) {
size = UINT_MAX;
break;
} else
negated = lit;
}
if (++found == size)
break;
}
if (satisfied) {
LOG (d, "found satisfied clause");
elim_update_removed_clause (eliminator, d);
mark_garbage (d);
} else if (found == size) {
if (!negated) {
LOG (d, "found subsumed clause");
elim_update_removed_clause (eliminator, d);
mark_garbage (d);
stats.subsumed++;
stats.elimbwsub++;
} else {
int unit = 0;
CADICAL_assert (minimize_chain.empty ());
CADICAL_assert (analyzed.empty ());
CADICAL_assert (lrat_chain.empty ());
// figure out wether we strengthen c or get a new unit
for (const auto &lit : *d) {
const signed char tmp = val (lit);
if (tmp < 0) {
if (!lrat)
continue;
Flags &f = flags (lit);
CADICAL_assert (!f.seen);
if (f.seen)
continue;
f.seen = true;
analyzed.push_back (lit);
continue;
}
if (tmp > 0) {
satisfied = true;
break;
}
if (lit == negated)
continue;
if (unit) {
unit = INT_MIN;
continue; // needed to guarantee d is not satsified
} else
unit = lit;
}
if (lrat && !satisfied) {
// if we found a unit we need to add all unit ids from
// {c\d}U{d\c} otherwise just the unit ids from {c\d}
for (const auto &lit : *c) {
const signed char tmp = val (lit);
CADICAL_assert (tmp <= 0);
if (tmp >= 0)
continue;
Flags &f = flags (lit);
if (f.seen && unit && unit == INT_MIN) {
f.seen = false;
continue;
} else if (!f.seen) {
f.seen = true;
analyzed.push_back (lit);
}
}
if (unit == INT_MIN) { // we do not need units from {d\c}
for (const auto &lit : *d) {
flags (lit).seen = false;
}
}
for (const auto &lit : analyzed) {
Flags &f = flags (lit);
if (!f.seen) {
f.seen = true;
continue;
}
int64_t id = unit_id (-lit);
lrat_chain.push_back (id);
}
clear_analyzed_literals ();
lrat_chain.push_back (d->id);
lrat_chain.push_back (c->id);
} else if (lrat)
clear_analyzed_literals ();
if (satisfied) {
CADICAL_assert (lrat_chain.empty ());
mark_garbage (d);
elim_update_removed_clause (eliminator, d);
} else if (unit && unit != INT_MIN) {
CADICAL_assert (unit);
LOG (d, "unit %d through hyper unary resolution with", unit);
assign_unit (unit);
elim_propagate (eliminator, unit);
lrat_chain.clear ();
break;
} else if (occs (negated).size () <= (size_t) opts.elimocclim) {
strengthen_clause (d, negated);
remove_occs (occs (negated), d);
elim_update_removed_lit (eliminator, negated);
stats.elimbwstr++;
CADICAL_assert (negated != best);
eliminator.enqueue (d);
}
lrat_chain.clear ();
}
}
}
}
mini_chain.clear ();
unmark (c);
}
/*------------------------------------------------------------------------*/
void Internal::elim_backward_clauses (Eliminator &eliminator) {
if (!opts.elimbackward) {
CADICAL_assert (eliminator.backward.empty ());
return;
}
START (backward);
LOG ("attempting backward subsumption and strengthening with %zd clauses",
eliminator.backward.size ());
Clause *c;
while (!unsat && (c = eliminator.dequeue ()))
elim_backward_clause (eliminator, c);
STOP (backward);
}
/*------------------------------------------------------------------------*/
} // namespace CaDiCaL
ABC_NAMESPACE_IMPL_END

View File

@ -1,28 +0,0 @@
#include "global.h"
#include "internal.hpp"
ABC_NAMESPACE_IMPL_START
namespace CaDiCaL {
/*------------------------------------------------------------------------*/
// Binary implication graph lists.
void Internal::init_bins () {
CADICAL_assert (big.empty ());
if (big.size () < 2 * vsize)
big.resize (2 * vsize, Bins ());
LOG ("initialized binary implication graph");
}
void Internal::reset_bins () {
CADICAL_assert (!big.empty ());
erase_vector (big);
LOG ("reset binary implication graph");
}
} // namespace CaDiCaL
ABC_NAMESPACE_IMPL_END

View File

@ -1,830 +0,0 @@
#include "global.h"
#include "internal.hpp"
ABC_NAMESPACE_IMPL_START
namespace CaDiCaL {
/*------------------------------------------------------------------------*/
// This implements an inprocessing version of blocked clause elimination and
// is assumed to be triggered just before bounded variable elimination. It
// has a separate 'block' flag while variable elimination uses 'elim'.
// Thus it only tries to block clauses on a literal which was removed in an
// irredundant clause in negated form before and has not been tried to use
// as blocking literal since then.
/*------------------------------------------------------------------------*/
inline bool block_more_occs_size::operator() (unsigned a, unsigned b) {
size_t s = internal->noccs (-internal->u2i (a));
size_t t = internal->noccs (-internal->u2i (b));
if (s > t)
return true;
if (s < t)
return false;
s = internal->noccs (internal->u2i (a));
t = internal->noccs (internal->u2i (b));
if (s > t)
return true;
if (s < t)
return false;
return a > b;
}
/*------------------------------------------------------------------------*/
// Determine whether 'c' is blocked on 'lit', by first marking all its
// literals and then checking all resolvents with negative clauses (with
// '-lit') are tautological. We use a move-to-front scheme for both the
// occurrence list of negative clauses (with '-lit') and then for literals
// within each such clause. The clause move-to-front scheme has the goal to
// find non-tautological clauses faster in the future, while the literal
// move-to-front scheme has the goal to faster find the matching literal,
// which makes the resolvent tautological (again in the future).
bool Internal::is_blocked_clause (Clause *c, int lit) {
LOG (c, "trying to block on %d", lit);
CADICAL_assert (c->size >= opts.blockminclslim);
CADICAL_assert (c->size <= opts.blockmaxclslim);
CADICAL_assert (active (lit));
CADICAL_assert (!val (lit));
CADICAL_assert (!c->garbage);
CADICAL_assert (!c->redundant);
CADICAL_assert (!level);
mark (c); // First mark all literals in 'c'.
Occs &os = occs (-lit);
LOG ("resolving against at most %zd clauses with %d", os.size (), -lit);
bool res = true; // Result is true if all resolvents tautological.
// Can not use 'auto' here since we update 'os' during traversal.
//
const auto end_of_os = os.end ();
auto i = os.begin ();
Clause *prev_d = 0; // Previous non-tautological clause.
for (; i != end_of_os; i++) {
// Move the first clause with non-tautological resolvent to the front of
// the occurrence list to improve finding it faster later.
//
Clause *d = *i;
CADICAL_assert (!d->garbage);
CADICAL_assert (!d->redundant);
CADICAL_assert (d->size <= opts.blockmaxclslim);
*i = prev_d; // Move previous non-tautological clause
prev_d = d; // backwards but remember clause at this position.
LOG (d, "resolving on %d against", lit);
stats.blockres++;
int prev_other = 0; // Previous non-tautological literal.
// No 'auto' since we update literals of 'd' during traversal.
//
const const_literal_iterator end_of_d = d->end ();
literal_iterator l;
for (l = d->begin (); l != end_of_d; l++) {
// Same move-to-front mechanism for literals within a clause. It
// moves the first negatively marked literal to the front to find it
// faster in the future.
//
const int other = *l;
*l = prev_other;
prev_other = other;
if (other == -lit)
continue;
CADICAL_assert (other != lit);
CADICAL_assert (active (other));
CADICAL_assert (!val (other));
if (marked (other) < 0) {
LOG ("found tautological literal %d", other);
d->literals[0] = other; // Move to front of 'd'.
break;
}
}
if (l == end_of_d) {
LOG ("no tautological literal found");
//
// Since we did not find a tautological literal we restore the old
// order of literals in the clause.
//
const const_literal_iterator begin_of_d = d->begin ();
while (l-- != begin_of_d) {
const int other = *l;
*l = prev_other;
prev_other = other;
}
res = false; // Now 'd' is a witness that 'c' is not blocked.
os[0] = d; // Move it to the front of the occurrence list.
break;
}
}
unmark (c); // ... all literals of the candidate clause.
// If all resolvents are tautological and thus the clause is blocked we
// restore the old order of clauses in the occurrence list of '-lit'.
//
if (res) {
CADICAL_assert (i == end_of_os);
const auto boc = os.begin ();
while (i != boc) {
Clause *d = *--i;
*i = prev_d;
prev_d = d;
}
}
return res;
}
/*------------------------------------------------------------------------*/
void Internal::block_schedule (Blocker &blocker) {
// Set skip flags for all literals in too large clauses.
//
for (const auto &c : clauses) {
if (c->garbage)
continue;
if (c->redundant)
continue;
if (c->size <= opts.blockmaxclslim)
continue;
for (const auto &lit : *c)
mark_skip (-lit);
}
// Connect all literal occurrences in irredundant clauses.
//
for (const auto &c : clauses) {
if (c->garbage)
continue;
if (c->redundant)
continue;
for (const auto &lit : *c) {
CADICAL_assert (active (lit));
CADICAL_assert (!val (lit));
occs (lit).push_back (c);
}
}
// We establish the invariant that 'noccs' gives the number of actual
// occurrences of 'lit' in non-garbage clauses, while 'occs' might still
// refer to garbage clauses, thus 'noccs (lit) <= occs (lit).size ()'. It
// is expensive to remove references to garbage clauses from 'occs' during
// blocked clause elimination, but decrementing 'noccs' is cheap.
for (auto lit : lits) {
if (!active (lit))
continue;
CADICAL_assert (!val (lit));
Occs &os = occs (lit);
noccs (lit) = os.size ();
}
// Now we fill the schedule (priority queue) of candidate literals to be
// tried as blocking literals. It is probably slightly faster to do this
// in one go after all occurrences have been determined, instead of
// filling the priority queue during pushing occurrences. Filling the
// schedule can not be fused with the previous loop (easily) since we
// first have to initialize 'noccs' for both 'lit' and '-lit'.
#ifndef CADICAL_QUIET
int skipped = 0;
#endif
for (auto idx : vars) {
if (!active (idx))
continue;
if (frozen (idx)) {
#ifndef CADICAL_QUIET
skipped += 2;
#endif
continue;
}
CADICAL_assert (!val (idx));
for (int sign = -1; sign <= 1; sign += 2) {
const int lit = sign * idx;
if (marked_skip (lit)) {
#ifndef CADICAL_QUIET
skipped++;
#endif
continue;
}
if (!marked_block (lit))
continue;
unmark_block (lit);
LOG ("scheduling %d with %" PRId64 " positive and %" PRId64
" negative occurrences",
lit, noccs (lit), noccs (-lit));
blocker.schedule.push_back (vlit (lit));
}
}
PHASE ("block", stats.blockings,
"scheduled %zd candidate literals %.2f%% (%d skipped %.2f%%)",
blocker.schedule.size (),
percent (blocker.schedule.size (), 2.0 * active ()), skipped,
percent (skipped, 2.0 * active ()));
}
/*------------------------------------------------------------------------*/
// A literal is pure if it only occurs positive. Then all clauses in which
// it occurs are blocked on it. This special case can be implemented faster
// than trying to block literals with at least one negative occurrence and
// is thus handled separately. It also allows to avoid pushing blocked
// clauses onto the extension stack.
void Internal::block_pure_literal (Blocker &blocker, int lit) {
if (frozen (lit))
return;
CADICAL_assert (active (lit));
Occs &pos = occs (lit);
Occs &nos = occs (-lit);
CADICAL_assert (!noccs (-lit));
#ifndef CADICAL_NDEBUG
for (const auto &c : nos)
CADICAL_assert (c->garbage);
#endif
stats.blockpurelits++;
LOG ("found pure literal %d", lit);
#ifdef LOGGING
int64_t pured = 0;
#endif
for (const auto &c : pos) {
if (c->garbage)
continue;
CADICAL_assert (!c->redundant);
LOG (c, "pure literal %d in", lit);
blocker.reschedule.push_back (c);
if (proof) {
proof->weaken_minus (c);
}
external->push_clause_on_extension_stack (c, lit);
stats.blockpured++;
mark_garbage (c);
#ifdef LOGGING
pured++;
#endif
}
erase_vector (pos);
erase_vector (nos);
mark_pure (lit);
stats.blockpured++;
LOG ("blocking %" PRId64 " clauses on pure literal %d", pured, lit);
}
/*------------------------------------------------------------------------*/
// If there is only one negative clause with '-lit' it is faster to mark it
// instead of marking all the positive clauses with 'lit' one after the
// other and then resolving against the negative clause.
void Internal::block_literal_with_one_negative_occ (Blocker &blocker,
int lit) {
CADICAL_assert (active (lit));
CADICAL_assert (!frozen (lit));
CADICAL_assert (noccs (lit) > 0);
CADICAL_assert (noccs (-lit) == 1);
Occs &nos = occs (-lit);
CADICAL_assert (nos.size () >= 1);
Clause *d = 0;
for (const auto &c : nos) {
if (c->garbage)
continue;
CADICAL_assert (!d);
d = c;
#ifndef CADICAL_NDEBUG
break;
#endif
}
CADICAL_assert (d);
nos.resize (1);
nos[0] = d;
if (d && d->size > opts.blockmaxclslim) {
LOG (d, "skipped common antecedent");
return;
}
CADICAL_assert (!d->garbage);
CADICAL_assert (!d->redundant);
CADICAL_assert (d->size <= opts.blockmaxclslim);
LOG (d, "common %d antecedent", lit);
mark (d);
int64_t blocked = 0;
#ifdef LOGGING
int64_t skipped = 0;
#endif
Occs &pos = occs (lit);
// Again no 'auto' since 'pos' is update during traversal.
//
const auto eop = pos.end ();
auto j = pos.begin (), i = j;
for (; i != eop; i++) {
Clause *c = *j++ = *i;
if (c->garbage) {
j--;
continue;
}
if (c->size > opts.blockmaxclslim) {
#ifdef LOGGING
skipped++;
#endif
continue;
}
if (c->size < opts.blockminclslim) {
#ifdef LOGGING
skipped++;
#endif
continue;
}
LOG (c, "trying to block on %d", lit);
// We use the same literal move-to-front strategy as in
// 'is_blocked_clause'. See there for more explanations.
int prev_other = 0; // Previous non-tautological literal.
// No 'auto' since literals of 'c' are updated during traversal.
//
const const_literal_iterator end_of_c = c->end ();
literal_iterator l;
for (l = c->begin (); l != end_of_c; l++) {
const int other = *l;
*l = prev_other;
prev_other = other;
if (other == lit)
continue;
CADICAL_assert (other != -lit);
CADICAL_assert (active (other));
CADICAL_assert (!val (other));
if (marked (other) < 0) {
LOG ("found tautological literal %d", other);
c->literals[0] = other; // Move to front of 'c'.
break;
}
}
if (l == end_of_c) {
LOG ("no tautological literal found");
// Restore old literal order in the clause because.
const const_literal_iterator begin_of_c = c->begin ();
while (l-- != begin_of_c) {
const int other = *l;
*l = prev_other;
prev_other = other;
}
continue; // ... with next candidate 'c' in 'pos'.
}
blocked++;
LOG (c, "blocked");
if (proof) {
proof->weaken_minus (c);
}
external->push_clause_on_extension_stack (c, lit);
blocker.reschedule.push_back (c);
mark_garbage (c);
j--;
}
if (j == pos.begin ())
erase_vector (pos);
else
pos.resize (j - pos.begin ());
stats.blocked += blocked;
LOG ("blocked %" PRId64 " clauses on %d (skipped %" PRId64 ")", blocked,
lit, skipped);
unmark (d);
}
/*------------------------------------------------------------------------*/
// Determine the set of candidate clauses with 'lit', which are checked to
// be blocked by 'lit'. Filter out too large and small clauses and which do
// not have any negated other literal in any of the clauses with '-lit'.
size_t Internal::block_candidates (Blocker &blocker, int lit) {
CADICAL_assert (blocker.candidates.empty ());
Occs &pos = occs (lit); // Positive occurrences of 'lit'.
Occs &nos = occs (-lit);
CADICAL_assert ((size_t) noccs (lit) <= pos.size ());
CADICAL_assert ((size_t) noccs (-lit) == nos.size ()); // Already flushed.
// Mark all literals in clauses with '-lit'. Note that 'mark2' uses
// separate bits for 'lit' and '-lit'.
//
for (const auto &c : nos)
mark2 (c);
const auto eop = pos.end ();
auto j = pos.begin (), i = j;
for (; i != eop; i++) {
Clause *c = *j++ = *i;
if (c->garbage) {
j--;
continue;
}
CADICAL_assert (!c->redundant);
if (c->size > opts.blockmaxclslim)
continue;
if (c->size < opts.blockminclslim)
continue;
const const_literal_iterator eoc = c->end ();
const_literal_iterator l;
for (l = c->begin (); l != eoc; l++) {
const int other = *l;
if (other == lit)
continue;
CADICAL_assert (other != -lit);
CADICAL_assert (active (other));
CADICAL_assert (!val (other));
if (marked2 (-other))
break;
}
if (l != eoc)
blocker.candidates.push_back (c);
}
if (j == pos.begin ())
erase_vector (pos);
else
pos.resize (j - pos.begin ());
CADICAL_assert (pos.size () == (size_t) noccs (lit)); // Now also flushed.
for (const auto &c : nos)
unmark (c);
return blocker.candidates.size ();
}
/*------------------------------------------------------------------------*/
// Try to find a clause with '-lit' which does not have any literal in
// clauses with 'lit'. If such a clause exists no candidate clause can be
// blocked on 'lit' since all candidates would produce a non-tautological
// resolvent with that clause.
Clause *Internal::block_impossible (Blocker &blocker, int lit) {
CADICAL_assert (noccs (-lit) > 1);
CADICAL_assert (blocker.candidates.size () > 1);
for (const auto &c : blocker.candidates)
mark2 (c);
Occs &nos = occs (-lit);
Clause *res = 0;
for (const auto &c : nos) {
CADICAL_assert (!c->garbage);
CADICAL_assert (!c->redundant);
CADICAL_assert (c->size <= opts.blockmaxclslim);
const const_literal_iterator eoc = c->end ();
const_literal_iterator l;
for (l = c->begin (); l != eoc; l++) {
const int other = *l;
if (other == -lit)
continue;
CADICAL_assert (other != lit);
CADICAL_assert (active (other));
CADICAL_assert (!val (other));
if (marked2 (-other))
break;
}
if (l == eoc)
res = c;
}
for (const auto &c : blocker.candidates)
unmark (c);
if (res) {
LOG (res, "common non-tautological resolvent producing");
blocker.candidates.clear ();
}
return res;
}
/*------------------------------------------------------------------------*/
// In the general case we have at least two negative occurrences.
void Internal::block_literal_with_at_least_two_negative_occs (
Blocker &blocker, int lit) {
CADICAL_assert (active (lit));
CADICAL_assert (!frozen (lit));
CADICAL_assert (noccs (lit) > 0);
CADICAL_assert (noccs (-lit) > 1);
Occs &nos = occs (-lit);
CADICAL_assert ((size_t) noccs (-lit) <= nos.size ());
int max_size = 0;
// Flush all garbage clauses in occurrence list 'nos' of '-lit' and
// determine the maximum size of negative clauses (with '-lit').
//
const auto eon = nos.end ();
auto j = nos.begin (), i = j;
for (; i != eon; i++) {
Clause *c = *j++ = *i;
if (c->garbage)
j--;
else if (c->size > max_size)
max_size = c->size;
}
if (j == nos.begin ())
erase_vector (nos);
else
nos.resize (j - nos.begin ());
CADICAL_assert (nos.size () == (size_t) noccs (-lit));
CADICAL_assert (nos.size () > 1);
// If the maximum size of a negative clause (with '-lit') exceeds the
// maximum clause size limit ignore this candidate literal.
//
if (max_size > opts.blockmaxclslim) {
LOG ("maximum size %d of clauses with %d exceeds clause size limit %d",
max_size, -lit, opts.blockmaxclslim);
return;
}
LOG ("maximum size %d of clauses with %d", max_size, -lit);
// We filter candidate clauses with positive occurrence of 'lit' in
// 'blocker.candidates' and return if no candidate clause remains.
// Candidates should be small enough and should have at least one literal
// which occurs negated in one of the clauses with '-lit'.
//
size_t candidates = block_candidates (blocker, lit);
if (!candidates) {
LOG ("no candidate clauses found");
return;
}
LOG ("found %zd candidate clauses", candidates);
// We further search for a clause with '-lit' that has no literal
// negated in any of the candidate clauses (except 'lit'). If such a
// clause exists, we know that none of the candidates is blocked.
//
if (candidates > 1 && block_impossible (blocker, lit)) {
LOG ("impossible to block any candidate clause on %d", lit);
CADICAL_assert (blocker.candidates.empty ());
return;
}
LOG ("trying to block %zd clauses out of %" PRId64 " with literal %d",
candidates, noccs (lit), lit);
int64_t blocked = 0;
// Go over all remaining candidates and try to block them on 'lit'.
//
for (const auto &c : blocker.candidates) {
CADICAL_assert (!c->garbage);
CADICAL_assert (!c->redundant);
if (!is_blocked_clause (c, lit))
continue;
blocked++;
LOG (c, "blocked");
if (proof) {
proof->weaken_minus (c);
}
external->push_clause_on_extension_stack (c, lit);
blocker.reschedule.push_back (c);
mark_garbage (c);
}
LOG ("blocked %" PRId64
" clauses on %d out of %zd candidates in %zd occurrences",
blocked, lit, blocker.candidates.size (), occs (lit).size ());
blocker.candidates.clear ();
stats.blocked += blocked;
if (blocked)
flush_occs (lit);
}
/*------------------------------------------------------------------------*/
// Reschedule literals in a clause (except 'lit') which was blocked.
void Internal::block_reschedule_clause (Blocker &blocker, int lit,
Clause *c) {
#ifdef CADICAL_NDEBUG
(void) lit;
#endif
CADICAL_assert (c->garbage);
for (const auto &other : *c) {
int64_t &n = noccs (other);
CADICAL_assert (n > 0);
n--;
LOG ("updating %d with %" PRId64 " positive and %" PRId64
" negative occurrences",
other, noccs (other), noccs (-other));
if (blocker.schedule.contains (vlit (-other)))
blocker.schedule.update (vlit (-other));
else if (active (other) && !frozen (other) && !marked_skip (-other)) {
LOG ("rescheduling to block clauses on %d", -other);
blocker.schedule.push_back (vlit (-other));
}
if (blocker.schedule.contains (vlit (other))) {
CADICAL_assert (other != lit);
blocker.schedule.update (vlit (other));
}
}
}
// Reschedule all literals in clauses blocked by 'lit' (except 'lit').
void Internal::block_reschedule (Blocker &blocker, int lit) {
while (!blocker.reschedule.empty ()) {
Clause *c = blocker.reschedule.back ();
blocker.reschedule.pop_back ();
block_reschedule_clause (blocker, lit, c);
}
}
/*------------------------------------------------------------------------*/
void Internal::block_literal (Blocker &blocker, int lit) {
CADICAL_assert (!marked_skip (lit));
if (!active (lit))
return; // Pure literal '-lit'.
if (frozen (lit))
return;
CADICAL_assert (!val (lit));
// If the maximum number of a negative clauses (with '-lit') exceeds the
// occurrence limit ignore this candidate literal.
//
if (noccs (-lit) > opts.blockocclim)
return;
LOG ("blocking literal candidate %d "
"with %" PRId64 " positive and %" PRId64 " negative occurrences",
lit, noccs (lit), noccs (-lit));
stats.blockcands++;
CADICAL_assert (blocker.reschedule.empty ());
CADICAL_assert (blocker.candidates.empty ());
if (!noccs (-lit))
block_pure_literal (blocker, lit);
else if (!noccs (lit)) {
// Rare situation, where the clause length limit was hit for 'lit' and
// '-lit' is skipped and then it becomes pure. Can be ignored. We also
// so it once happening for a 'elimboundmin=-1' and zero positive and
// one negative occurrence.
} else if (noccs (-lit) == 1)
block_literal_with_one_negative_occ (blocker, lit);
else
block_literal_with_at_least_two_negative_occs (blocker, lit);
// Done with blocked clause elimination on this literal and we do not
// have to try blocked clause elimination on it again until irredundant
// clauses with its negation are removed.
//
CADICAL_assert (!frozen (lit)); // just to be sure ...
unmark_block (lit);
}
/*------------------------------------------------------------------------*/
bool Internal::block () {
if (!opts.block)
return false;
if (unsat)
return false;
if (!stats.current.irredundant)
return false;
if (terminated_asynchronously ())
return false;
if (propagated < trail.size ()) {
LOG ("need to propagate %zd units first", trail.size () - propagated);
init_watches ();
connect_watches ();
if (!propagate ()) {
LOG ("propagating units results in empty clause");
learn_empty_clause ();
CADICAL_assert (unsat);
}
clear_watches ();
reset_watches ();
if (unsat)
return false;
}
START_SIMPLIFIER (block, BLOCK);
stats.blockings++;
LOG ("block-%" PRId64 "", stats.blockings);
CADICAL_assert (!level);
CADICAL_assert (!watching ());
CADICAL_assert (!occurring ());
mark_satisfied_clauses_as_garbage ();
init_occs (); // Occurrence lists for all literals.
init_noccs (); // Number of occurrences to avoid flushing garbage clauses.
Blocker blocker (this);
block_schedule (blocker);
int64_t blocked = stats.blocked;
int64_t resolutions = stats.blockres;
int64_t purelits = stats.blockpurelits;
int64_t pured = stats.blockpured;
while (!terminated_asynchronously () && !blocker.schedule.empty ()) {
int lit = u2i (blocker.schedule.front ());
blocker.schedule.pop_front ();
block_literal (blocker, lit);
block_reschedule (blocker, lit);
}
blocker.erase ();
reset_noccs ();
reset_occs ();
resolutions = stats.blockres - resolutions;
blocked = stats.blocked - blocked;
PHASE ("block", stats.blockings,
"blocked %" PRId64 " clauses in %" PRId64 " resolutions", blocked,
resolutions);
pured = stats.blockpured - pured;
purelits = stats.blockpurelits - purelits;
if (pured)
mark_redundant_clauses_with_eliminated_variables_as_garbage ();
if (purelits)
PHASE ("block", stats.blockings,
"found %" PRId64 " pure literals in %" PRId64 " clauses",
purelits, pured);
else
PHASE ("block", stats.blockings, "no pure literals found");
report ('b', !opts.reportall && !blocked);
STOP_SIMPLIFIER (block, BLOCK);
return blocked;
}
} // namespace CaDiCaL
ABC_NAMESPACE_IMPL_END

View File

@ -1,211 +0,0 @@
#include "global.h"
#include "cadical.hpp"
#include <cstdlib>
#include <cstring>
ABC_NAMESPACE_IMPL_START
namespace CaDiCaL {
struct Wrapper : Learner, Terminator {
Solver *solver;
struct {
void *state;
int (*function) (void *);
} terminator;
struct {
void *state;
int max_length;
int *begin_clause, *end_clause, *capacity_clause;
void (*function) (void *, int *);
} learner;
bool terminate () {
if (!terminator.function)
return false;
return terminator.function (terminator.state);
}
bool learning (int size) {
if (!learner.function)
return false;
return size <= learner.max_length;
}
void learn (int lit) {
if (learner.end_clause == learner.capacity_clause) {
size_t count = learner.end_clause - learner.begin_clause;
size_t size = count ? 2 * count : 1;
learner.begin_clause =
(int *) realloc (learner.begin_clause, size * sizeof (int));
learner.end_clause = learner.begin_clause + count;
learner.capacity_clause = learner.begin_clause + size;
}
*learner.end_clause++ = lit;
if (lit)
return;
learner.function (learner.state, learner.begin_clause);
learner.end_clause = learner.begin_clause;
}
Wrapper () : solver (new Solver ()) {
memset (&terminator, 0, sizeof terminator);
memset (&learner, 0, sizeof learner);
}
~Wrapper () {
terminator.function = 0;
if (learner.begin_clause)
free (learner.begin_clause);
delete solver;
}
};
} // namespace CaDiCaL
ABC_NAMESPACE_IMPL_END
#include "ccadical.h"
ABC_NAMESPACE_IMPL_START
using namespace CaDiCaL;
const char *ccadical_signature (void) { return Solver::signature (); }
CCaDiCaL *ccadical_init (void) { return (CCaDiCaL *) new Wrapper (); }
void ccadical_release (CCaDiCaL *wrapper) { delete (Wrapper *) wrapper; }
void ccadical_constrain (CCaDiCaL *wrapper, int lit) {
((Wrapper *) wrapper)->solver->constrain (lit);
}
int ccadical_constraint_failed (CCaDiCaL *wrapper) {
return ((Wrapper *) wrapper)->solver->constraint_failed ();
}
void ccadical_set_option (CCaDiCaL *wrapper, const char *name, int val) {
((Wrapper *) wrapper)->solver->set (name, val);
}
void ccadical_limit (CCaDiCaL *wrapper, const char *name, int val) {
((Wrapper *) wrapper)->solver->limit (name, val);
}
int ccadical_get_option (CCaDiCaL *wrapper, const char *name) {
return ((Wrapper *) wrapper)->solver->get (name);
}
void ccadical_add (CCaDiCaL *wrapper, int lit) {
((Wrapper *) wrapper)->solver->add (lit);
}
void ccadical_assume (CCaDiCaL *wrapper, int lit) {
((Wrapper *) wrapper)->solver->assume (lit);
}
int ccadical_solve (CCaDiCaL *wrapper) {
return ((Wrapper *) wrapper)->solver->solve ();
}
int ccadical_simplify (CCaDiCaL *wrapper) {
return ((Wrapper *) wrapper)->solver->simplify ();
}
int ccadical_val (CCaDiCaL *wrapper, int lit) {
return ((Wrapper *) wrapper)->solver->val (lit);
}
int ccadical_failed (CCaDiCaL *wrapper, int lit) {
return ((Wrapper *) wrapper)->solver->failed (lit);
}
void ccadical_print_statistics (CCaDiCaL *wrapper) {
((Wrapper *) wrapper)->solver->statistics ();
}
void ccadical_terminate (CCaDiCaL *wrapper) {
((Wrapper *) wrapper)->solver->terminate ();
}
int64_t ccadical_active (CCaDiCaL *wrapper) {
return ((Wrapper *) wrapper)->solver->active ();
}
int64_t ccadical_irredundant (CCaDiCaL *wrapper) {
return ((Wrapper *) wrapper)->solver->irredundant ();
}
int ccadical_fixed (CCaDiCaL *wrapper, int lit) {
return ((Wrapper *) wrapper)->solver->fixed (lit);
}
void ccadical_set_terminate (CCaDiCaL *ptr, void *state,
int (*terminate) (void *)) {
Wrapper *wrapper = (Wrapper *) ptr;
wrapper->terminator.state = state;
wrapper->terminator.function = terminate;
if (terminate)
wrapper->solver->connect_terminator (wrapper);
else
wrapper->solver->disconnect_terminator ();
}
void ccadical_set_learn (CCaDiCaL *ptr, void *state, int max_length,
void (*learn) (void *state, int *clause)) {
Wrapper *wrapper = (Wrapper *) ptr;
wrapper->learner.state = state;
wrapper->learner.max_length = max_length;
wrapper->learner.function = learn;
if (learn)
wrapper->solver->connect_learner (wrapper);
else
wrapper->solver->disconnect_learner ();
}
void ccadical_freeze (CCaDiCaL *ptr, int lit) {
((Wrapper *) ptr)->solver->freeze (lit);
}
void ccadical_melt (CCaDiCaL *ptr, int lit) {
((Wrapper *) ptr)->solver->melt (lit);
}
int ccadical_frozen (CCaDiCaL *ptr, int lit) {
return ((Wrapper *) ptr)->solver->frozen (lit);
}
int ccadical_trace_proof (CCaDiCaL *ptr, FILE *file, const char *path) {
return ((Wrapper *) ptr)->solver->trace_proof (file, path);
}
void ccadical_close_proof (CCaDiCaL *ptr) {
((Wrapper *) ptr)->solver->close_proof_trace ();
}
void ccadical_conclude (CCaDiCaL *ptr) {
((Wrapper *) ptr)->solver->conclude ();
}
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);
}
void ccadical_reserve(CCaDiCaL *ptr, int min_max_var) {
((Wrapper *) ptr)->solver->reserve(min_max_var);
}
int ccadical_is_inconsistent(CCaDiCaL *ptr) {
return ((Wrapper *) ptr)->solver->inconsistent ();
}
ABC_NAMESPACE_IMPL_END

View File

@ -1,654 +0,0 @@
#include "global.h"
#include "internal.hpp"
ABC_NAMESPACE_IMPL_START
namespace CaDiCaL {
/*------------------------------------------------------------------------*/
inline unsigned Checker::l2u (int lit) {
CADICAL_assert (lit);
CADICAL_assert (lit != INT_MIN);
unsigned res = 2 * (abs (lit) - 1);
if (lit < 0)
res++;
return res;
}
inline signed char Checker::val (int lit) {
CADICAL_assert (lit);
CADICAL_assert (lit != INT_MIN);
CADICAL_assert (abs (lit) < size_vars);
CADICAL_assert (vals[lit] == -vals[-lit]);
return vals[lit];
}
signed char &Checker::mark (int lit) {
const unsigned u = l2u (lit);
CADICAL_assert (u < marks.size ());
return marks[u];
}
inline CheckerWatcher &Checker::watcher (int lit) {
const unsigned u = l2u (lit);
CADICAL_assert (u < watchers.size ());
return watchers[u];
}
/*------------------------------------------------------------------------*/
CheckerClause *Checker::new_clause () {
const size_t size = simplified.size ();
CADICAL_assert (size > 1), CADICAL_assert (size <= UINT_MAX);
const size_t bytes = sizeof (CheckerClause) + (size - 2) * sizeof (int);
CheckerClause *res = (CheckerClause *) new char[bytes];
DeferDeleteArray<char> delete_res ((char *) res);
res->next = 0;
res->hash = last_hash;
res->size = size;
int *literals = res->literals, *p = literals;
for (const auto &lit : simplified)
*p++ = lit;
num_clauses++;
// First two literals are used as watches and should not be false.
//
for (unsigned i = 0; i < 2; i++) {
int lit = literals[i];
if (!val (lit))
continue;
for (unsigned j = i + 1; j < size; j++) {
int other = literals[j];
if (val (other))
continue;
swap (literals[i], literals[j]);
break;
}
}
CADICAL_assert (!val (literals[0]));
CADICAL_assert (!val (literals[1]));
watcher (literals[0]).push_back (CheckerWatch (literals[1], res));
watcher (literals[1]).push_back (CheckerWatch (literals[0], res));
delete_res.release ();
return res;
}
void Checker::delete_clause (CheckerClause *c) {
if (c->size) {
CADICAL_assert (c->size > 1);
CADICAL_assert (num_clauses);
num_clauses--;
} else {
CADICAL_assert (num_garbage);
num_garbage--;
}
delete[] (char *) c;
}
void Checker::enlarge_clauses () {
CADICAL_assert (num_clauses == size_clauses);
const uint64_t new_size_clauses = size_clauses ? 2 * size_clauses : 1;
LOG ("CHECKER enlarging clauses of checker from %" PRIu64 " to %" PRIu64,
(uint64_t) size_clauses, (uint64_t) new_size_clauses);
CheckerClause **new_clauses;
new_clauses = new CheckerClause *[new_size_clauses];
clear_n (new_clauses, new_size_clauses);
for (uint64_t i = 0; i < size_clauses; i++) {
for (CheckerClause *c = clauses[i], *next; c; c = next) {
next = c->next;
const uint64_t h = reduce_hash (c->hash, new_size_clauses);
c->next = new_clauses[h];
new_clauses[h] = c;
}
}
delete[] clauses;
clauses = new_clauses;
size_clauses = new_size_clauses;
}
bool Checker::clause_satisfied (CheckerClause *c) {
for (unsigned i = 0; i < c->size; i++)
if (val (c->literals[i]) > 0)
return true;
return false;
}
// The main reason why we have an explicit garbage collection phase is that
// removing clauses from watcher lists eagerly might lead to an accumulated
// quadratic algorithm. Thus we delay removing garbage clauses from watcher
// lists until garbage collection (even though we remove garbage clauses on
// the fly during propagation too). We also remove satisfied clauses.
//
void Checker::collect_garbage_clauses () {
stats.collections++;
for (size_t i = 0; i < size_clauses; i++) {
CheckerClause **p = clauses + i, *c;
while ((c = *p)) {
if (clause_satisfied (c)) {
c->size = 0; // mark as garbage
*p = c->next;
c->next = garbage;
garbage = c;
num_garbage++;
CADICAL_assert (num_clauses);
num_clauses--;
} else
p = &c->next;
}
}
LOG ("CHECKER collecting %" PRIu64 " garbage clauses %.0f%%", num_garbage,
percent (num_garbage, num_clauses));
for (int lit = -size_vars + 1; lit < size_vars; lit++) {
if (!lit)
continue;
CheckerWatcher &ws = watcher (lit);
const auto end = ws.end ();
auto j = ws.begin (), i = j;
for (; i != end; i++) {
CheckerWatch &w = *i;
if (w.clause->size)
*j++ = w;
}
if (j == ws.end ())
continue;
if (j == ws.begin ())
erase_vector (ws);
else
ws.resize (j - ws.begin ());
}
for (CheckerClause *c = garbage, *next; c; c = next)
next = c->next, delete_clause (c);
CADICAL_assert (!num_garbage);
garbage = 0;
}
/*------------------------------------------------------------------------*/
Checker::Checker (Internal *i)
: internal (i), size_vars (0), vals (0), inconsistent (false),
num_clauses (0), num_garbage (0), size_clauses (0), clauses (0),
garbage (0), next_to_propagate (0), last_hash (0) {
// Initialize random number table for hash function.
//
Random random (42);
for (unsigned n = 0; n < num_nonces; n++) {
uint64_t nonce = random.next ();
if (!(nonce & 1))
nonce++;
CADICAL_assert (nonce), CADICAL_assert (nonce & 1);
nonces[n] = nonce;
}
memset (&stats, 0, sizeof (stats)); // Initialize statistics.
}
void Checker::connect_internal (Internal *i) {
internal = i;
LOG ("CHECKER connected to internal");
}
Checker::~Checker () {
LOG ("CHECKER delete");
vals -= size_vars;
delete[] vals;
for (size_t i = 0; i < size_clauses; i++)
for (CheckerClause *c = clauses[i], *next; c; c = next)
next = c->next, delete_clause (c);
for (CheckerClause *c = garbage, *next; c; c = next)
next = c->next, delete_clause (c);
delete[] clauses;
}
/*------------------------------------------------------------------------*/
// The simplicity for accessing 'vals' and 'watchers' directly through a
// signed integer literal, comes with the price of slightly more complex
// code in deleting and enlarging the checker data structures.
void Checker::enlarge_vars (int64_t idx) {
CADICAL_assert (0 < idx), CADICAL_assert (idx <= INT_MAX);
int64_t new_size_vars = size_vars ? 2 * size_vars : 2;
while (idx >= new_size_vars)
new_size_vars *= 2;
LOG ("CHECKER enlarging variables of checker from %" PRId64 " to %" PRId64
"",
size_vars, new_size_vars);
signed char *new_vals;
new_vals = new signed char[2 * new_size_vars];
clear_n (new_vals, 2 * new_size_vars);
new_vals += new_size_vars;
if (size_vars) // To make sanitizer happy (without '-O').
memcpy ((void *) (new_vals - size_vars), (void *) (vals - size_vars),
2 * size_vars);
vals -= size_vars;
delete[] vals;
vals = new_vals;
size_vars = new_size_vars;
watchers.resize (2 * new_size_vars);
marks.resize (2 * new_size_vars);
CADICAL_assert (idx < new_size_vars);
}
inline void Checker::import_literal (int lit) {
CADICAL_assert (lit);
CADICAL_assert (lit != INT_MIN);
int idx = abs (lit);
if (idx >= size_vars)
enlarge_vars (idx);
simplified.push_back (lit);
unsimplified.push_back (lit);
}
void Checker::import_clause (const vector<int> &c) {
for (const auto &lit : c)
import_literal (lit);
}
struct lit_smaller {
bool operator() (int a, int b) const {
int c = abs (a), d = abs (b);
if (c < d)
return true;
if (c > d)
return false;
return a < b;
}
};
bool Checker::tautological () {
sort (simplified.begin (), simplified.end (), lit_smaller ());
const auto end = simplified.end ();
auto j = simplified.begin ();
int prev = 0;
for (auto i = j; i != end; i++) {
int lit = *i;
if (lit == prev)
continue; // duplicated literal
if (lit == -prev)
return true; // tautological clause
const signed char tmp = val (lit);
if (tmp > 0)
return true; // satisfied literal and clause
*j++ = prev = lit;
}
simplified.resize (j - simplified.begin ());
return false;
}
/*------------------------------------------------------------------------*/
uint64_t Checker::reduce_hash (uint64_t hash, uint64_t size) {
CADICAL_assert (size > 0);
unsigned shift = 32;
uint64_t res = hash;
while ((((uint64_t) 1) << shift) > size) {
res ^= res >> shift;
shift >>= 1;
}
res &= size - 1;
CADICAL_assert (res < size);
return res;
}
uint64_t Checker::compute_hash () {
unsigned j = last_id % num_nonces;
uint64_t tmp = nonces[j] * last_id;
return last_hash = tmp;
}
CheckerClause **Checker::find () {
stats.searches++;
CheckerClause **res, *c;
const uint64_t hash = compute_hash ();
const unsigned size = simplified.size ();
const uint64_t h = reduce_hash (hash, size_clauses);
for (const auto &lit : simplified)
mark (lit) = true;
for (res = clauses + h; (c = *res); res = &c->next) {
if (c->hash == hash && c->size == size) {
bool found = true;
const int *literals = c->literals;
for (unsigned i = 0; found && i != size; i++)
found = mark (literals[i]);
if (found)
break;
}
stats.collisions++;
}
for (const auto &lit : simplified)
mark (lit) = false;
return res;
}
void Checker::insert () {
stats.insertions++;
if (num_clauses == size_clauses)
enlarge_clauses ();
const uint64_t h = reduce_hash (compute_hash (), size_clauses);
CheckerClause *c = new_clause ();
c->next = clauses[h];
clauses[h] = c;
}
/*------------------------------------------------------------------------*/
inline void Checker::assign (int lit) {
CADICAL_assert (!val (lit));
vals[lit] = 1;
vals[-lit] = -1;
trail.push_back (lit);
}
inline void Checker::assume (int lit) {
signed char tmp = val (lit);
if (tmp > 0)
return;
CADICAL_assert (!tmp);
stats.assumptions++;
assign (lit);
}
void Checker::backtrack (unsigned previously_propagated) {
CADICAL_assert (previously_propagated <= trail.size ());
while (trail.size () > previously_propagated) {
int lit = trail.back ();
CADICAL_assert (val (lit) > 0);
CADICAL_assert (val (-lit) < 0);
vals[lit] = vals[-lit] = 0;
trail.pop_back ();
}
trail.resize (previously_propagated);
next_to_propagate = previously_propagated;
CADICAL_assert (trail.size () == next_to_propagate);
}
/*------------------------------------------------------------------------*/
// This is a standard propagation routine without using blocking literals
// nor without saving the last replacement position.
bool Checker::propagate () {
bool res = true;
while (res && next_to_propagate < trail.size ()) {
int lit = trail[next_to_propagate++];
stats.propagations++;
CADICAL_assert (val (lit) > 0);
CADICAL_assert (abs (lit) < size_vars);
CheckerWatcher &ws = watcher (-lit);
const auto end = ws.end ();
auto j = ws.begin (), i = j;
for (; res && i != end; i++) {
CheckerWatch &w = *j++ = *i;
const int blit = w.blit;
CADICAL_assert (blit != -lit);
const signed char blit_val = val (blit);
if (blit_val > 0)
continue;
const unsigned size = w.size;
if (size == 2) { // not precise since
if (blit_val < 0)
res = false; // clause might be garbage
else
assign (w.blit); // but still sound
} else {
CADICAL_assert (size > 2);
CheckerClause *c = w.clause;
if (!c->size) {
j--;
continue;
} // skip garbage clauses
CADICAL_assert (size == c->size);
int *lits = c->literals;
int other = lits[0] ^ lits[1] ^ (-lit);
CADICAL_assert (other != -lit);
signed char other_val = val (other);
if (other_val > 0) {
j[-1].blit = other;
continue;
}
lits[0] = other, lits[1] = -lit;
unsigned k;
int replacement = 0;
signed char replacement_val = -1;
for (k = 2; k < size; k++)
if ((replacement_val = val (replacement = lits[k])) >= 0)
break;
if (replacement_val >= 0) {
watcher (replacement).push_back (CheckerWatch (-lit, c));
swap (lits[1], lits[k]);
j--;
} else if (!other_val)
assign (other);
else
res = false;
}
}
while (i != end)
*j++ = *i++;
ws.resize (j - ws.begin ());
}
return res;
}
bool Checker::check () {
stats.checks++;
if (inconsistent)
return true;
unsigned previously_propagated = next_to_propagate;
for (const auto &lit : simplified)
assume (-lit);
bool res = !propagate ();
backtrack (previously_propagated);
return res;
}
bool Checker::check_blocked () {
for (const auto &lit : unsimplified) {
mark (-lit) = true;
}
vector<int> not_blocked;
for (size_t i = 0; i < size_clauses; i++) {
for (CheckerClause *c = clauses[i], *next; c; c = next) {
next = c->next;
unsigned count = 0;
int first;
for (int *i = c->literals; i < c->literals + c->size; i++) {
const int lit = *i;
if (val (lit) > 0) {
LOG (c->literals, c->size, "satisfied clause");
count = 2;
break;
}
if (mark (lit)) {
count++;
LOG (c->literals, c->size, "clause");
first = lit;
}
}
if (count == 1)
not_blocked.push_back (first);
}
}
for (const auto &lit : not_blocked) {
mark (lit) = false;
}
bool blocked = false;
for (const auto &lit : unsimplified) {
if (mark (-lit))
blocked = true;
mark (-lit) = false;
}
return blocked;
}
/*------------------------------------------------------------------------*/
void Checker::add_clause (const char *type) {
#ifndef LOGGING
(void) type;
#endif
// If there are enough garbage clauses collect them first.
if (num_garbage > 0.5 * max ((size_t) size_clauses, (size_t) size_vars))
collect_garbage_clauses ();
int unit = 0;
for (const auto &lit : simplified) {
const signed char tmp = val (lit);
if (tmp < 0)
continue;
CADICAL_assert (!tmp);
if (unit) {
unit = INT_MIN;
break;
}
unit = lit;
}
if (simplified.empty ()) {
LOG ("CHECKER added empty %s clause", type);
inconsistent = true;
}
if (!unit) {
LOG ("CHECKER added and checked falsified %s clause", type);
inconsistent = true;
} else if (unit != INT_MIN) {
LOG ("CHECKER added and checked %s unit clause %d", type, unit);
assign (unit);
stats.units++;
if (!propagate ()) {
LOG ("CHECKER inconsistent after propagating %s unit", type);
inconsistent = true;
}
} else
insert ();
}
void Checker::add_original_clause (int64_t id, bool, const vector<int> &c,
bool) {
if (inconsistent)
return;
START (checking);
LOG (c, "CHECKER addition of original clause");
stats.added++;
stats.original++;
import_clause (c);
last_id = id;
if (tautological ())
LOG ("CHECKER ignoring satisfied original clause");
else
add_clause ("original");
simplified.clear ();
unsimplified.clear ();
STOP (checking);
}
void Checker::add_derived_clause (int64_t id, bool, const vector<int> &c,
const vector<int64_t> &) {
if (inconsistent)
return;
START (checking);
LOG (c, "CHECKER addition of derived clause");
stats.added++;
stats.derived++;
import_clause (c);
last_id = id;
if (tautological ())
LOG ("CHECKER ignoring satisfied derived clause");
else if (!check () && !check_blocked ()) { // needed for ER proof support
fatal_message_start ();
fputs ("failed to check derived clause:\n", stderr);
for (const auto &lit : unsimplified)
fprintf (stderr, "%d ", lit);
fputc ('0', stderr);
fatal_message_end ();
} else
add_clause ("derived");
simplified.clear ();
unsimplified.clear ();
STOP (checking);
}
/*------------------------------------------------------------------------*/
void Checker::delete_clause (int64_t id, bool, const vector<int> &c) {
if (inconsistent)
return;
START (checking);
LOG (c, "CHECKER checking deletion of clause");
stats.deleted++;
simplified.clear (); // Can be non-empty if clause allocation fails.
unsimplified.clear (); // Can be non-empty if clause allocation fails.
import_clause (c);
last_id = id;
if (!tautological ()) {
CheckerClause **p = find (), *d = *p;
if (d) {
CADICAL_assert (d->size > 1);
// Remove from hash table, mark as garbage, connect to garbage list.
num_garbage++;
CADICAL_assert (num_clauses);
num_clauses--;
*p = d->next;
d->next = garbage;
garbage = d;
d->size = 0;
} else {
fatal_message_start ();
fputs ("deleted clause not in proof:\n", stderr);
for (const auto &lit : unsimplified)
fprintf (stderr, "%d ", lit);
fputc ('0', stderr);
fatal_message_end ();
}
}
simplified.clear ();
unsimplified.clear ();
STOP (checking);
}
void Checker::add_assumption_clause (int64_t id, const vector<int> &c,
const vector<int64_t> &chain) {
add_derived_clause (id, true, c, chain);
delete_clause (id, true, c);
}
/*------------------------------------------------------------------------*/
void Checker::dump () {
int max_var = 0;
for (uint64_t i = 0; i < size_clauses; i++)
for (CheckerClause *c = clauses[i]; c; c = c->next)
for (unsigned i = 0; i < c->size; i++)
if (abs (c->literals[i]) > max_var)
max_var = abs (c->literals[i]);
printf ("p cnf %d %" PRIu64 "\n", max_var, num_clauses);
for (uint64_t i = 0; i < size_clauses; i++)
for (CheckerClause *c = clauses[i]; c; c = c->next) {
for (unsigned i = 0; i < c->size; i++)
printf ("%d ", c->literals[i]);
printf ("0\n");
}
}
} // namespace CaDiCaL
ABC_NAMESPACE_IMPL_END

View File

@ -1,649 +0,0 @@
#include "global.h"
#include "internal.hpp"
ABC_NAMESPACE_IMPL_START
namespace CaDiCaL {
/*------------------------------------------------------------------------*/
// Signed marking or unmarking of a clause or the global 'clause'.
void Internal::mark (Clause *c) {
for (const auto &lit : *c)
mark (lit);
}
void Internal::mark2 (Clause *c) {
for (const auto &lit : *c)
mark2 (lit);
}
void Internal::unmark (Clause *c) {
for (const auto &lit : *c)
unmark (lit);
}
void Internal::mark_clause () {
for (const auto &lit : clause)
mark (lit);
}
void Internal::unmark_clause () {
for (const auto &lit : clause)
unmark (lit);
}
/*------------------------------------------------------------------------*/
// Mark the variables of an irredundant clause to 'have been removed', which
// will trigger these variables to be considered again in the next bounded
// variable elimination phase. This is called from 'mark_garbage' below.
// Note that 'mark_removed (int lit)' will also mark the blocking flag of
// '-lit' to trigger reconsidering blocking clauses on '-lit'.
void Internal::mark_removed (Clause *c, int except) {
LOG (c, "marking removed");
CADICAL_assert (!c->redundant);
for (const auto &lit : *c)
if (lit != except)
mark_removed (lit);
}
// Mark the variables of a (redundant or irredundant) clause to 'have been
// added', which triggers clauses with such a variables, to be considered
// both as a subsumed or subsuming clause in the next subsumption phase.
// This function is called from 'new_clause' below as well as in situations
// where a clause is shrunken (and thus needs to be at least considered
// again to subsume a larger clause). We also use this to tell
// 'ternary' preprocessing reconsider clauses on an added literal as well as
// trying to block clauses on it.
inline void Internal::mark_added (int lit, int size, bool redundant) {
mark_subsume (lit);
if (size == 3)
mark_ternary (lit);
if (!redundant)
mark_block (lit);
if (!redundant || size == 2)
mark_factor (lit);
}
void Internal::mark_added (Clause *c) {
LOG (c, "marking added");
CADICAL_assert (likely_to_be_kept_clause (c));
for (const auto &lit : *c)
mark_added (lit, c->size, c->redundant);
}
/*------------------------------------------------------------------------*/
Clause *Internal::new_clause (bool red, int glue) {
CADICAL_assert (clause.size () <= (size_t) INT_MAX);
const int size = (int) clause.size ();
CADICAL_assert (size >= 2);
if (glue > size)
glue = size;
size_t bytes = Clause::bytes (size);
Clause *c = (Clause *) new char[bytes];
DeferDeleteArray<char> clause_delete ((char *) c);
c->id = ++clause_id;
c->conditioned = false;
c->covered = false;
c->enqueued = false;
c->frozen = false;
c->garbage = false;
c->gate = false;
c->hyper = false;
c->instantiated = false;
c->moved = false;
c->reason = false;
c->redundant = red;
c->transred = false;
c->subsume = false;
c->swept = false;
c->flushed = false;
c->vivified = false;
c->vivify = false;
c->used = 0;
c->glue = glue;
c->size = size;
c->pos = 2;
for (int i = 0; i < size; i++)
c->literals[i] = clause[i];
// Just checking that we did not mess up our sophisticated memory layout.
// This might be compiler dependent though. Crucial for correctness.
//
CADICAL_assert (c->bytes () == bytes);
stats.current.total++;
stats.added.total++;
if (red) {
stats.current.redundant++;
stats.added.redundant++;
} else {
stats.irrlits += size;
stats.current.irredundant++;
stats.added.irredundant++;
}
clauses.push_back (c);
clause_delete.release ();
LOG (c, "new pointer %p", (void *) c);
if (likely_to_be_kept_clause (c))
mark_added (c);
return c;
}
/*------------------------------------------------------------------------*/
void Internal::promote_clause (Clause *c, int new_glue) {
CADICAL_assert (c->redundant);
const int tier1limit = tier1[false];
const int tier2limit = max (tier1limit, tier2[false]);
if (!c->redundant)
return;
if (c->hyper)
return;
int old_glue = c->glue;
if (new_glue >= old_glue)
return;
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++;
} else if (old_glue <= tier2limit)
LOG (c, "keeping with new glue %d in tier2", new_glue);
else
LOG (c, "keeping with new glue %d in tier3", new_glue);
stats.improvedglue++;
c->glue = new_glue;
}
/*------------------------------------------------------------------------*/
void Internal::promote_clause_glue_only (Clause *c, int new_glue) {
CADICAL_assert (c->redundant);
if (c->hyper)
return;
int old_glue = c->glue;
const int tier1limit = tier1[false];
const int tier2limit = max (tier1limit, tier2[false]);
if (new_glue >= old_glue)
return;
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++;
} else if (old_glue <= tier2limit)
LOG (c, "keeping with new glue %d in tier2", new_glue);
else
LOG (c, "keeping with new glue %d in tier3", new_glue);
stats.improvedglue++;
c->glue = new_glue;
}
/*------------------------------------------------------------------------*/
// Shrinking a clause, e.g., removing one or more literals, requires to fix
// the 'pos' field, if it exists and points after the new last literal. We
// also have adjust the global statistics counter of irredundant literals
// for irredundant clauses, and also adjust the glue value of redundant
// clauses if the size becomes smaller than the glue. Also mark the
// literals in the resulting clause as 'added'. The result is the number of
// (aligned) removed bytes, resulting from shrinking the clause.
//
size_t Internal::shrink_clause (Clause *c, int new_size) {
if (opts.check && is_external_forgettable (c->id))
mark_garbage_external_forgettable (c->id);
CADICAL_assert (new_size >= 2);
int old_size = c->size;
CADICAL_assert (new_size < old_size);
#ifndef CADICAL_NDEBUG
for (int i = c->size; i < new_size; i++)
c->literals[i] = 0;
#endif
if (c->pos >= new_size)
c->pos = 2;
size_t old_bytes = c->bytes ();
c->size = new_size;
size_t new_bytes = c->bytes ();
size_t res = old_bytes - new_bytes;
if (c->redundant)
promote_clause_glue_only (c, min (c->size - 1, c->glue));
else {
int delta_size = old_size - new_size;
CADICAL_assert (stats.irrlits >= delta_size);
stats.irrlits -= delta_size;
}
if (likely_to_be_kept_clause (c))
mark_added (c);
return res;
}
// This is the 'raw' deallocation of a clause. If the clause is in the
// arena nothing happens. If the clause is not in the arena its memory is
// reclaimed immediately.
void Internal::deallocate_clause (Clause *c) {
char *p = (char *) c;
if (arena.contains (p))
return;
LOG (c, "deallocate pointer %p", (void *) c);
delete[] p;
}
void Internal::delete_clause (Clause *c) {
LOG (c, "delete pointer %p", (void *) c);
size_t bytes = c->bytes ();
stats.collected += bytes;
if (c->garbage) {
CADICAL_assert (stats.garbage.bytes >= (int64_t) bytes);
stats.garbage.bytes -= bytes;
CADICAL_assert (stats.garbage.clauses > 0);
stats.garbage.clauses--;
CADICAL_assert (stats.garbage.literals >= c->size);
stats.garbage.literals -= c->size;
// See the discussion in 'propagate' on avoiding to eagerly trace binary
// clauses as deleted (produce 'd ...' lines) as soon they are marked
// garbage. We avoid this and only trace them as deleted when they are
// actually deleted here. This allows the solver to propagate binary
// garbage clauses without producing incorrect 'd' lines. The effect
// from the proof perspective is that the deletion of these binary
// clauses occurs later in the proof file.
//
if (proof && c->size == 2 && !c->flushed) {
proof->delete_clause (c);
}
}
deallocate_clause (c);
}
// We want to eagerly update statistics as soon clauses are marked garbage.
// Otherwise 'report' for instance gives wrong numbers after 'subsume'
// before the next 'reduce'. Thus we factored out marking and accounting
// for garbage clauses.
//
// Eagerly deleting clauses instead is problematic, since references to
// these clauses need to be flushed, which is too costly to do eagerly.
//
// We also update garbage statistics at this point. This helps to
// determine whether the garbage collector should be called during for
// instance bounded variable elimination, which usually generates lots of
// garbage clauses.
//
// In order not to miss any update to these clause statistics we call
// 'check_clause_stats' after garbage collection in debugging mode.
//
void Internal::mark_garbage (Clause *c) {
CADICAL_assert (!c->garbage);
// Delay tracing deletion of binary clauses. See the discussion above in
// 'delete_clause' and also in 'propagate'.
//
if (proof && (c->size != 2 || !watching ())) {
c->flushed = true;
proof->delete_clause (c);
}
// Because of the internal model checking, external forgettable clauses
// must be marked as removed already upon mark_garbage, can not wait until
// actual deletion.
if (opts.check && is_external_forgettable (c->id))
mark_garbage_external_forgettable (c->id);
CADICAL_assert (stats.current.total > 0);
stats.current.total--;
size_t bytes = c->bytes ();
if (c->redundant) {
CADICAL_assert (stats.current.redundant > 0);
stats.current.redundant--;
} else {
CADICAL_assert (stats.current.irredundant > 0);
stats.current.irredundant--;
CADICAL_assert (stats.irrlits >= c->size);
stats.irrlits -= c->size;
mark_removed (c);
}
stats.garbage.bytes += bytes;
stats.garbage.clauses++;
stats.garbage.literals += c->size;
c->garbage = true;
c->used = 0;
LOG (c, "marked garbage pointer %p", (void *) c);
}
/*------------------------------------------------------------------------*/
// Almost the same function as 'search_assign' except that we do not pretend
// to learn a new unit clause (which was confusing in log files).
void Internal::assign_original_unit (int64_t id, int lit) {
CADICAL_assert (!level || opts.chrono);
CADICAL_assert (!unsat);
const int idx = vidx (lit);
CADICAL_assert (!vals[idx]);
CADICAL_assert (!flags (idx).eliminated ());
Var &v = var (idx);
v.level = 0;
v.trail = (int) trail.size ();
v.reason = 0;
const signed char tmp = sign (lit);
set_val (idx, tmp);
trail.push_back (lit);
num_assigned++;
const unsigned uidx = vlit (lit);
if (lrat || frat)
unit_clauses (uidx) = id;
LOG ("original unit assign %d", lit);
CADICAL_assert (num_assigned == trail.size () || level);
mark_fixed (lit);
if (level)
return;
if (propagate ())
return;
CADICAL_assert (conflict);
LOG ("propagation of original unit results in conflict");
learn_empty_clause ();
}
// New clause added through the API, e.g., while parsing a DIMACS file.
// Also used by external_propagate in various different modes.
// clause, original, lrat_chain and external->eclause are set.
// from_propagator and force_no_backtrack change the behaviour.
// sometimes the pointer to the new clause is needed, therefore it is
// made sure that newest_clause points to the new clause upon return.
//
// TODO: Find another name for 'tainted' in the context of ilb, tainted
// is reconstruction related already and they should not mix.
void Internal::add_new_original_clause (int64_t id) {
if (!from_propagator && level && !opts.ilb) {
backtrack ();
} else if (tainted_literal) {
CADICAL_assert (val (tainted_literal));
int new_level = var (tainted_literal).level - 1;
CADICAL_assert (new_level >= 0);
backtrack (new_level);
}
CADICAL_assert (!tainted_literal);
LOG (original, "original clause");
CADICAL_assert (clause.empty ());
bool skip = false;
unordered_set<int> learned_levels;
size_t unassigned = 0;
newest_clause = 0;
if (unsat) {
LOG ("skipping clause since formula is already inconsistent");
skip = true;
} else {
CADICAL_assert (clause.empty ());
for (const auto &lit : original) {
int tmp = marked (lit);
if (tmp > 0) {
LOG ("removing duplicated literal %d", lit);
} else if (tmp < 0) {
LOG ("tautological since both %d and %d occur", -lit, lit);
skip = true;
} else {
mark (lit);
tmp = fixed (lit);
if (tmp < 0) {
LOG ("removing falsified literal %d", lit);
if (lrat) {
int elit = externalize (lit);
unsigned eidx = (elit > 0) + 2u * (unsigned) abs (elit);
if (!external->ext_units[eidx]) {
int64_t uid = unit_id (-lit);
lrat_chain.push_back (uid);
}
}
} else if (tmp > 0) {
LOG ("satisfied since literal %d true", lit);
skip = true;
} else {
clause.push_back (lit);
CADICAL_assert (flags (lit).status != Flags::UNUSED);
tmp = val (lit);
if (tmp)
learned_levels.insert (var (lit).level);
else
unassigned++;
}
}
}
for (const auto &lit : original)
unmark (lit);
}
if (skip) {
if (from_propagator) {
stats.ext_prop.elearn_conf++;
// In case it was a skipped external forgettable, we need to mark it
// immediately as removed
if (opts.check && is_external_forgettable (id))
mark_garbage_external_forgettable (id);
}
if (proof) {
proof->delete_external_original_clause (id, false, external->eclause);
}
} else {
int64_t new_id = id;
const size_t size = clause.size ();
if (original.size () > size) {
new_id = ++clause_id;
if (proof) {
if (lrat)
lrat_chain.push_back (id);
proof->add_derived_clause (new_id, false, clause, lrat_chain);
proof->delete_external_original_clause (id, false,
external->eclause);
}
external->check_learned_clause ();
if (from_propagator) {
// The original form of the added clause is immediately forgotten
// TODO: shall we save and check the simplified form? (one with
// new_id)
if (opts.check && is_external_forgettable (id))
mark_garbage_external_forgettable (id);
}
}
external->eclause.clear ();
lrat_chain.clear ();
if (!size) {
if (from_propagator)
stats.ext_prop.elearn_conf++;
CADICAL_assert (!unsat);
if (!original.size ())
VERBOSE (1, "found empty original clause");
else
VERBOSE (1, "found falsified original clause");
unsat = true;
conflict_id = new_id;
marked_failed = true;
conclusion.push_back (new_id);
} else if (size == 1) {
if (force_no_backtrack) {
CADICAL_assert (level);
const int idx = vidx (clause[0]);
CADICAL_assert (val (clause[0]) >= 0);
CADICAL_assert (!flags (idx).eliminated ());
Var &v = var (idx);
CADICAL_assert (val (clause[0]));
v.level = 0;
v.reason = 0;
const unsigned uidx = vlit (clause[0]);
if (lrat || frat)
unit_clauses (uidx) = new_id;
mark_fixed (clause[0]);
} else {
const int lit = clause[0];
CADICAL_assert (!val (lit) || var (lit).level);
if (val (lit) < 0)
backtrack (var (lit).level - 1);
CADICAL_assert (val (lit) >= 0);
handle_external_clause (0);
assign_original_unit (new_id, lit);
}
} else {
move_literals_to_watch ();
#ifndef CADICAL_NDEBUG
check_watched_literal_invariants ();
#endif
int glue = (int) (learned_levels.size () + unassigned);
CADICAL_assert (glue <= (int) clause.size ());
bool clause_redundancy = from_propagator && ext_clause_forgettable;
Clause *c = new_clause (clause_redundancy, glue);
c->id = new_id;
clause_id--;
watch_clause (c);
clause.clear ();
original.clear ();
handle_external_clause (c);
newest_clause = c;
}
}
clause.clear ();
lrat_chain.clear ();
}
// Add learned new clause during conflict analysis and watch it. Requires
// that the clause is at least of size 2, and the first two literals
// are assigned at the highest decision level.
//
Clause *Internal::new_learned_redundant_clause (int glue) {
CADICAL_assert (clause.size () > 1);
#ifndef CADICAL_NDEBUG
for (size_t i = 2; i < clause.size (); i++)
CADICAL_assert (var (clause[0]).level >= var (clause[i]).level),
CADICAL_assert (var (clause[1]).level >= var (clause[i]).level);
#endif
external->check_learned_clause ();
Clause *res = new_clause (true, glue);
if (proof) {
proof->add_derived_clause (res, lrat_chain);
}
CADICAL_assert (watching ());
watch_clause (res);
return res;
}
// Add hyper binary resolved clause during 'probing'.
//
Clause *Internal::new_hyper_binary_resolved_clause (bool red, int glue) {
external->check_learned_clause ();
Clause *res = new_clause (red, glue);
if (proof) {
proof->add_derived_clause (res, lrat_chain);
}
CADICAL_assert (watching ());
watch_clause (res);
return res;
}
// Add hyper ternary resolved clause during 'ternary'.
//
Clause *Internal::new_hyper_ternary_resolved_clause (bool red) {
external->check_learned_clause ();
size_t size = clause.size ();
Clause *res = new_clause (red, size);
if (proof) {
proof->add_derived_clause (res, lrat_chain);
}
CADICAL_assert (!watching ());
return res;
}
Clause *Internal::new_factor_clause () {
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);
}
CADICAL_assert (!watching ());
CADICAL_assert (occurring ());
for (const auto &lit : *res) {
occs (lit).push_back (res);
}
return res;
}
// Add hyper ternary resolved clause during 'congruence' and watch it
//
Clause *
Internal::new_hyper_ternary_resolved_clause_and_watch (bool red,
bool full_watching) {
external->check_learned_clause ();
size_t size = clause.size ();
Clause *res = new_clause (red, size);
if (proof) {
proof->add_derived_clause (res, lrat_chain);
}
if (full_watching) {
CADICAL_assert (watching ());
watch_clause (res);
}
return res;
}
// Add a new clause with same glue and redundancy as 'orig' but literals are
// assumed to be in 'clause' in 'decompose' and 'vivify'.
//
Clause *Internal::new_clause_as (const Clause *orig) {
external->check_learned_clause ();
const int new_glue = orig->glue;
Clause *res = new_clause (orig->redundant, new_glue);
if (proof) {
proof->add_derived_clause (res, lrat_chain);
}
CADICAL_assert (watching ());
watch_clause (res);
return res;
}
// Add resolved clause during resolution, e.g., bounded variable
// elimination, but do not connect its occurrences here.
//
Clause *Internal::new_resolved_irredundant_clause () {
external->check_learned_clause ();
if (proof) {
proof->add_derived_clause (clause_id + 1, false, clause, lrat_chain);
}
Clause *res = new_clause (false);
CADICAL_assert (!watching ());
return res;
}
} // namespace CaDiCaL
ABC_NAMESPACE_IMPL_END

View File

@ -1,551 +0,0 @@
#include "global.h"
#include "internal.hpp"
ABC_NAMESPACE_IMPL_START
namespace CaDiCaL {
/*------------------------------------------------------------------------*/
// Returns the positive number '1' ( > 0) if the given clause is root level
// satisfied or the negative number '-1' ( < 0) if it is not root level
// satisfied but contains a root level falsified literal. Otherwise, if it
// contains neither a satisfied nor falsified literal, then '0' is returned.
int Internal::clause_contains_fixed_literal (Clause *c) {
int satisfied = 0, falsified = 0;
for (const auto &lit : *c) {
const int tmp = fixed (lit);
if (tmp > 0) {
LOG (c, "root level satisfied literal %d in", lit);
satisfied++;
}
if (tmp < 0) {
LOG (c, "root level falsified literal %d in", lit);
falsified++;
}
}
if (satisfied)
return 1;
else if (falsified)
return -1;
else
return 0;
}
// Assume that the clause is not root level satisfied but contains a literal
// set to false (root level falsified literal), so it can be shrunken. The
// clause data is not actually reallocated at this point to avoid dealing
// with issues of special policies for watching binary clauses or whether a
// clause is extended or not. Only its size field is adjusted accordingly
// after flushing out root level falsified literals.
void Internal::remove_falsified_literals (Clause *c) {
const const_literal_iterator end = c->end ();
const_literal_iterator i;
int num_non_false = 0;
for (i = c->begin (); num_non_false < 2 && i != end; i++)
if (fixed (*i) >= 0)
num_non_false++;
if (num_non_false < 2)
return;
if (proof) {
// Flush changes the clause id, external forgettables need to be
// marked here (or the new id could be used instead of old one)
if (opts.check && is_external_forgettable (c->id))
mark_garbage_external_forgettable (c->id);
proof->flush_clause (c);
}
literal_iterator j = c->begin ();
for (i = j; i != end; i++) {
const int lit = *j++ = *i, tmp = fixed (lit);
CADICAL_assert (tmp <= 0);
if (tmp >= 0)
continue;
LOG ("flushing %d", lit);
j--;
}
stats.collected += shrink_clause (c, j - c->begin ());
}
// If there are new units (fixed variables) since the last garbage
// collection we go over all clauses, mark satisfied ones as garbage and
// flush falsified literals. Otherwise if no new units have been generated
// since the last garbage collection just skip this step.
void Internal::mark_satisfied_clauses_as_garbage () {
if (last.collect.fixed >= stats.all.fixed)
return;
last.collect.fixed = stats.all.fixed;
LOG ("marking satisfied clauses and removing falsified literals");
for (const auto &c : clauses) {
if (c->garbage)
continue;
const int tmp = clause_contains_fixed_literal (c);
if (tmp > 0)
mark_garbage (c);
else if (tmp < 0)
remove_falsified_literals (c);
}
}
/*------------------------------------------------------------------------*/
// Reason clauses can not be collected.
//
// We protect reasons before and release protection after garbage collection
// (actually within garbage collection).
//
// For 'reduce' we still need to make sure that all clauses which should not
// be removed are marked as such and thus we need to call it before marking
// clauses to be flushed.
void Internal::protect_reasons () {
LOG ("protecting reason clauses of all assigned variables on trail");
CADICAL_assert (!protected_reasons);
#ifdef LOGGING
size_t count = 0;
#endif
for (const auto &lit : trail) {
if (!active (lit))
continue;
CADICAL_assert (val (lit));
Var &v = var (lit);
CADICAL_assert (v.level > 0);
Clause *reason = v.reason;
if (!reason)
continue;
if (reason == external_reason)
continue;
LOG (reason, "protecting assigned %d reason %p", lit, (void *) reason);
CADICAL_assert (!reason->reason);
reason->reason = true;
#ifdef LOGGING
count++;
#endif
}
LOG ("protected %zd reason clauses referenced on trail", count);
protected_reasons = true;
}
/*------------------------------------------------------------------------*/
// After garbage collection we reset the 'reason' flag of the reasons
// of assigned literals on the trail.
void Internal::unprotect_reasons () {
LOG ("unprotecting reasons clauses of all assigned variables on trail");
CADICAL_assert (protected_reasons);
#ifdef LOGGING
size_t count = 0;
#endif
for (const auto &lit : trail) {
if (!active (lit))
continue;
CADICAL_assert (val (lit));
Var &v = var (lit);
CADICAL_assert (v.level > 0);
Clause *reason = v.reason;
if (!reason)
continue;
if (reason == external_reason)
continue;
LOG (reason, "unprotecting assigned %d reason %p", lit,
(void *) reason);
CADICAL_assert (reason->reason);
reason->reason = false;
#ifdef LOGGING
count++;
#endif
}
LOG ("unprotected %zd reason clauses referenced on trail", count);
protected_reasons = false;
}
/*------------------------------------------------------------------------*/
// Update occurrence lists before deleting garbage clauses in the context of
// preprocessing, e.g., during bounded variable elimination 'elim'. The
// result is the number of remaining clauses, which in this context means
// the number of non-garbage clauses.
size_t Internal::flush_occs (int lit) {
Occs &os = occs (lit);
const const_occs_iterator end = os.end ();
occs_iterator j = os.begin ();
const_occs_iterator i;
size_t res = 0;
Clause *c;
for (i = j; i != end; i++) {
c = *i;
if (c->collect ())
continue;
*j++ = c->moved ? c->copy : c;
// CADICAL_assert (!c->redundant); // -> not true in sweeping
res++;
}
os.resize (j - os.begin ());
shrink_occs (os);
return res;
}
// Update watch lists before deleting garbage clauses in the context of
// 'reduce' where we watch and no occurrence lists. We have to protect
// reason clauses not be collected and thus we have this additional check
// hidden in 'Clause.collect', which for the root level context of
// preprocessing is actually redundant.
inline void Internal::flush_watches (int lit, Watches &saved) {
CADICAL_assert (saved.empty ());
Watches &ws = watches (lit);
const const_watch_iterator end = ws.end ();
watch_iterator j = ws.begin ();
const_watch_iterator i;
for (i = j; i != end; i++) {
Watch w = *i;
Clause *c = w.clause;
if (c->collect ())
continue;
if (c->moved)
c = w.clause = c->copy;
w.size = c->size;
const int new_blit_pos = (c->literals[0] == lit);
LOG (c, "clause in flush_watch starting from %d", lit);
CADICAL_assert (c->literals[!new_blit_pos] == lit); /*FW1*/
w.blit = c->literals[new_blit_pos];
if (w.binary ())
*j++ = w;
else
saved.push_back (w);
}
ws.resize (j - ws.begin ());
for (const auto &w : saved)
ws.push_back (w);
saved.clear ();
shrink_vector (ws);
}
void Internal::flush_all_occs_and_watches () {
if (occurring ())
for (auto idx : vars)
flush_occs (idx), flush_occs (-idx);
if (watching ()) {
Watches tmp;
for (auto idx : vars)
flush_watches (idx, tmp), flush_watches (-idx, tmp);
}
}
/*------------------------------------------------------------------------*/
void Internal::update_reason_references () {
LOG ("update assigned reason references");
#ifdef LOGGING
size_t count = 0;
#endif
for (auto &lit : trail) {
if (!active (lit))
continue;
Var &v = var (lit);
Clause *c = v.reason;
if (!c)
continue;
if (c == external_reason)
continue;
LOG (c, "updating assigned %d reason", lit);
CADICAL_assert (c->reason);
CADICAL_assert (c->moved);
Clause *d = c->copy;
v.reason = d;
#ifdef LOGGING
count++;
#endif
}
LOG ("updated %zd assigned reason references", count);
}
/*------------------------------------------------------------------------*/
// This is a simple garbage collector which does not move clauses. It needs
// less space than the arena based clause allocator, but is not as cache
// efficient, since the copying garbage collector can put clauses together
// which are likely accessed after each other.
void Internal::delete_garbage_clauses () {
flush_all_occs_and_watches ();
LOG ("deleting garbage clauses");
#ifndef CADICAL_QUIET
int64_t collected_bytes = 0, collected_clauses = 0;
#endif
const auto end = clauses.end ();
auto j = clauses.begin (), i = j;
while (i != end) {
Clause *c = *j++ = *i++;
if (!c->collect ())
continue;
#ifndef CADICAL_QUIET
collected_bytes += c->bytes ();
collected_clauses++;
#endif
delete_clause (c);
j--;
}
clauses.resize (j - clauses.begin ());
shrink_vector (clauses);
PHASE ("collect", stats.collections,
"collected %" PRId64 " bytes of %" PRId64 " garbage clauses",
collected_bytes, collected_clauses);
}
/*------------------------------------------------------------------------*/
// This is the start of the copying garbage collector using the arena. At
// the core is the following function, which copies a clause to the 'to'
// space of the arena. Be careful if this clause is a reason of an
// assignment. In that case update the reason reference.
//
void Internal::copy_clause (Clause *c) {
LOG (c, "moving");
CADICAL_assert (!c->moved);
char *p = (char *) c;
char *q = arena.copy (p, c->bytes ());
c->copy = (Clause *) q;
c->moved = true;
LOG ("copied clause[%" PRId64 "] from %p to %p", c->id, (void *) c,
(void *) c->copy);
}
// This is the moving garbage collector.
void Internal::copy_non_garbage_clauses () {
size_t collected_clauses = 0, collected_bytes = 0;
size_t moved_clauses = 0, moved_bytes = 0;
// First determine 'moved_bytes' and 'collected_bytes'.
//
for (const auto &c : clauses)
if (!c->collect ())
moved_bytes += c->bytes (), moved_clauses++;
else
collected_bytes += c->bytes (), collected_clauses++;
PHASE ("collect", stats.collections,
"moving %zd bytes %.0f%% of %zd non garbage clauses", moved_bytes,
percent (moved_bytes, collected_bytes + moved_bytes),
moved_clauses);
(void) moved_clauses, (void) collected_clauses, (void) collected_bytes;
// Prepare 'to' space of size 'moved_bytes'.
//
arena.prepare (moved_bytes);
// Keep clauses in arena in the same order.
//
if (opts.arenacompact)
for (const auto &c : clauses)
if (!c->collect () && arena.contains (c))
copy_clause (c);
if (opts.arenatype == 1 || !watching ()) {
// Localize according to current clause order.
// If the option 'opts.arenatype == 1' is set, then this means the
// solver uses the original order of clauses. If there are no watches,
// we can not use the watched based copying policies below. This
// happens if garbage collection is triggered during bounded variable
// elimination.
// Copy clauses according to the order of calling 'copy_clause', which
// in essence just gives a compacting garbage collector, since their
// relative order is kept, and actually already gives the largest
// benefit due to better cache locality.
for (const auto &c : clauses)
if (!c->moved && !c->collect ())
copy_clause (c);
} else if (opts.arenatype == 2) {
// Localize according to (original) variable order.
// This is almost the version used by MiniSAT and descendants.
// Our version uses saved phases too.
for (int sign = -1; sign <= 1; sign += 2)
for (auto idx : vars)
for (const auto &w : watches (sign * likely_phase (idx)))
if (!w.clause->moved && !w.clause->collect ())
copy_clause (w.clause);
} else {
// Localize according to decision queue order.
// This is the default for search. It allocates clauses in the order of
// the decision queue and also uses saved phases. It seems faster than
// the MiniSAT version and thus we keep 'opts.arenatype == 3'.
CADICAL_assert (opts.arenatype == 3);
for (int sign = -1; sign <= 1; sign += 2)
for (int idx = queue.last; idx; idx = link (idx).prev)
for (const auto &w : watches (sign * likely_phase (idx)))
if (!w.clause->moved && !w.clause->collect ())
copy_clause (w.clause);
}
// Do not forget to move clauses which are not watched, which happened in
// a rare situation, and now is only left as defensive code.
//
for (const auto &c : clauses)
if (!c->collect () && !c->moved)
copy_clause (c);
flush_all_occs_and_watches ();
update_reason_references ();
// Replace and flush clause references in 'clauses'.
//
const auto end = clauses.end ();
auto j = clauses.begin (), i = j;
for (; i != end; i++) {
Clause *c = *i;
if (c->collect ())
delete_clause (c);
else
CADICAL_assert (c->moved), *j++ = c->copy, deallocate_clause (c);
}
clauses.resize (j - clauses.begin ());
if (clauses.size () < clauses.capacity () / 2)
shrink_vector (clauses);
if (opts.arenasort)
rsort (clauses.begin (), clauses.end (), pointer_rank ());
// Release 'from' space completely and then swap 'to' with 'from'.
//
arena.swap ();
PHASE ("collect", stats.collections,
"collected %zd bytes %.0f%% of %zd garbage clauses",
collected_bytes,
percent (collected_bytes, collected_bytes + moved_bytes),
collected_clauses);
}
/*------------------------------------------------------------------------*/
// Maintaining clause statistics is complex and error prone but necessary
// for proper scheduling of garbage collection, particularly during bounded
// variable elimination. With this function we can check whether these
// statistics are updated correctly.
void Internal::check_clause_stats () {
#ifndef CADICAL_NDEBUG
int64_t irredundant = 0, redundant = 0, total = 0, irrlits = 0;
for (const auto &c : clauses) {
if (c->garbage)
continue;
if (c->redundant)
redundant++;
else
irredundant++;
if (!c->redundant)
irrlits += c->size;
total++;
}
CADICAL_assert (stats.current.irredundant == irredundant);
CADICAL_assert (stats.current.redundant == redundant);
CADICAL_assert (stats.current.total == total);
CADICAL_assert (stats.irrlits == irrlits);
#endif
}
/*------------------------------------------------------------------------*/
// only delete binary clauses from watch list that are already mark as
// deleted.
void Internal::remove_garbage_binaries () {
if (unsat)
return;
START (collect);
if (!protected_reasons)
protect_reasons ();
int backtrack_level = level + 1;
Watches saved;
for (auto v : vars) {
for (auto lit : {-v, v}) {
CADICAL_assert (saved.empty ());
Watches &ws = watches (lit);
const const_watch_iterator end = ws.end ();
watch_iterator j = ws.begin ();
const_watch_iterator i;
for (i = j; i != end; i++) {
Watch w = *i;
*j++ = w;
Clause *c = w.clause;
COVER (!w.binary () && c->size == 2);
if (!w.binary ())
continue;
if (c->reason && c->garbage) {
COVER (true);
CADICAL_assert (c->size == 2);
backtrack_level =
min (backtrack_level, var (c->literals[0]).level);
LOG ("need to backtrack to before level %d", backtrack_level);
--j;
continue;
}
if (!c->collect ())
continue;
LOG (c, "removing from watch list");
--j;
}
ws.resize (j - ws.begin ());
shrink_vector (ws);
}
}
delete_garbage_clauses ();
unprotect_reasons ();
if (backtrack_level - 1 < level)
backtrack (backtrack_level - 1);
STOP (collect);
}
/*------------------------------------------------------------------------*/
bool Internal::arenaing () { return opts.arena && (stats.collections > 1); }
void Internal::garbage_collection () {
if (unsat)
return;
START (collect);
report ('G', 1);
stats.collections++;
mark_satisfied_clauses_as_garbage ();
if (!protected_reasons)
protect_reasons ();
if (arenaing ())
copy_non_garbage_clauses ();
else
delete_garbage_clauses ();
check_clause_stats ();
check_var_stats ();
unprotect_reasons ();
report ('C', 1);
STOP (collect);
}
} // namespace CaDiCaL
ABC_NAMESPACE_IMPL_END

View File

@ -1,557 +0,0 @@
#include "global.h"
#include "internal.hpp"
ABC_NAMESPACE_IMPL_START
namespace CaDiCaL {
/*------------------------------------------------------------------------*/
// Compacting removes holes generated by inactive variables (fixed,
// eliminated, substituted or pure) by mapping active variables indices down
// to a contiguous interval of indices.
/*------------------------------------------------------------------------*/
bool Internal::compacting () {
if (level)
return false;
if (!opts.compact)
return false;
if (stats.conflicts < lim.compact)
return false;
int inactive = max_var - active ();
CADICAL_assert (inactive >= 0);
if (!inactive)
return false;
if (inactive < opts.compactmin)
return false;
return inactive >= (1e-3 * opts.compactlim) * max_var;
}
/*------------------------------------------------------------------------*/
struct Mapper {
Internal *internal;
int new_max_var; // New 'max_var' after compacting.
int *table; // Old variable index to new literal map.
int first_fixed; // First fixed variable index.
int map_first_fixed; // Mapped literal of first fixed variable.
signed char first_fixed_val; // Value of first fixed variable.
size_t new_vsize;
/*----------------------------------------------------------------------*/
// We produce a compacting garbage collector like map of old 'src' to
// new 'dst' variables. Inactive variables are just skipped except for
// fixed ones which will be mapped to the first fixed variable (in the
// appropriate phase). This avoids to handle the case 'fixed value'
// separately as it is done in Lingeling, where fixed variables are
// mapped to the internal variable '1'.
//
Mapper (Internal *i)
: internal (i), new_max_var (0), first_fixed (0), map_first_fixed (0),
first_fixed_val (0) {
table = new int[internal->max_var + 1u];
clear_n (table, internal->max_var + 1u);
CADICAL_assert (!internal->level);
for (auto src : internal->vars) {
const Flags &f = internal->flags (src);
if (f.active ())
table[src] = ++new_max_var;
else if (f.fixed () && !first_fixed)
table[first_fixed = src] = map_first_fixed = ++new_max_var;
}
first_fixed_val = first_fixed ? internal->val (first_fixed) : 0;
new_vsize = new_max_var + 1u;
}
~Mapper () { delete[] table; }
/*----------------------------------------------------------------------*/
// Map old variable indices. A result of zero means not mapped.
//
int map_idx (int src) {
CADICAL_assert (0 < src);
CADICAL_assert (src <= internal->max_var);
const int res = table[src];
CADICAL_assert (res <= new_max_var);
return res;
}
/*----------------------------------------------------------------------*/
// The 'map_idx' above is just a look-up into the 'table'. Here we have
// to care about signedness of 'src', and in addition that fixed variables
// have all to be mapped to the first fixed variable 'first_fixed'.
//
int map_lit (int src) {
int res = map_idx (abs (src));
if (!res) {
const signed char tmp = internal->val (src);
if (tmp) {
CADICAL_assert (first_fixed);
res = map_first_fixed;
if (tmp != first_fixed_val)
res = -res;
}
} else if ((src) < 0)
res = -res;
CADICAL_assert (abs (res) <= new_max_var);
return res;
}
/*----------------------------------------------------------------------*/
// Map positive variable indices in vector.
//
template <class T> void map_vector (vector<T> &v) {
for (auto src : internal->vars) {
const int dst = map_idx (src);
if (!dst)
continue;
CADICAL_assert (0 < dst);
CADICAL_assert (dst <= src);
v[dst] = v[src];
}
v.resize (new_vsize);
shrink_vector (v);
}
/*----------------------------------------------------------------------*/
// Map positive and negative variable indices in two-sided vector.
//
template <class T> void map2_vector (vector<T> &v) {
for (auto src : internal->vars) {
const int dst = map_idx (src);
if (!dst)
continue;
CADICAL_assert (0 < dst);
CADICAL_assert (dst <= src);
v[2 * dst] = v[2 * src];
v[2 * dst + 1] = v[2 * src + 1];
}
v.resize (2 * new_vsize);
shrink_vector (v);
}
/*----------------------------------------------------------------------*/
// Map a vector of literals, flush inactive literals, then resize and
// shrink it to fit the new size after flushing.
//
void map_flush_and_shrink_lits (vector<int> &v) {
const auto end = v.end ();
auto j = v.begin (), i = j;
for (; i != end; i++) {
const int src = *i;
int dst = map_idx (abs (src));
CADICAL_assert (abs (dst) <= abs (src));
if (!dst)
continue;
if (src < 0)
dst = -dst;
*j++ = dst;
}
v.resize (j - v.begin ());
shrink_vector (v);
}
};
/*------------------------------------------------------------------------*/
static signed char *ignore_clang_analyze_memory_leak_warning;
void Internal::compact () {
START (compact);
CADICAL_assert (active () < max_var);
stats.compacts++;
CADICAL_assert (!level);
CADICAL_assert (!unsat);
CADICAL_assert (!conflict);
CADICAL_assert (clause.empty ());
CADICAL_assert (levels.empty ());
CADICAL_assert (analyzed.empty ());
CADICAL_assert (minimized.empty ());
CADICAL_assert (control.size () == 1);
CADICAL_assert (propagated == trail.size ());
garbage_collection ();
Mapper mapper (this);
if (mapper.first_fixed)
LOG ("found first fixed %d",
sign (mapper.first_fixed_val) * mapper.first_fixed);
else
LOG ("no variable fixed");
if (!assumptions.empty ()) {
CADICAL_assert (!external->assumptions.empty ());
LOG ("temporarily reset internal assumptions");
reset_assumptions ();
}
const bool is_constraint = !constraint.empty ();
if (is_constraint) {
CADICAL_assert (!external->constraint.empty ());
LOG ("temporarily reset internal constraint");
reset_constraint ();
}
/*======================================================================*/
// In this first part we only map stuff without reallocation / shrinking.
/*======================================================================*/
// Flush the external indices. This has to occur before we map 'vals'.
// Also fixes external units.
//
for (auto eidx : external->vars) {
int src = external->e2i[eidx];
if (!src) {
continue;
}
if (lrat || frat) {
CADICAL_assert (eidx > 0);
CADICAL_assert (external->ext_units.size () >= (size_t) 2 * eidx + 1);
int64_t id1 = external->ext_units[2 * eidx];
int64_t id2 = external->ext_units[2 * eidx + 1];
CADICAL_assert (!id1 || !id2);
if (!id1 && !id2) {
int64_t new_id1 = unit_clauses (2 * src);
int64_t new_id2 = unit_clauses (2 * src + 1);
external->ext_units[2 * eidx] = new_id1;
external->ext_units[2 * eidx + 1] = new_id2;
}
}
int dst = mapper.map_lit (src);
LOG ("compact %" PRId64
" maps external %d to internal %d from internal %d",
stats.compacts, eidx, dst, src);
external->e2i[eidx] = dst;
}
// Delete garbage units. Needs to occur before resizing unit_clauses
//
if (lrat || frat) {
for (auto src : internal->vars) {
const int dst = mapper.map_idx (src);
CADICAL_assert (dst <= src);
const signed char tmp = internal->val (src);
if (!dst && !tmp) {
unit_clauses (2 * src) = 0;
unit_clauses (2 * src + 1) = 0;
continue;
}
if (!tmp || src == mapper.first_fixed) {
CADICAL_assert (0 < dst);
if (dst == src)
continue;
CADICAL_assert (!unit_clauses (2 * dst) && !unit_clauses (2 * dst + 1));
unit_clauses (2 * dst) = unit_clauses (2 * src);
unit_clauses (2 * dst + 1) = unit_clauses (2 * src + 1);
unit_clauses (2 * src) = 0;
unit_clauses (2 * src + 1) = 0;
continue;
}
int64_t id = unit_clauses (2 * src);
int lit = src;
if (!id) {
id = unit_clauses (2 * src + 1);
lit = -lit;
}
unit_clauses (2 * src) = 0;
unit_clauses (2 * src + 1) = 0;
CADICAL_assert (id);
}
unit_clauses_idx.resize (2 * mapper.new_vsize);
shrink_vector (unit_clauses_idx);
}
// Map the literals in all clauses.
//
for (const auto &c : clauses) {
CADICAL_assert (!c->garbage);
for (auto &src : *c) {
CADICAL_assert (!val (src));
int dst;
dst = mapper.map_lit (src);
CADICAL_assert (dst || c->garbage);
src = dst;
}
}
// Map the blocking literals in all watches.
//
if (!wtab.empty ())
for (auto lit : lits)
for (auto &w : watches (lit))
w.blit = mapper.map_lit (w.blit);
// We first flush inactive variables and map the links in the queue. This
// has to be done before we map the actual links data structure 'links'.
{
int prev = 0, mapped_prev = 0, next;
for (int idx = queue.first; idx; idx = next) {
next = links[idx].next;
if (idx == mapper.first_fixed)
continue;
const int dst = mapper.map_idx (idx);
if (!dst)
continue;
CADICAL_assert (active (idx));
if (prev)
links[prev].next = dst;
else
queue.first = dst;
links[idx].prev = mapped_prev;
mapped_prev = dst;
prev = idx;
}
if (prev)
links[prev].next = 0;
else
queue.first = 0;
queue.unassigned = queue.last = mapped_prev;
}
/*======================================================================*/
// In the second part we map, flush and shrink arrays.
/*======================================================================*/
CADICAL_assert (trail.size () == num_assigned);
mapper.map_flush_and_shrink_lits (trail);
propagated = trail.size ();
num_assigned = trail.size ();
if (mapper.first_fixed) {
CADICAL_assert (trail.size () == 1);
var (mapper.first_fixed).trail = 0; // before mapping 'vtab'
} else
CADICAL_assert (trail.empty ());
if (!probes.empty ())
mapper.map_flush_and_shrink_lits (probes);
if (!sweep_schedule.empty ())
mapper.map_flush_and_shrink_lits (sweep_schedule);
/*======================================================================*/
// In the third part we map stuff and also reallocate memory.
/*======================================================================*/
// Now we continue in reverse order of allocated bytes, e.g., see
// 'Internal::enlarge' which reallocates in order of allocated bytes.
mapper.map_vector (ftab);
mapper.map_vector (parents);
mapper.map_vector (marks);
mapper.map_vector (phases.saved);
mapper.map_vector (phases.forced);
mapper.map_vector (phases.target);
mapper.map_vector (phases.best);
mapper.map_vector (phases.prev);
mapper.map_vector (phases.min);
// Special code for 'frozentab'.
//
for (auto src : vars) {
const int dst = abs (mapper.map_lit (src));
if (!dst)
continue;
if (src == dst)
continue;
CADICAL_assert (dst < src);
if ((size_t) src >= frozentab.size ())
break;
if ((size_t) dst >= frozentab.size ())
break;
frozentab[dst] += frozentab[src];
frozentab[src] = 0;
}
frozentab.resize (min (frozentab.size (), mapper.new_vsize));
shrink_vector (frozentab);
// Special code for 'relevanttab'.
//
if (external) {
for (auto src : vars) {
const int dst = abs (mapper.map_lit (src));
if (!dst)
continue;
if (src == dst)
continue;
CADICAL_assert (dst < src);
relevanttab[dst] += relevanttab[src];
relevanttab[src] = 0;
}
relevanttab.resize (mapper.new_vsize);
shrink_vector (relevanttab);
}
/*----------------------------------------------------------------------*/
if (!external->assumptions.empty ()) {
for (const auto &elit : external->assumptions) {
CADICAL_assert (elit);
CADICAL_assert (elit != INT_MIN);
int eidx = abs (elit);
CADICAL_assert (eidx <= external->max_var);
int ilit = external->e2i[eidx];
CADICAL_assert (ilit); // Because we froze all!!!
if (elit < 0)
ilit = -ilit;
assume (ilit);
}
PHASE ("compact", stats.compacts, "reassumed %zd external assumptions",
external->assumptions.size ());
}
// Special case for 'val' as for 'val' we trade branch less code for
// memory and always allocated an [-maxvar,...,maxvar] array.
{
signed char *new_vals = new signed char[2 * mapper.new_vsize];
ignore_clang_analyze_memory_leak_warning = new_vals;
new_vals += mapper.new_vsize;
for (auto src : vars)
new_vals[-mapper.map_idx (src)] = vals[-src];
for (auto src : vars)
new_vals[mapper.map_idx (src)] = vals[src];
new_vals[0] = 0;
vals -= vsize;
delete[] vals;
vals = new_vals;
vsize = mapper.new_vsize;
}
// 'constrain' uses 'val', so this code has to be after remapping that
if (is_constraint) {
CADICAL_assert (!level);
CADICAL_assert (!external->constraint.back ());
for (auto elit : external->constraint) {
CADICAL_assert (elit != INT_MIN);
int eidx = abs (elit);
CADICAL_assert (eidx <= external->max_var);
int ilit = external->e2i[eidx];
CADICAL_assert (!ilit == !elit);
if (elit < 0)
ilit = -ilit;
LOG ("re adding lit external %d internal %d to constraint", elit,
ilit);
constrain (ilit);
}
PHASE ("compact", stats.compacts,
"added %zd external literals to constraint",
external->constraint.size () - 1);
}
mapper.map_vector (i2e);
mapper.map2_vector (ptab);
mapper.map_vector (btab);
mapper.map_vector (gtab);
mapper.map_vector (links);
mapper.map_vector (vtab);
if (!ntab.empty ())
mapper.map2_vector (ntab);
if (!wtab.empty ())
mapper.map2_vector (wtab);
if (!otab.empty ())
mapper.map2_vector (otab);
if (!rtab.empty ())
mapper.map2_vector (rtab);
if (!big.empty ())
mapper.map2_vector (big);
/*======================================================================*/
// In the fourth part we map the binary heap for scores.
/*======================================================================*/
// The simplest way to map a binary heap is to get all elements from the
// heap and reinsert them. This could be slightly improved in terms of
// speed if we add a 'flush (int * map)' function to 'Heap', but that is
// pretty complicated and would require that the 'Heap' knows that mapped
// elements with 'zero' destination should be flushed.
vector<int> saved;
CADICAL_assert (saved.empty ());
if (!scores.empty ()) {
while (!scores.empty ()) {
const int src = scores.front ();
scores.pop_front ();
const int dst = mapper.map_idx (src);
if (!dst)
continue;
if (src == mapper.first_fixed)
continue;
saved.push_back (dst);
}
scores.erase ();
}
mapper.map_vector (stab);
if (!saved.empty ()) {
for (const auto idx : saved)
scores.push_back (idx);
scores.shrink ();
}
/*----------------------------------------------------------------------*/
PHASE ("compact", stats.compacts,
"reducing internal variables from %d to %d", max_var,
mapper.new_max_var);
/*----------------------------------------------------------------------*/
// Need to adjust the target and best assigned counters too.
size_t new_target_assigned = 0, new_best_assigned = 0;
for (auto idx : Range (mapper.new_max_var)) {
if (phases.target[idx])
new_target_assigned++;
if (phases.best[idx])
new_best_assigned++;
}
LOG ("reset target assigned from %zd to %zd", target_assigned,
new_target_assigned);
LOG ("reset best assigned from %zd to %zd", best_assigned,
new_best_assigned);
target_assigned = new_target_assigned;
best_assigned = new_best_assigned;
no_conflict_until = 0;
notified = 0;
INIT_EMA (averages.current.trail.fast, opts.ematrailfast);
INIT_EMA (averages.current.trail.slow, opts.ematrailslow);
/*----------------------------------------------------------------------*/
max_var = mapper.new_max_var;
stats.unused = 0;
stats.inactive = stats.now.fixed = mapper.first_fixed ? 1 : 0;
stats.now.substituted = stats.now.eliminated = stats.now.pure = 0;
check_var_stats ();
int64_t delta = opts.compactint * (stats.compacts + 1);
lim.compact = stats.conflicts + delta;
PHASE ("compact", stats.compacts,
"new compact limit %" PRId64 " after %" PRId64 " conflicts",
lim.compact, delta);
STOP (compact);
}
} // namespace CaDiCaL
ABC_NAMESPACE_IMPL_END

View File

@ -1,946 +0,0 @@
#include "global.h"
#include "internal.hpp"
ABC_NAMESPACE_IMPL_START
namespace CaDiCaL {
/*------------------------------------------------------------------------*/
// Globally blocked clause elimination (which we call here 'conditioning')
// is described first in the PhD thesis of Benjamin Kiesl from 2019. An
// extended version, which in particular describes the algorithm implemented
// below is in our invited ATVA'19 paper [KieslHeuleBiere-ATVA'19]. This
// accordingly needs witnesses consisting potentially of more than one
// literal. It is the first technique implemented in CaDiCaL with this
// feature (PR clause elimination thus should work in principle too).
// Basically globally blocked clauses are like set blocked clauses, except
// that the witness cube (of literals to be flipped during reconstruction)
// can contain variables which are not in the blocked clause. This
// can simulate some interesting global optimizations like 'headlines' from
// the FAN algorithm for ATPG. The technique was actually motivated to
// simulate this optimization. It turns out that globally blocked clauses
// can be seen as 'conditional autarkies', where in essence the condition
// cube is the negation of the globally blocked redundant clause (it
// needs to contain one autarky literal though) and the autarky part
// represents the witness.
/*------------------------------------------------------------------------*/
// Elimination of globally blocked clauses is first tried in regular
// intervals in terms of the number of conflicts. Then the main heuristics
// is to trigger 'condition' if the decision level is above the current
// moving average of the back jump level.
// TODO We might need to consider less frequent conditioning.
bool Internal::conditioning () {
if (!opts.condition)
return false;
if (!preprocessing && !opts.inprocessing)
return false;
if (preprocessing)
CADICAL_assert (lim.preprocessing);
// Triggered in regular 'opts.conditionint' conflict intervals.
//
if (lim.condition > stats.conflicts)
return false;
if (!level)
return false; // One decision necessary.
if (level <= averages.current.jump)
return false; // Main heuristic.
if (!stats.current.irredundant)
return false;
double remain = active ();
if (!remain)
return false;
double ratio = stats.current.irredundant / remain;
return ratio <= opts.conditionmaxrat;
}
/*------------------------------------------------------------------------*/
// We start with the current assignment and then temporarily unassign
// literals. They are reassigned afterwards. The global state of the CDCL
// solver should not change though. Thus we copied from 'search_unassign'
// in 'backtrack.cpp' what is needed to unassign literals and then from
// 'search_assign' in 'propagate.cpp' what is needed for reassigning
// literals, but restricted the copied code to only updating the actual
// assignment (in 'vals') and not changing anything else.
// We use temporarily unassigning for two purposes. First, if a conditional
// literal does not occur negated in a candidate clause it is unassigned.
// Second, as a minor optimization, we first unassign all root-level
// assigned (fixed) literals, to avoid checking the decision level of
// literals during the procedure.
void Internal::condition_unassign (int lit) {
LOG ("condition unassign %d", lit);
CADICAL_assert (val (lit) > 0);
set_val (lit, 0);
}
void Internal::condition_assign (int lit) {
LOG ("condition assign %d", lit);
CADICAL_assert (!val (lit));
set_val (lit, 1);
}
/*------------------------------------------------------------------------*/
// The current partition into conditional part and autarky part during
// refinement is represented through a conditional bit in 'marks'.
inline bool Internal::is_conditional_literal (int lit) const {
return val (lit) > 0 && getbit (lit, 0);
}
inline bool Internal::is_autarky_literal (int lit) const {
return val (lit) > 0 && !getbit (lit, 0);
}
inline void Internal::mark_as_conditional_literal (int lit) {
LOG ("marking %d as conditional literal", lit);
CADICAL_assert (val (lit) > 0);
setbit (lit, 0);
CADICAL_assert (is_conditional_literal (lit));
CADICAL_assert (!is_autarky_literal (lit));
}
inline void Internal::unmark_as_conditional_literal (int lit) {
LOG ("unmarking %d as conditional literal", lit);
CADICAL_assert (is_conditional_literal (lit));
unsetbit (lit, 0);
}
/*------------------------------------------------------------------------*/
// We also need to know the literals which are in the current clause. These
// are just marked (also in 'marks' but with the (signed) upper two bits).
// We need a signed mark here, since we have to distinguish positive and
// negative occurrences of literals in the candidate clause.
inline bool Internal::is_in_candidate_clause (int lit) const {
return marked67 (lit) > 0;
}
inline void Internal::mark_in_candidate_clause (int lit) {
LOG ("marking %d as literal of the candidate clause", lit);
mark67 (lit);
CADICAL_assert (is_in_candidate_clause (lit));
CADICAL_assert (!is_in_candidate_clause (-lit));
}
inline void Internal::unmark_in_candidate_clause (int lit) {
LOG ("unmarking %d as literal of the candidate clause", lit);
CADICAL_assert (is_in_candidate_clause (lit));
unmark67 (lit);
}
/*------------------------------------------------------------------------*/
struct less_conditioned {
bool operator() (Clause *a, Clause *b) {
return !a->conditioned && b->conditioned;
}
};
// This is the function for eliminating globally blocked clauses. It is
// triggered during CDCL search according to 'conditioning' above and uses
// the current assignment as basis to find globally blocked clauses.
long Internal::condition_round (long delta) {
long limit;
#ifndef CADICAL_QUIET
long props = 0;
#endif
if (LONG_MAX - delta < stats.condprops)
limit = LONG_MAX;
else
limit = stats.condprops + delta;
size_t initial_trail_level = trail.size ();
int initial_level = level;
LOG ("initial trail level %zd", initial_trail_level);
protect_reasons ();
#if defined(LOGGING) || !defined(CADICAL_NDEBUG)
int additionally_assigned = 0;
#endif
for (auto idx : vars) {
const signed char tmp = val (idx);
Var &v = var (idx);
if (tmp) {
if (v.level) {
const int lit = tmp < 0 ? -idx : idx;
if (!active (idx)) {
LOG ("temporarily unassigning inactive literal %d", lit);
condition_unassign (lit);
}
if (frozen (idx)) {
LOG ("temporarily unassigning frozen literal %d", lit);
condition_unassign (lit);
}
}
} else if (frozen (idx)) {
LOG ("keeping frozen literal %d unassigned", idx);
} else if (!active (idx)) {
LOG ("keeping inactive literal %d unassigned", idx);
} else { // if (preprocessing) {
if (initial_level == level) {
level++;
LOG ("new condition decision level");
}
const int lit = decide_phase (idx, true);
condition_assign (lit);
v.level = level;
trail.push_back (lit);
#if defined(LOGGING) || !defined(CADICAL_NDEBUG)
additionally_assigned++;
#endif
}
}
LOG ("assigned %d additional literals", additionally_assigned);
// We compute statistics about the size of the assignments.
//
// The initial assignment consists of the non-root-level assigned literals
// split into a conditional and an autarky part. The conditional part
// consists of literals assigned true and occurring negated in a clause
// (touch the clause), which does not contain another literal assigned to
// true. This initial partition is the same for all refinements used in
// checking whether a candidate clause is globally blocked.
//
// For each candidate clause some of the conditional literals have to be
// unassigned, and the autarky is shrunken by turning some of the autarky
// literals into conditional literals (which might get unassigned in a
// later refinement though).
//
// The fix-point of this procedure produces a final assignment, which
// consists of the remaining assigned literals, again split into a
// conditional and an autarky part.
//
struct {
size_t assigned, conditional, autarky;
} initial, remain;
initial.assigned = 0;
for (auto idx : vars) {
const signed char tmp = val (idx);
if (!tmp)
continue;
if (!var (idx).level)
continue;
LOG ("initial assignment %ds", tmp < 0 ? -idx : idx);
initial.assigned++;
}
PHASE ("condition", stats.conditionings, "initial assignment of size %zd",
initial.assigned);
// For each candidate clause we refine the assignment (monotonically),
// by unassigning some conditional literals and turning some autarky
// literals into conditionals.
//
// As the conditional part is usually smaller than the autarky part our
// implementation only explicitly maintains the initial conditional part,
// with conditional bit set to true through 'mark_as_conditional_literal'.
// The autarky part consists of all literals assigned true which do not
// have their conditional bit set to true. Since in both cases the
// literal has to be assigned true, we only need a single bit for both the
// literal as well as its negation (it does not have to be 'signed').
//
vector<int> conditional;
vector<Clause *> candidates; // Gather candidate clauses.
#ifndef CADICAL_QUIET
size_t watched = 0; // Number of watched clauses.
#endif
initial.autarky = initial.assigned; // Initially all are in autarky
initial.conditional = 0; // and none in conditional part.
// Upper bound on the number of watched clauses. In principle one could
// use 'SIZE_MAX' but this is not available by default (yet).
//
const size_t size_max = clauses.size () + 1;
// Initialize additional occurrence lists.
//
init_occs ();
// Number of previously conditioned and unconditioned candidates.
//
size_t conditioned = 0, unconditioned = 0;
// Now go over all (non-garbage) irredundant clauses and check whether
// they are candidates, have to be watched, or whether they force the
// negation of some of their literals to be conditional initially.
//
for (const auto &c : clauses) {
if (c->garbage)
continue; // Can already be ignored.
if (c->redundant)
continue; // Ignore redundant clauses too.
// First determine the following numbers for the candidate clause
// (restricted to non-root-level assignments).
//
int positive = 0; // Number true literals.
int negative = 0; // Number false literals.
int watch = 0; // True Literal to watch.
//
size_t minsize = size_max; // Number of occurrences of 'watch'.
//
// But also ignore root-level satisfied but not yet garbage clauses.
//
bool satisfied = false; // Root level satisfied.
//
for (const_literal_iterator l = c->begin ();
!satisfied && l != c->end (); l++) {
const int lit = *l;
const signed char tmp = val (lit);
if (tmp && !var (lit).level)
satisfied = (tmp > 0);
else if (tmp < 0)
negative++;
else if (tmp > 0) {
const size_t size = occs (lit).size ();
if (size < minsize)
watch = lit, minsize = size;
positive++;
}
}
if (satisfied) { // Ignore root-level satisfied clauses.
mark_garbage (c); // But mark them as garbage already now.
continue; // ... with next clause 'c'.
}
// Candidates are clauses with at least a positive literal in it.
//
if (positive > 0) {
LOG (c, "found %d positive literals in candidate", positive);
candidates.push_back (c);
if (c->conditioned)
conditioned++;
else
unconditioned++;
}
// Only one positive literal in each clauses with also at least one
// negative literal has to be watched in occurrence lists. These
// watched clauses will be checked to contain only negative literals as
// soon such a positive literal is unassigned. If this is the case
// these false literals have to be unassigned and potentially new
// conditional literals have to be determined.
//
// Note that only conditional literals are unassigned. However it does
// not matter that we might also watch autarky literals, because either
// such an autarky literal remains a witness that the clause is
// satisfied as long it remains an autarky literal. Otherwise at one
// point it becomes conditional and is unassigned, but then a
// replacement watch will be searched.
//
if (negative > 0 && positive > 0) {
LOG (c, "found %d negative literals in candidate", negative);
CADICAL_assert (watch);
CADICAL_assert (val (watch) > 0);
Occs &os = occs (watch);
CADICAL_assert (os.size () == minsize);
os.push_back (c);
#ifndef CADICAL_QUIET
watched++;
#endif
LOG (c, "watching %d with %zd occurrences in", watch, minsize);
}
// The initial global conditional part for the current assignment is
// extracted from clauses with only negative literals. It is the same
// for all considered candidate clauses. These negative literals make up
// the global conditional part, are marked here.
//
if (negative > 0 && !positive) {
size_t new_conditionals = 0;
for (const_literal_iterator l = c->begin (); l != c->end (); l++) {
const int lit = *l;
signed char tmp = val (lit);
if (!tmp)
continue;
CADICAL_assert (tmp < 0);
if (!var (lit).level)
continue; // Not unassigned yet!
if (is_conditional_literal (-lit))
continue;
mark_as_conditional_literal (-lit);
conditional.push_back (-lit);
new_conditionals++;
}
if (new_conditionals > 0)
LOG (c, "marked %zu negations of literals as conditional in",
new_conditionals);
initial.conditional += new_conditionals;
CADICAL_assert (initial.autarky >= new_conditionals);
initial.autarky -= new_conditionals;
}
} // End of loop over all clauses to collect candidates etc.
PHASE ("condition", stats.conditionings, "found %zd candidate clauses",
candidates.size ());
PHASE ("condition", stats.conditionings,
"watching %zu literals and clauses", watched);
PHASE ("condition", stats.conditionings,
"initially %zd conditional literals %.0f%%", initial.conditional,
percent (initial.conditional, initial.assigned));
PHASE ("condition", stats.conditionings,
"initially %zd autarky literals %.0f%%", initial.autarky,
percent (initial.autarky, initial.assigned));
#ifdef LOGGING
for (size_t i = 0; i < conditional.size (); i++) {
LOG ("initial conditional %d", conditional[i]);
CADICAL_assert (is_conditional_literal (conditional[i]));
}
for (size_t i = 0; i < trail.size (); i++)
if (is_autarky_literal (trail[i]))
LOG ("initial autarky %d", trail[i]);
#endif
CADICAL_assert (initial.conditional == conditional.size ());
CADICAL_assert (initial.assigned == initial.conditional + initial.autarky);
stats.condassinit += initial.assigned;
stats.condcondinit += initial.conditional;
stats.condautinit += initial.autarky;
stats.condassvars += active ();
// To speed-up and particularly simplify the code we unassign all
// root-level variables temporarily, actually all inactive assigned
// variables. This allows us to avoid tests on whether an assigned
// literal is actually root-level assigned and thus should be ignored (not
// considered to be assigned). For this to work we have to ignore root
// level satisfied clauses as done above. These are neither candidates
// nor have to be watched. Remaining originally root-level assigned
// literals in clauses are only set to false.
//
for (const auto &lit : trail)
if (fixed (lit))
condition_unassign (lit);
// Stack to save temporarily unassigned (conditional) literals.
//
vector<int> unassigned;
// Make sure to focus on clauses not tried before by marking clauses which
// have been checked before using the 'conditioned' bit of clauses. If all
// candidates have their bit set, we have to reset it. Since the
// assignment might be completely different then last time and thus also
// the set of candidates this method does not really exactly lead to a
// round robin scheme of scheduling clauses.
//
// TODO consider computing conditioned and unconditioned over all clauses.
//
CADICAL_assert (conditioned + unconditioned == candidates.size ());
if (conditioned && unconditioned) {
stable_sort (candidates.begin (), candidates.end (),
less_conditioned ());
PHASE ("condition", stats.conditionings,
"focusing on %zd candidates %.0f%% not tried last time",
unconditioned, percent (unconditioned, candidates.size ()));
} else if (conditioned && !unconditioned) {
for (auto const &c : candidates) {
CADICAL_assert (c->conditioned);
c->conditioned = false; // Reset 'conditioned' bit.
}
PHASE ("condition", stats.conditionings,
"all %zd candidates tried before", conditioned);
} else {
CADICAL_assert (!conditioned);
PHASE ("condition", stats.conditionings, "all %zd candidates are fresh",
unconditioned);
}
// TODO prune assignments further!
// And thus might result in less watched clauses.
// So watching should be done here and not earlier.
// Also, see below, we might need to consider the negation of unassigned
// literals in candidate clauses as being watched.
// Now try to block all candidate clauses.
//
long blocked = 0; // Number of Successfully blocked clauses.
//
#ifndef CADICAL_QUIET
size_t untried = candidates.size ();
#endif
for (const auto &c : candidates) {
if (initial.autarky <= 0)
break;
if (c->reason)
continue;
bool terminated_or_limit_hit = true;
if (terminated_asynchronously ())
LOG ("asynchronous termination detected");
else if (stats.condprops >= limit)
LOG ("condition propagation limit %ld hit", limit);
else
terminated_or_limit_hit = false;
if (terminated_or_limit_hit) {
PHASE ("condition", stats.conditionings,
"%zd candidates %.0f%% not tried after %ld propagations",
untried, percent (untried, candidates.size ()), props);
break;
}
#ifndef CADICAL_QUIET
untried--;
#endif
CADICAL_assert (!c->garbage);
CADICAL_assert (!c->redundant);
LOG (c, "candidate");
c->conditioned = 1; // Next time later.
// We watch an autarky literal in the clause, and can stop trying to
// globally block the clause as soon it turns into a conditional
// literal and we can not find another one. If the fix-point assignment
// is reached and we still have an autarky literal left the watched one
// is reported as witness for this clause being globally blocked.
//
int watched_autarky_literal = 0;
// First mark all true literals in the candidate clause and find an
// autarky literal which witnesses that this clause has still a chance
// to be globally blocked.
//
for (const_literal_iterator l = c->begin (); l != c->end (); l++) {
const int lit = *l;
mark_in_candidate_clause (lit);
if (watched_autarky_literal)
continue;
if (!is_autarky_literal (lit))
continue;
watched_autarky_literal = lit;
// TODO assign non-assigned literals to false?
// Which might need to trigger watching additional clauses.
}
if (!watched_autarky_literal) {
LOG ("no initial autarky literal found");
for (const_literal_iterator l = c->begin (); l != c->end (); l++)
unmark_in_candidate_clause (*l);
continue;
}
stats.condcands++; // Only now ...
LOG ("watching first autarky literal %d", watched_autarky_literal);
// Save assignment sizes for statistics, logging and checking.
//
remain = initial;
// Position of next conditional and unassigned literal to process in the
// 'conditional' and the 'unassigned' stack.
//
struct {
size_t conditional, unassigned;
} next = {0, 0};
CADICAL_assert (unassigned.empty ());
CADICAL_assert (conditional.size () == initial.conditional);
while (watched_autarky_literal && stats.condprops < limit &&
next.conditional < conditional.size ()) {
CADICAL_assert (next.unassigned == unassigned.size ());
const int conditional_lit = conditional[next.conditional++];
LOG ("processing next conditional %d", conditional_lit);
CADICAL_assert (is_conditional_literal (conditional_lit));
if (is_in_candidate_clause (-conditional_lit)) {
LOG ("conditional %d negated in candidate clause", conditional_lit);
continue;
}
LOG ("conditional %d does not occur negated in candidate clause",
conditional_lit);
condition_unassign (conditional_lit);
CADICAL_assert (!is_conditional_literal (conditional_lit));
unassigned.push_back (conditional_lit);
CADICAL_assert (remain.assigned > 0);
CADICAL_assert (remain.conditional > 0);
remain.conditional--;
remain.assigned--;
while (watched_autarky_literal && stats.condprops < limit &&
next.unassigned < unassigned.size ()) {
const int unassigned_lit = unassigned[next.unassigned++];
LOG ("processing next unassigned %d", unassigned_lit);
CADICAL_assert (!val (unassigned_lit));
#ifndef CADICAL_QUIET
props++;
#endif
stats.condprops++;
Occs &os = occs (unassigned_lit);
if (os.empty ())
continue;
// Traverse all watched clauses of 'unassigned_lit' and find
// replacement watches or if none is found turn the negation of all
// false autarky literals in that clause into conditional literals.
// If one of those autarky literals is the watched autarky literal
// in the candidate clause, that one has to be updated too.
//
// We expect that this loop is a hot-spot for the procedure and thus
// are more careful about accessing end points for iterating.
//
auto i = os.begin (), j = i;
for (; watched_autarky_literal && j != os.end (); j++) {
Clause *d = *i++ = *j;
int replacement = 0; // New watched literal in 'd'.
int negative = 0; // Negative autarky literals in 'd'.
for (const_literal_iterator l = d->begin (); l != d->end ();
l++) {
const int lit = *l;
const signed char tmp = val (lit);
if (tmp > 0)
replacement = lit;
if (tmp < 0 && is_autarky_literal (-lit))
negative++;
}
if (replacement) {
LOG ("found replacement %d for unassigned %d", replacement,
unassigned_lit);
LOG (d, "unwatching %d in", unassigned_lit);
i--; // Drop watch!
LOG (d, "watching %d in", replacement);
CADICAL_assert (replacement != unassigned_lit);
occs (replacement).push_back (d);
continue; // ... with next watched clause 'd'.
}
LOG ("no replacement found for unassigned %d", unassigned_lit);
// Keep watching 'd' by 'unassigned_lit' if no replacement found.
if (!negative) {
LOG (d, "no negative autarky literals left in");
continue; // ... with next watched clause 'd'.
}
LOG (d, "found %d negative autarky literals in", negative);
for (const_literal_iterator l = d->begin ();
watched_autarky_literal && l != d->end (); l++) {
const int lit = *l;
if (!is_autarky_literal (-lit))
continue;
mark_as_conditional_literal (-lit);
conditional.push_back (-lit);
remain.conditional++;
CADICAL_assert (remain.autarky > 0);
remain.autarky--;
if (-lit != watched_autarky_literal)
continue;
LOG ("need to replace autarky literal %d in candidate", -lit);
replacement = 0;
// TODO save starting point because we only move it forward?
for (const_literal_iterator k = c->begin ();
!replacement && k != c->end (); k++) {
const int other = *k;
if (is_autarky_literal (other))
replacement = other;
}
watched_autarky_literal = replacement;
if (replacement) {
LOG (c, "watching autarky %d instead %d in candidate",
replacement, watched_autarky_literal);
watched_autarky_literal = replacement;
} else {
LOG ("failed to find an autarky replacement");
watched_autarky_literal = 0; // Breaks out of 4 loops!!!!!
}
} // End of loop of turning autarky literals into conditionals.
} // End of loop of all watched clauses of an unassigned literal.
//
// We might abort the occurrence traversal early but already
// removed some watches, thus have to just copy the rest.
//
if (i < j) {
while (j != os.end ())
*i++ = *j++;
LOG ("flushed %zd occurrences of %d", os.end () - i,
unassigned_lit);
os.resize (i - os.begin ());
}
} // End of loop which goes over all unprocessed unassigned literals.
} // End of loop which goes over all unprocessed conditional literals.
// We are still processing the candidate 'c' and now have reached a
// final fix-point assignment partitioned into a conditional and an
// autarky part, or during unassigned literals figured that there is no
// positive autarky literal left in 'c'.
LOG ("remaining assignment of size %zd", remain.assigned);
LOG ("remaining conditional part of size %zd", remain.conditional);
LOG ("remaining autarky part of size %zd", remain.autarky);
//
CADICAL_assert (remain.assigned - remain.conditional == remain.autarky);
//
#if defined(LOGGING) || !defined(CADICAL_NDEBUG)
//
// This is a sanity check, that the size of our implicit representation
// of the autarky part matches our 'remain' counts. We need the same
// code for determining autarky literals as in the loop below which adds
// autarky literals to the extension stack.
//
struct {
size_t assigned, conditional, autarky;
} check;
check.assigned = check.conditional = check.autarky = 0;
for (size_t i = 0; i < trail.size (); i++) {
const int lit = trail[i];
if (val (lit)) {
check.assigned++;
if (is_conditional_literal (lit)) {
LOG ("remaining conditional %d", lit);
CADICAL_assert (!is_autarky_literal (lit));
check.conditional++;
} else {
CADICAL_assert (is_autarky_literal (lit));
LOG ("remaining autarky %d", lit);
check.autarky++;
}
} else {
CADICAL_assert (!is_autarky_literal (lit));
CADICAL_assert (!is_conditional_literal (lit));
}
}
CADICAL_assert (remain.assigned == check.assigned);
CADICAL_assert (remain.conditional == check.conditional);
CADICAL_assert (remain.autarky == check.autarky);
#endif
// Success if an autarky literal is left in the clause and
// we did not abort the loop too early because the propagation
// limit was hit.
//
if (watched_autarky_literal && stats.condprops < limit) {
CADICAL_assert (is_autarky_literal (watched_autarky_literal));
CADICAL_assert (is_in_candidate_clause (watched_autarky_literal));
blocked++;
stats.conditioned++;
LOG (c, "positive autarky literal %d globally blocks",
watched_autarky_literal);
LOG ("remaining %zd assigned literals %.0f%%", remain.assigned,
percent (remain.assigned, initial.assigned));
LOG ("remaining %zd conditional literals %.0f%%", remain.conditional,
percent (remain.conditional, remain.assigned));
LOG ("remaining %zd autarky literals %.0f%%", remain.autarky,
percent (remain.autarky, remain.assigned));
// A satisfying assignment of a formula after removing a globally
// blocked clause might not satisfy that clause. As for variable
// elimination and classical blocked clauses, we thus maintain an
// extension stack for reconstructing an assignment which both
// satisfies the remaining formula as well as the clause.
//
// For globally blocked clauses we simply have to flip all literals in
// the autarky part and thus save the autarky on the extension stack
// in addition to the removed clause. In the classical situation (in
// bounded variable elimination etc.) we simply save one literal on
// the extension stack.
//
// TODO find a way to shrink the autarky part or some other way to
// avoid pushing too many literals on the extension stack.
//
external->push_zero_on_extension_stack ();
for (const auto &lit : trail)
if (is_autarky_literal (lit))
external->push_witness_literal_on_extension_stack (lit);
if (proof)
proof->weaken_minus (c);
external->push_clause_on_extension_stack (c);
mark_garbage (c);
stats.condassrem += remain.assigned;
stats.condcondrem += remain.conditional;
stats.condautrem += remain.autarky;
stats.condassirem += initial.assigned;
}
// In this last part specific to one candidate clause, we have to get
// back to the initial assignment and reset conditionals. First we
// assign all the unassigned literals (if necessary).
//
if (!unassigned.empty ()) {
LOG ("reassigning %zd literals", unassigned.size ());
while (!unassigned.empty ()) {
const int lit = unassigned.back ();
unassigned.pop_back ();
condition_assign (lit);
}
}
// Then we remove from the conditional stack autarky literals which
// became conditional and also reset their 'conditional' bit.
//
if (initial.conditional < conditional.size ()) {
LOG ("flushing %zd autarky literals from conditional stack",
conditional.size () - initial.conditional);
while (initial.conditional < conditional.size ()) {
const int lit = conditional.back ();
conditional.pop_back ();
unmark_as_conditional_literal (lit);
}
}
// Finally unmark all literals in the candidate clause.
//
for (const_literal_iterator l = c->begin (); l != c->end (); l++)
unmark_in_candidate_clause (*l);
} // End of loop over all candidate clauses.
PHASE ("condition", stats.conditionings,
"globally blocked %ld clauses %.0f%%", blocked,
percent (blocked, candidates.size ()));
// Unmark initial conditional variables.
//
for (const auto &lit : conditional)
unmark_as_conditional_literal (lit);
erase_vector (unassigned);
erase_vector (conditional);
erase_vector (candidates);
// Unassign additionally assigned literals.
//
#if defined(LOGGING) || !defined(CADICAL_NDEBUG)
int additionally_unassigned = 0;
#endif
while (trail.size () > initial_trail_level) {
int lit = trail.back ();
trail.pop_back ();
condition_unassign (lit);
#if defined(LOGGING) || !defined(CADICAL_NDEBUG)
additionally_unassigned++;
#endif
}
LOG ("unassigned %d additionally assigned literals",
additionally_unassigned);
CADICAL_assert (additionally_unassigned == additionally_assigned);
if (level > initial_level) {
LOG ("reset condition decision level");
level = initial_level;
}
reset_occs ();
delete_garbage_clauses ();
// Reassign previously assigned variables again.
//
LOG ("reassigning previously assigned variables");
for (size_t i = 0; i < initial_trail_level; i++) {
const int lit = trail[i];
const signed char tmp = val (lit);
CADICAL_assert (tmp >= 0);
if (!tmp)
condition_assign (lit);
}
#ifndef CADICAL_NDEBUG
for (const auto &lit : trail)
CADICAL_assert (!marked (lit));
#endif
unprotect_reasons ();
return blocked;
}
void Internal::condition (bool update_limits) {
if (unsat)
return;
if (!stats.current.irredundant)
return;
START_SIMPLIFIER (condition, CONDITION);
stats.conditionings++;
// Propagation limit to avoid too much work in 'condition'. We mark
// tried candidate clauses after giving up, such that next time we run
// 'condition' we can try them.
//
long limit = stats.propagations.search;
limit *= opts.conditioneffort;
limit /= 1000;
if (limit < opts.conditionmineff)
limit = opts.conditionmineff;
if (limit > opts.conditionmaxeff)
limit = opts.conditionmaxeff;
CADICAL_assert (stats.current.irredundant);
limit *= 2.0 * active () / (double) stats.current.irredundant;
limit = max (limit, 2l * active ());
PHASE ("condition", stats.conditionings,
"started after %" PRIu64 " conflicts limited by %ld propagations",
stats.conflicts, limit);
long blocked = condition_round (limit);
STOP_SIMPLIFIER (condition, CONDITION);
report ('g', !blocked);
if (!update_limits)
return;
long delta = opts.conditionint * (stats.conditionings + 1);
lim.condition = stats.conflicts + delta;
PHASE ("condition", stats.conditionings,
"next limit at %" PRIu64 " after %ld conflicts", lim.condition,
delta);
}
} // namespace CaDiCaL
ABC_NAMESPACE_IMPL_END

View File

@ -1,107 +0,0 @@
#include "global.h"
#include "internal.hpp"
ABC_NAMESPACE_IMPL_START
namespace CaDiCaL {
/*------------------------------------------------------------------------*/
struct NameVal {
const char *name;
int val;
};
/*------------------------------------------------------------------------*/
// These are dummy configurations, which require additional code.
static NameVal default_config[1]; // With '-pedantic' just '[]' or
static NameVal plain_config[1]; // '[0]' gave a warning.
/*------------------------------------------------------------------------*/
// Here we have the pre-defined default configurations.
static NameVal sat_config[] = {
{"elimeffort", 10},
{"stabilizeonly", 1},
{"subsumeeffort", 60},
};
static NameVal unsat_config[] = {
{"stabilize", 0},
{"walk", 0},
};
/*------------------------------------------------------------------------*/
#define CONFIGS \
\
CONFIG (default, "set default advanced internal options") \
CONFIG (plain, "disable all internal preprocessing options") \
CONFIG (sat, "set internal options to target satisfiable instances") \
CONFIG (unsat, "set internal options to target unsatisfiable instances")
static const char *configs[] = {
#define CONFIG(N, D) #N,
CONFIGS
#undef CONFIG
};
static size_t num_configs = sizeof configs / sizeof *configs;
/*------------------------------------------------------------------------*/
bool Config::has (const char *name) {
#define CONFIG(N, D) \
if (!strcmp (name, #N)) \
return true;
CONFIGS
#undef CONFIG
return false;
}
bool Config::set (Options &opts, const char *name) {
if (!strcmp (name, "default")) {
opts.reset_default_values ();
return true;
}
if (!strcmp (name, "plain")) {
opts.disable_preprocessing ();
return true;
}
#define CONFIG(N, D) \
do { \
if (strcmp (name, #N)) \
break; \
const NameVal *BEGIN = N##_config; \
const NameVal *END = BEGIN + sizeof N##_config / sizeof (NameVal); \
for (const NameVal *P = BEGIN; P != END; P++) { \
CADICAL_assert (Options::has (P->name)); \
opts.set (P->name, P->val); \
} \
return true; \
} while (0);
CONFIGS
#undef CONFIG
return false;
}
/*------------------------------------------------------------------------*/
void Config::usage () {
#define CONFIG(N, D) printf (" %-14s " D "\n", "--" #N);
CONFIGS
#undef CONFIG
}
/*------------------------------------------------------------------------*/
const char **Config::begin () { return configs; }
const char **Config::end () { return &configs[num_configs]; }
} // namespace CaDiCaL
ABC_NAMESPACE_IMPL_END

File diff suppressed because it is too large Load Diff

View File

@ -1,70 +0,0 @@
#include "global.h"
#include "internal.hpp"
ABC_NAMESPACE_IMPL_START
namespace CaDiCaL {
void Internal::constrain (int lit) {
if (lit)
constraint.push_back (lit);
else {
if (level)
backtrack ();
LOG (constraint, "shrinking constraint");
bool satisfied_constraint = false;
const vector<int>::const_iterator end = constraint.end ();
vector<int>::iterator i = constraint.begin ();
for (vector<int>::const_iterator j = i; j != end; j++) {
int tmp = marked (*j);
if (tmp > 0) {
LOG ("removing duplicated literal %d from constraint", *j);
} else if (tmp < 0) {
LOG ("tautological since both %d and %d occur in constraint", -*j,
*j);
satisfied_constraint = true;
break;
} else {
tmp = val (*j);
if (tmp < 0) {
LOG ("removing falsified literal %d from constraint clause", *j);
} else if (tmp > 0) {
LOG ("satisfied constraint with literal %d", *j);
satisfied_constraint = true;
break;
} else {
*i++ = *j;
mark (*j);
}
}
}
constraint.resize (i - constraint.begin ());
for (const auto &lit : constraint)
unmark (lit);
if (satisfied_constraint)
constraint.clear ();
else if (constraint.empty ()) {
unsat_constraint = true;
if (!conflict_id)
marked_failed = false; // allow to trigger failing ()
} else
for (const auto lit : constraint)
freeze (lit);
}
}
bool Internal::failed_constraint () { return unsat_constraint; }
void Internal::reset_constraint () {
for (auto lit : constraint)
melt (lit);
LOG ("cleared %zd constraint literals", constraint.size ());
constraint.clear ();
unsat_constraint = false;
marked_failed = true;
}
} // namespace CaDiCaL
ABC_NAMESPACE_IMPL_END

View File

@ -1,33 +0,0 @@
#include "global.h"
#ifndef CADICAL_NCONTRACTS
#include "internal.hpp"
ABC_NAMESPACE_IMPL_START
namespace CaDiCaL {
void fatal_message_start ();
// See comments in 'contract.hpp'. Ugly hack we keep for now.
void require_solver_pointer_to_be_non_zero (const void *ptr,
const char *function_name,
const char *file_name) {
if (ptr)
return;
fatal_message_start ();
fprintf (stderr,
"invalid API usage of '%s' in '%s': "
"solver 'this' pointer zero (not initialized)\n",
function_name, file_name);
fflush (stderr);
abort ();
}
} // namespace CaDiCaL
ABC_NAMESPACE_IMPL_END
#endif

View File

@ -1,710 +0,0 @@
#include "global.h"
#include "internal.hpp"
ABC_NAMESPACE_IMPL_START
namespace CaDiCaL {
/*------------------------------------------------------------------------*/
// Covered clause elimination (CCE) is described in our short LPAR-10 paper
// and later in more detail in our JAIR'15 article. Actually implement
// the asymmetric version which adds asymmetric literals too but still call
// it 'CCE' in the following (and not 'ACCE'). This implementation provides
// a simplified and cleaner version of the one implemented before in
// Lingeling. We still follow quite closely the original description in the
// literature, which is based on asymmetric literal addition (ALA) and
// covered literal addition (CLA). Both can be seen as kind of propagation,
// where the literals in the original and then extended clause are assigned
// to false, and the literals on the trail (actually we use our own 'added'
// stack for that) make up the extended clause. The ALA steps can be
// implemented by simple propagation (copied from 'propagate.cpp') using
// watches, while the CLA steps need full occurrence lists to determine the
// resolution candidate clauses. The CCE is successful if a conflict is
// found during ALA steps or if during a CLA step all resolution candidates
// of a literal on the trail are satisfied (the extended clause is blocked).
struct Coveror {
std::vector<int> added; // acts as trail
std::vector<int> extend; // extension stack for witness
std::vector<int> covered; // clause literals or added through CLA
std::vector<int> intersection; // of literals in resolution candidates
size_t alas, clas; // actual number of ALAs and CLAs
struct {
size_t added, covered;
} next; // propagate next ...
Coveror () : alas (0), clas (0) {}
};
/*------------------------------------------------------------------------*/
// Push on the extension stack a clause made up of the given literal, the
// original clause (initially copied to 'covered') and all the added covered
// literals so far. The given literal will act as blocking literal for that
// clause, if CCE is successful. Only in this case, this private extension
// stack is copied to the actual extension stack of the solver. Note, that
// even though all 'added' clauses correspond to the extended clause, we
// only need to save the original and added covered literals.
inline void Internal::cover_push_extension (int lit, Coveror &coveror) {
coveror.extend.push_back (0);
coveror.extend.push_back (lit); // blocking literal comes first
bool found = false;
for (const auto &other : coveror.covered)
if (lit == other)
CADICAL_assert (!found), found = true;
else
coveror.extend.push_back (other);
CADICAL_assert (found);
(void) found;
}
// Successful covered literal addition (CLA) step.
inline void Internal::covered_literal_addition (int lit, Coveror &coveror) {
require_mode (COVER);
CADICAL_assert (level == 1);
cover_push_extension (lit, coveror);
for (const auto &other : coveror.intersection) {
LOG ("covered literal addition %d", other);
CADICAL_assert (!vals[other]), CADICAL_assert (!vals[-other]);
set_val (other, -1);
coveror.covered.push_back (other);
coveror.added.push_back (other);
coveror.clas++;
}
coveror.next.covered = 0;
}
// Successful asymmetric literal addition (ALA) step.
inline void Internal::asymmetric_literal_addition (int lit,
Coveror &coveror) {
require_mode (COVER);
CADICAL_assert (level == 1);
LOG ("initial asymmetric literal addition %d", lit);
CADICAL_assert (!vals[lit]), CADICAL_assert (!vals[-lit]);
set_val (lit, -1);
coveror.added.push_back (lit);
coveror.alas++;
coveror.next.covered = 0;
}
/*------------------------------------------------------------------------*/
// In essence copied and adapted from 'propagate' in 'propagate.cpp'. Since
// this function is also a hot-spot here in 'cover' we specialize it in the
// same spirit as 'probe_propagate' and 'vivify_propagate'. Please refer to
// the detailed comments for 'propagate' in 'propagate.cpp' for details.
bool Internal::cover_propagate_asymmetric (int lit, Clause *ignore,
Coveror &coveror) {
require_mode (COVER);
stats.propagations.cover++;
CADICAL_assert (val (lit) < 0);
bool subsumed = false;
LOG ("asymmetric literal propagation of %d", lit);
Watches &ws = watches (lit);
const const_watch_iterator eow = ws.end ();
watch_iterator j = ws.begin ();
const_watch_iterator i = j;
while (!subsumed && i != eow) {
const Watch w = *j++ = *i++;
if (w.clause == ignore)
continue; // costly but necessary here ...
const signed char b = val (w.blit);
if (b > 0)
continue;
if (w.clause->garbage)
j--;
else if (w.binary ()) {
if (b < 0) {
LOG (w.clause, "found subsuming");
subsumed = true;
} else
asymmetric_literal_addition (-w.blit, coveror);
} else {
literal_iterator lits = w.clause->begin ();
const int other = lits[0] ^ lits[1] ^ lit;
lits[0] = other, 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", lit);
lits[1] = r;
*k = lit;
watch_literal (r, lit, w.clause);
j--;
} else if (!u) {
CADICAL_assert (v < 0);
asymmetric_literal_addition (-other, coveror);
} else {
CADICAL_assert (u < 0), CADICAL_assert (v < 0);
LOG (w.clause, "found subsuming");
subsumed = true;
break;
}
}
}
}
if (j != i) {
while (i != eow)
*j++ = *i++;
ws.resize (j - ws.begin ());
}
return subsumed;
}
// Covered literal addition (which needs full occurrence lists). The
// function returns 'true' if the extended clause is blocked on 'lit.'
bool Internal::cover_propagate_covered (int lit, Coveror &coveror) {
require_mode (COVER);
CADICAL_assert (val (lit) < 0);
if (frozen (lit)) {
LOG ("no covered propagation on frozen literal %d", lit);
return false;
}
stats.propagations.cover++;
LOG ("covered propagation of %d", lit);
CADICAL_assert (coveror.intersection.empty ());
Occs &os = occs (-lit);
const auto end = os.end ();
bool first = true;
// Compute the intersection of the literals in all the clauses with
// '-lit'. If all these clauses are double satisfied then we know that
// the extended clauses (in 'added') is blocked. All literals in the
// intersection can be added as covered literal. As soon the intersection
// becomes empty (during traversal of clauses with '-lit') we abort.
for (auto i = os.begin (); i != end; i++) {
Clause *c = *i;
if (c->garbage)
continue;
// First check whether clause is 'blocked', i.e., is double satisfied.
bool blocked = false;
for (const auto &other : *c) {
if (other == -lit)
continue;
const signed char tmp = val (other);
if (tmp < 0)
continue;
if (tmp > 0) {
blocked = true;
break;
}
}
if (blocked) { // ... if 'c' is double satisfied.
LOG (c, "blocked");
continue; // with next clause with '-lit'.
}
if (first) {
// Copy and mark literals of first clause.
for (const auto &other : *c) {
if (other == -lit)
continue;
const signed char tmp = val (other);
if (tmp < 0)
continue;
CADICAL_assert (!tmp);
coveror.intersection.push_back (other);
mark (other);
}
first = false;
} else {
// Unmark all literals in current clause.
for (const auto &other : *c) {
if (other == -lit)
continue;
signed char tmp = val (other);
if (tmp < 0)
continue;
CADICAL_assert (!tmp);
tmp = marked (other);
if (tmp > 0)
unmark (other);
}
// Then remove from intersection all marked literals.
const auto end = coveror.intersection.end ();
auto j = coveror.intersection.begin ();
for (auto k = j; k != end; k++) {
const int other = *j++ = *k;
const int tmp = marked (other);
CADICAL_assert (tmp >= 0);
if (tmp)
j--, unmark (other); // remove marked and unmark it
else
mark (other); // keep unmarked and mark it
}
const size_t new_size = j - coveror.intersection.begin ();
coveror.intersection.resize (new_size);
if (!coveror.intersection.empty ())
continue;
// No covered literal addition candidates in the intersection left!
// Move this clause triggering early abort to the beginning.
// This is a common move to front strategy to minimize effort.
auto begin = os.begin ();
while (i != begin) {
auto prev = i - 1;
*i = *prev;
i = prev;
}
*begin = c;
break; // early abort ...
}
}
bool res = false;
if (first) {
LOG ("all resolution candidates with %d blocked", -lit);
CADICAL_assert (coveror.intersection.empty ());
cover_push_extension (lit, coveror);
res = true;
} else if (coveror.intersection.empty ()) {
LOG ("empty intersection of resolution candidate literals");
} else {
LOG (coveror.intersection,
"non-empty intersection of resolution candidate literals");
covered_literal_addition (lit, coveror);
unmark (coveror.intersection);
coveror.intersection.clear ();
coveror.next.covered = 0; // Restart covering.
}
unmark (coveror.intersection);
coveror.intersection.clear ();
return res;
}
/*------------------------------------------------------------------------*/
bool Internal::cover_clause (Clause *c, Coveror &coveror) {
require_mode (COVER);
CADICAL_assert (!c->garbage);
LOG (c, "trying covered clauses elimination on");
bool satisfied = false;
for (const auto &lit : *c)
if (val (lit) > 0)
satisfied = true;
if (satisfied) {
LOG (c, "clause already satisfied");
mark_garbage (c);
return false;
}
CADICAL_assert (coveror.added.empty ());
CADICAL_assert (coveror.extend.empty ());
CADICAL_assert (coveror.covered.empty ());
CADICAL_assert (!level);
level = 1;
LOG ("assuming literals of candidate clause");
for (const auto &lit : *c) {
if (val (lit))
continue;
asymmetric_literal_addition (lit, coveror);
coveror.covered.push_back (lit);
}
bool tautological = false;
coveror.next.added = coveror.next.covered = 0;
while (!tautological) {
if (coveror.next.added < coveror.added.size ()) {
const int lit = coveror.added[coveror.next.added++];
tautological = cover_propagate_asymmetric (lit, c, coveror);
} else if (coveror.next.covered < coveror.covered.size ()) {
const int lit = coveror.covered[coveror.next.covered++];
tautological = cover_propagate_covered (lit, coveror);
} else
break;
}
if (tautological) {
if (coveror.extend.empty ()) {
stats.cover.asymmetric++;
stats.cover.total++;
LOG (c, "asymmetric tautological");
} else {
stats.cover.blocked++;
stats.cover.total++;
// Only copy extension stack if successful.
int prev = INT_MIN;
bool already_pushed = false;
int64_t last_id = 0;
LOG (c, "covered tautological");
CADICAL_assert (clause.empty ());
LOG (coveror.extend, "extension = ");
for (const auto &other : coveror.extend) {
if (!prev) {
// are we finishing a clause?
if (already_pushed) {
// add missing literals that are not needed for covering
// but avoid RAT proofs
for (auto i = 0, j = 0; i < c->size; ++i, ++j) {
const int lit = c->literals[i];
if (j >= (int) coveror.covered.size () ||
c->literals[i] != coveror.covered[j]) {
--j;
LOG ("adding lit %d not needed for ATA", lit);
clause.push_back (lit);
external->push_clause_literal_on_extension_stack (lit);
}
}
}
if (proof && already_pushed) {
if (lrat)
lrat_chain.push_back (c->id);
LOG ("LEARNING clause with id %" PRId64, last_id);
proof->add_derived_clause (last_id, false, clause, lrat_chain);
proof->weaken_plus (last_id, clause);
lrat_chain.clear ();
}
last_id = ++clause_id;
external->push_zero_on_extension_stack ();
external->push_witness_literal_on_extension_stack (other);
external->push_zero_on_extension_stack ();
external->push_id_on_extension_stack (last_id);
external->push_zero_on_extension_stack ();
clause.clear ();
already_pushed = true;
}
if (other) {
external->push_clause_literal_on_extension_stack (other);
clause.push_back (other);
LOG (clause, "current clause is");
}
prev = other;
}
if (proof) {
// add missing literals that are not needed for covering
// but avoid RAT proofs
for (auto i = 0, j = 0; i < c->size; ++i, ++j) {
const int lit = c->literals[i];
if (j >= (int) coveror.covered.size () ||
c->literals[i] != coveror.covered[j]) {
--j;
LOG ("adding lit %d not needed for ATA", lit);
clause.push_back (lit);
external->push_clause_literal_on_extension_stack (lit);
}
}
if (lrat)
lrat_chain.push_back (c->id);
proof->add_derived_clause (last_id, false, clause, lrat_chain);
proof->weaken_plus (last_id, clause);
lrat_chain.clear ();
}
clause.clear ();
mark_garbage (c);
}
}
// Backtrack and 'unassign' all literals.
CADICAL_assert (level == 1);
for (const auto &lit : coveror.added)
set_val (lit, 0);
level = 0;
coveror.covered.clear ();
coveror.extend.clear ();
coveror.added.clear ();
return tautological;
}
/*------------------------------------------------------------------------*/
// Not yet tried and larger clauses are tried first.
struct clause_covered_or_smaller {
bool operator() (const Clause *a, const Clause *b) {
if (a->covered && !b->covered)
return true;
if (!a->covered && b->covered)
return false;
return a->size < b->size;
}
};
int64_t Internal::cover_round () {
if (unsat)
return 0;
init_watches ();
connect_watches (true); // irredundant watches only is enough
int64_t delta = stats.propagations.search;
delta *= 1e-3 * opts.covereffort;
if (delta < opts.covermineff)
delta = opts.covermineff;
if (delta > opts.covermaxeff)
delta = opts.covermaxeff;
delta = max (delta, ((int64_t) 2) * active ());
PHASE ("cover", stats.cover.count,
"covered clause elimination limit of %" PRId64 " propagations",
delta);
int64_t limit = stats.propagations.cover + delta;
init_occs ();
vector<Clause *> schedule;
Coveror coveror;
// First connect all clauses and find all not yet tried clauses.
//
#ifndef CADICAL_QUIET
int64_t untried = 0;
#endif
//
for (auto c : clauses) {
CADICAL_assert (!c->frozen);
if (c->garbage)
continue;
if (c->redundant)
continue;
bool satisfied = false, allfrozen = true;
for (const auto &lit : *c)
if (val (lit) > 0) {
satisfied = true;
break;
} else if (allfrozen && !frozen (lit))
allfrozen = false;
if (satisfied) {
mark_garbage (c);
continue;
}
if (allfrozen) {
c->frozen = true;
continue;
}
for (const auto &lit : *c)
occs (lit).push_back (c);
if (c->size < opts.coverminclslim)
continue;
if (c->size > opts.covermaxclslim)
continue;
if (c->covered)
continue;
schedule.push_back (c);
#ifndef CADICAL_QUIET
untried++;
#endif
}
if (schedule.empty ()) {
PHASE ("cover", stats.cover.count, "no previously untried clause left");
for (auto c : clauses) {
if (c->garbage)
continue;
if (c->redundant)
continue;
if (c->frozen) {
c->frozen = false;
continue;
}
if (c->size < opts.coverminclslim)
continue;
if (c->size > opts.covermaxclslim)
continue;
CADICAL_assert (c->covered);
c->covered = false;
schedule.push_back (c);
}
} else { // Mix of tried and not tried clauses ....
for (auto c : clauses) {
if (c->garbage)
continue;
if (c->redundant)
continue;
if (c->frozen) {
c->frozen = false;
continue;
}
if (c->size < opts.coverminclslim)
continue;
if (c->size > opts.covermaxclslim)
continue;
if (!c->covered)
continue;
schedule.push_back (c);
}
}
stable_sort (schedule.begin (), schedule.end (),
clause_covered_or_smaller ());
#ifndef CADICAL_QUIET
const size_t scheduled = schedule.size ();
PHASE ("cover", stats.cover.count,
"scheduled %zd clauses %.0f%% with %" PRId64 " untried %.0f%%",
scheduled, percent (scheduled, stats.current.irredundant), untried,
percent (untried, scheduled));
#endif
// Heuristically it should be beneficial to intersect with smaller clauses
// first, since then the chances are higher that the intersection of
// resolution candidates becomes emptier earlier.
for (auto lit : lits) {
if (!active (lit))
continue;
Occs &os = occs (lit);
stable_sort (os.begin (), os.end (), clause_smaller_size ());
}
// This is the main loop of trying to do CCE of candidate clauses.
//
int64_t covered = 0;
//
while (!terminated_asynchronously () && !schedule.empty () &&
stats.propagations.cover < limit) {
Clause *c = schedule.back ();
schedule.pop_back ();
c->covered = true;
if (cover_clause (c, coveror))
covered++;
}
#ifndef CADICAL_QUIET
const size_t remain = schedule.size ();
const size_t tried = scheduled - remain;
PHASE ("cover", stats.cover.count,
"eliminated %" PRId64 " covered clauses out of %zd tried %.0f%%",
covered, tried, percent (covered, tried));
if (remain)
PHASE ("cover", stats.cover.count,
"remaining %zu clauses %.0f%% untried", remain,
percent (remain, scheduled));
else
PHASE ("cover", stats.cover.count, "all scheduled clauses tried");
#endif
reset_occs ();
reset_watches ();
return covered;
}
/*------------------------------------------------------------------------*/
bool Internal::cover () {
if (!opts.cover)
return false;
if (unsat)
return false;
if (terminated_asynchronously ())
return false;
if (!stats.current.irredundant)
return false;
// TODO: Our current algorithm for producing the necessary clauses on the
// reconstruction stack for extending the witness requires a covered
// literal addition step which (empirically) conflicts with flushing
// during restoring clauses (see 'regr00{48,51}.trace') even though
// flushing during restore is disabled by default (as is covered clause
// elimination). The consequence of combining these two options
// ('opts.cover' and 'opts.restoreflush') can thus produce incorrect
// witness reconstruction and thus invalid witnesses. This is quite
// infrequent (one out of half billion mobical test cases) but as the two
// regression traces show, does happen. Thus we disable the combination.
//
if (opts.restoreflush)
return false;
START_SIMPLIFIER (cover, COVER);
stats.cover.count++;
// During variable elimination unit clauses can be generated which need to
// be propagated properly over redundant clauses too. Since variable
// elimination avoids to have occurrence lists and watches at the same
// time this propagation is delayed until the end of variable elimination.
// Since we want to interleave CCE with it, we have to propagate here.
// Otherwise this triggers inconsistencies.
//
if (propagated < trail.size ()) {
init_watches ();
connect_watches (); // need to propagated over all clauses!
LOG ("elimination produced %zd units",
(size_t) (trail.size () - propagated));
if (!propagate ()) {
LOG ("propagating units before covered clause elimination "
"results in empty clause");
learn_empty_clause ();
CADICAL_assert (unsat);
}
reset_watches ();
}
CADICAL_assert (unsat || propagated == trail.size ());
int64_t covered = cover_round ();
STOP_SIMPLIFIER (cover, COVER);
report ('c', !opts.reportall && !covered);
return covered;
}
} // namespace CaDiCaL
ABC_NAMESPACE_IMPL_END

View File

@ -1,258 +0,0 @@
#include "global.h"
#include "internal.hpp"
ABC_NAMESPACE_IMPL_START
namespace CaDiCaL {
// This function determines the next decision variable on the queue, without
// actually removing it from the decision queue, e.g., calling it multiple
// times without any assignment will return the same result. This is of
// course used below in 'decide' but also in 'reuse_trail' to determine the
// largest decision level to backtrack to during 'restart' without changing
// the assigned variables (if 'opts.restartreusetrail' is non-zero).
int Internal::next_decision_variable_on_queue () {
int64_t searched = 0;
int res = queue.unassigned;
while (val (res))
res = link (res).prev, searched++;
if (searched) {
stats.searched += searched;
update_queue_unassigned (res);
}
LOG ("next queue decision variable %d bumped %" PRId64 "", res,
bumped (res));
return res;
}
// This function determines the best decision with respect to score.
//
int Internal::next_decision_variable_with_best_score () {
int res = 0;
for (;;) {
res = scores.front ();
if (!val (res))
break;
(void) scores.pop_front ();
}
LOG ("next decision variable %d with score %g", res, score (res));
return res;
}
int Internal::next_decision_variable () {
if (use_scores ())
return next_decision_variable_with_best_score ();
else
return next_decision_variable_on_queue ();
}
/*------------------------------------------------------------------------*/
// Implements phase saving as well using a target phase during
// stabilization unless decision phase is forced to the initial value
// of a phase is forced through the 'phase' option.
int Internal::decide_phase (int idx, bool target) {
const int initial_phase = opts.phase ? 1 : -1;
int phase = 0;
if (force_saved_phase)
phase = phases.saved[idx];
if (!phase)
phase = phases.forced[idx]; // swapped with opts.forcephase case!
if (!phase && opts.forcephase)
phase = initial_phase;
if (!phase && target)
phase = phases.target[idx];
if (!phase)
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
// triggered for some users and we could not get to the root cause of
// 'phase' still not being set here. The logic for phase and target
// saving is pretty complex, particularly in combination with local
// search, and to avoid running in such an issue in the future again, we
// now use this 'defensive' code here, even though such defensive code is
// considered bad programming practice.
//
if (!phase)
phase = initial_phase;
return phase * idx;
}
// The likely phase of an variable used in 'collect' for optimizing
// co-location of clauses likely accessed together during search.
int Internal::likely_phase (int idx) { return decide_phase (idx, false); }
/*------------------------------------------------------------------------*/
// adds new level to control and trail
//
void Internal::new_trail_level (int lit) {
level++;
control.push_back (Level (lit, trail.size ()));
}
/*------------------------------------------------------------------------*/
bool Internal::satisfied () {
if ((size_t) level < assumptions.size () + (!!constraint.size ()))
return false;
if (num_assigned < (size_t) max_var)
return false;
CADICAL_assert (num_assigned == (size_t) max_var);
if (propagated < trail.size ())
return false;
size_t assigned = num_assigned;
return (assigned == (size_t) max_var);
}
bool Internal::better_decision (int lit, int other) {
int lit_idx = abs (lit);
int other_idx = abs (other);
if (stable)
return stab[lit_idx] > stab[other_idx];
else
return btab[lit_idx] > btab[other_idx];
}
// Search for the next decision and assign it to the saved phase. Requires
// that not all variables are assigned.
int Internal::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");
notify_decision ();
} 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 {
int decision = ask_decision ();
if ((size_t) level < assumptions.size () ||
((size_t) level == assumptions.size () && constraint.size ())) {
// Forced backtrack below pseudo decision levels.
// So one of the two branches above will handle it.
STOP (decide);
res = decide (); // STARTS and STOPS profiling
START (decide);
} else {
stats.decisions++;
if (!decision) {
int idx = next_decision_variable ();
const bool target = (opts.target > 1 || (stable && opts.target));
decision = decide_phase (idx, target);
}
search_assume_decision (decision);
}
}
if (res)
marked_failed = false;
STOP (decide);
return res;
}
} // namespace CaDiCaL
ABC_NAMESPACE_IMPL_END

View File

@ -1,739 +0,0 @@
#include "global.h"
#include "internal.hpp"
ABC_NAMESPACE_IMPL_START
namespace CaDiCaL {
void Internal::decompose_analyze_binary_chain (DFS *dfs, int from) {
if (!lrat)
return;
LOG ("binary chain starting at %d", from);
DFS &from_dfs = dfs[vlit (from)];
Clause *reason = from_dfs.parent;
if (!reason)
return;
CADICAL_assert (reason->size == 2);
mini_chain.push_back (reason->id);
int other = reason->literals[0];
other = other == from ? -reason->literals[1] : -other;
Flags &f = flags (other);
if (f.seen)
return;
f.seen = true;
analyzed.push_back (other);
decompose_analyze_binary_chain (dfs, other);
}
vector<Clause *> Internal::decompose_analyze_binary_clauses (DFS *dfs,
int from) {
vector<Clause *> result;
LOG ("binary chain starting at %d", from);
DFS &from_dfs = dfs[vlit (from)];
Clause *reason = from_dfs.parent;
while (reason) {
result.push_back (reason);
CADICAL_assert (reason->size == 2);
int other = reason->literals[0];
other = other == from ? -reason->literals[1] : -other;
Flags &f = flags (other);
if (f.seen)
break;
f.seen = true;
analyzed.push_back (other);
from = other;
DFS &from_dfs = dfs[vlit (from)];
reason = from_dfs.parent;
}
return result;
}
void Internal::decompose_conflicting_scc_lrat (DFS *dfs, vector<int> &scc) {
if (!lrat)
return;
CADICAL_assert (lrat_chain.empty ());
CADICAL_assert (mini_chain.empty ());
for (auto &lit : scc) {
Flags &f = flags (lit);
if (f.seen)
return;
f.seen = true;
analyzed.push_back (lit);
decompose_analyze_binary_chain (dfs, lit);
for (auto p = mini_chain.rbegin (); p != mini_chain.rend (); p++) {
lrat_chain.push_back (*p);
}
mini_chain.clear ();
}
clear_analyzed_literals ();
}
void Internal::build_lrat_for_clause (
const vector<vector<Clause *>> &dfs_chains, bool invert) {
CADICAL_assert (lrat);
LOG ("building chain for not subsumed clause");
CADICAL_assert (lrat_chain.empty ());
CADICAL_assert (sign_marked.empty ());
// build chain for each replaced literal
for (const auto lit : clause) {
auto other = lit;
if (val (other) > 0) {
if (marked_decomposed (other))
continue;
mark_decomposed (other);
int64_t id = unit_id (other);
lrat_chain.push_back (id);
continue;
}
CADICAL_assert (mini_chain.empty ());
for (auto p : dfs_chains[vlit (other)]) {
if (marked_decomposed (other))
continue;
mark_decomposed (other);
int implied = p->literals[0];
implied = implied == other ? -p->literals[1] : -implied;
LOG ("ADDED %d -> %d (%" PRId64 ")", implied, other, p->id);
other = implied;
mini_chain.push_back (p->id);
if (val (implied) <= 0)
continue;
if (marked_decomposed (implied))
break;
mark_decomposed (implied);
int64_t id = unit_id (implied);
mini_chain.push_back (id);
break;
}
if (invert)
for (auto p = mini_chain.rbegin (); p != mini_chain.rend (); p++)
lrat_chain.push_back (*p);
else
for (auto p = mini_chain.begin (); p != mini_chain.end (); p++)
lrat_chain.push_back (*p);
mini_chain.clear ();
}
clear_sign_marked_literals ();
LOG (lrat_chain, "lrat_chain:");
}
void Internal::clear_sign_marked_literals () {
LOG ("clearing %zd marked literals", sign_marked.size ());
for (const auto &lit : sign_marked) {
// CADICAL_assert (marked_signed (lit)); violated on purpose in factor
unmark_decomposed (lit);
}
sign_marked.clear ();
}
// This performs one round of Tarjan's algorithm, e.g., equivalent literal
// detection and substitution, on the whole formula. We might want to
// repeat it since its application might produce new binary clauses or
// units. Such units might even result in an empty clause.
bool Internal::decompose_round () {
if (!opts.decompose)
return false;
if (unsat)
return false;
if (terminated_asynchronously ())
return false;
CADICAL_assert (!level);
START_SIMPLIFIER (decompose, DECOMP);
stats.decompositions++;
const size_t size_dfs = 2 * (1 + (size_t) max_var);
DFS *dfs = new DFS[size_dfs];
DeferDeleteArray<DFS> dfs_delete (dfs);
int *reprs = new int[size_dfs];
DeferDeleteArray<int> reprs_delete (reprs);
clear_n (reprs, size_dfs);
vector<vector<Clause *>> dfs_chains;
dfs_chains.resize (size_dfs);
if (lrat) {
for (size_t i = 0; i > size_dfs; i++) {
vector<Clause *> empty;
dfs_chains[i] = empty;
}
}
int substituted = 0;
#ifndef CADICAL_QUIET
int non_trivial_sccs = 0;
int before = active ();
#endif
unsigned dfs_idx = 0;
vector<int> work; // depth first search working stack
vector<int> scc; // collects members of one SCC
// The binary implication graph might have disconnected components and
// thus we have in general to start several depth first searches.
for (auto root_idx : vars) {
if (unsat)
break;
if (!active (root_idx))
continue;
for (int root_sign = -1; !unsat && root_sign <= 1; root_sign += 2) {
int root = root_sign * root_idx;
if (dfs[vlit (root)].min == TRAVERSED)
continue; // skip traversed
LOG ("new dfs search starting at root %d", root);
CADICAL_assert (work.empty ());
CADICAL_assert (scc.empty ());
work.push_back (root);
while (!unsat && !work.empty ()) {
int parent = work.back ();
DFS &parent_dfs = dfs[vlit (parent)];
if (parent_dfs.min == TRAVERSED) { // skip traversed
CADICAL_assert (reprs[vlit (parent)]);
work.pop_back ();
} else {
CADICAL_assert (!reprs[vlit (parent)]);
// Go over all implied literals, thus need to iterate over all
// binary watched clauses with the negation of 'parent'.
Watches &ws = watches (-parent);
// Two cases: Either the node has never been visited before, i.e.,
// it's depth first search index is zero, then perform the
// 'pre-fix' work before visiting it's children. Otherwise all
// it's children and nodes reachable from those children have been
// visited and their minimum reachable depth first search index
// has been computed. This second case is the 'post-fix' work.
if (parent_dfs.idx) { // post-fix
work.pop_back (); // 'parent' done
// Get the minimum reachable depth first search index reachable
// from the children of 'parent'.
unsigned new_min = parent_dfs.min;
for (const auto &w : ws) {
if (!w.binary ())
continue;
const int child = w.blit;
if (!active (child))
continue;
DFS &child_dfs = dfs[vlit (child)];
if (new_min > child_dfs.min)
new_min = child_dfs.min;
}
LOG ("post-fix work dfs search %d index %u reaches minimum %u",
parent, parent_dfs.idx, new_min);
if (parent_dfs.idx == new_min) { // entry to SCC
// All nodes on the 'scc' stack after and including 'parent'
// are in the same SCC. Their representative is computed as
// the smallest literal (index-wise) in the SCC. If the SCC
// contains both a literal and its negation, then the formula
// becomes unsatisfiable.
if (lrat) {
CADICAL_assert (analyzed.empty ());
int other, first = 0;
bool conflicting = false;
size_t j = scc.size ();
do {
CADICAL_assert (j > 0);
other = scc[--j];
if (!first || vlit (other) < vlit (first))
first = other;
Flags &f = flags (other);
if (other == -parent) {
conflicting = true; // conflicting scc
}
if (f.seen) {
continue; // also conflicting scc
}
f.seen = true;
analyzed.push_back (other);
} while (other != parent);
CADICAL_assert (!conflicting || first > 0);
vector<int> to_justify;
if (conflicting) {
LOG ("conflicting scc simulating up at %d", parent);
to_justify.push_back (-parent);
} else
to_justify.push_back (first);
while (!to_justify.empty ()) {
const int next = to_justify.back ();
to_justify.pop_back ();
Watches &next_ws = watches (-next);
for (const auto &w : next_ws) {
if (!w.binary ())
continue;
const int child = w.blit;
if (!active (child))
continue;
if (!flags (child).seen)
continue;
DFS &child_dfs = dfs[vlit (child)];
if (child_dfs.parent)
continue;
child_dfs.parent = w.clause;
to_justify.push_back (child);
}
}
clear_analyzed_literals ();
}
int other, repr = parent;
#ifndef CADICAL_QUIET
int size = 0;
#endif
CADICAL_assert (!scc.empty ());
size_t j = scc.size ();
do {
CADICAL_assert (j > 0);
other = scc[--j];
if (other == -parent) {
LOG ("both %d and %d in one SCC", parent, -parent);
if (lrat) {
Flags &f = flags (-parent);
f.seen = true;
analyzed.push_back (-parent);
decompose_analyze_binary_chain (dfs, parent);
for (auto p : mini_chain)
lrat_chain.push_back (p);
mini_chain.clear ();
}
assign_unit (parent);
#ifndef CADICAL_NDEBUG
bool ok =
#endif
propagate ();
CADICAL_assert (!ok);
learn_empty_clause ();
lrat_chain.clear ();
} else {
if (abs (other) < abs (repr))
repr = other;
#ifndef CADICAL_QUIET
size++;
#endif
}
} while (!unsat && other != parent);
if (unsat)
break;
#ifndef CADICAL_QUIET
LOG ("SCC of representative %d of size %d", repr, size);
#endif
do {
CADICAL_assert (!scc.empty ());
other = scc.back ();
scc.pop_back ();
dfs[vlit (other)].min = TRAVERSED;
if (frozen (other)) {
reprs[vlit (other)] = other;
continue;
}
reprs[vlit (other)] = repr;
if (other == repr)
continue;
substituted++;
LOG ("literal %d in SCC of %d", other, repr);
if (!lrat)
continue;
CADICAL_assert (mini_chain.empty ());
Flags &f = flags (repr);
f.seen = true;
analyzed.push_back (repr);
// no need to reverse dfs_chain because this is handled by
// build_lrat_for_clause.
dfs_chains[vlit (other)] =
decompose_analyze_binary_clauses (dfs, other);
clear_analyzed_literals ();
} while (other != parent);
#ifndef CADICAL_QUIET
if (size > 1)
non_trivial_sccs++;
#endif
} else {
// Current node 'parent' is in a non-trivial SCC but is not
// the entry point of the SCC in this depth first search, so
// keep it on the SCC stack until the entry point is reached.
parent_dfs.min = new_min;
}
} else { // pre-fix
dfs_idx++;
CADICAL_assert (dfs_idx < TRAVERSED);
parent_dfs.idx = parent_dfs.min = dfs_idx;
scc.push_back (parent);
LOG ("pre-fix work dfs search %d index %u", parent, dfs_idx);
// Now traverse all the children in the binary implication
// graph but keep 'parent' on the stack for 'post-fix' work.
for (const auto &w : ws) {
if (!w.binary ())
continue;
const int child = w.blit;
if (!active (child))
continue;
DFS &child_dfs = dfs[vlit (child)];
if (child_dfs.idx)
continue;
work.push_back (child);
}
}
}
}
}
}
erase_vector (work);
erase_vector (scc);
// delete [] dfs; need to postpone until after changing clauses...
// Only keep the representatives 'repr' mapping.
PHASE ("decompose", stats.decompositions,
"%d non-trivial sccs, %d substituted %.2f%%", non_trivial_sccs,
substituted, percent (substituted, before));
bool new_unit = false, new_binary_clause = false;
// Finally, mark substituted literals as such and push the equivalences of
// the substituted literals to their representative on the extension
// stack to fix an assignment during 'extend'.
// It is also necessary to do so for proper IDRUP/LIDRUP/Resolution proofs
vector<int64_t> decompose_ids;
const size_t size = 2 * (1 + (size_t) max_var);
decompose_ids.resize (size);
for (auto idx : vars) {
if (!substituted)
break;
if (unsat)
break;
if (!active (idx))
continue;
int other = reprs[vlit (idx)];
if (other == idx)
continue;
CADICAL_assert (!flags (other).eliminated ());
CADICAL_assert (!flags (other).substituted ());
LOG ("marking equivalence of %d and %d", idx, other);
CADICAL_assert (clause.empty ());
CADICAL_assert (lrat_chain.empty ());
clause.push_back (other);
clause.push_back (-idx);
if (lrat) {
build_lrat_for_clause (dfs_chains);
CADICAL_assert (!lrat_chain.empty ());
}
const int64_t id1 = ++clause_id;
if (proof) {
proof->add_derived_clause (id1, false, clause, lrat_chain);
proof->weaken_minus (id1, clause);
}
external->push_binary_clause_on_extension_stack (id1, -idx, other);
decompose_ids[vlit (-idx)] = id1;
lrat_chain.clear ();
clause.clear ();
CADICAL_assert (clause.empty ());
CADICAL_assert (lrat_chain.empty ());
clause.push_back (idx);
clause.push_back (-other);
if (lrat) {
build_lrat_for_clause (dfs_chains);
CADICAL_assert (!lrat_chain.empty ());
}
const int64_t id2 = ++clause_id;
if (proof) {
proof->add_derived_clause (id2, false, clause, lrat_chain);
proof->weaken_minus (id2, clause);
}
external->push_binary_clause_on_extension_stack (id2, idx, -other);
decompose_ids[vlit (idx)] = id2;
clause.clear ();
lrat_chain.clear ();
}
vector<Clause *> postponed_garbage;
// Now go over all clauses and find clause which contain literals that
// should be substituted by their representative.
size_t clauses_size = clauses.size ();
#ifndef CADICAL_QUIET
size_t garbage = 0, replaced = 0;
#endif
for (size_t i = 0; substituted && !unsat && i < clauses_size; i++) {
Clause *c = clauses[i];
if (c->garbage)
continue;
int j, size = c->size;
for (j = 0; j < size; j++) {
const int lit = c->literals[j];
if (reprs[vlit (lit)] != lit)
break;
}
if (j == size)
continue;
#ifndef CADICAL_QUIET
replaced++;
#endif
LOG (c, "first substituted literal %d in", substituted);
// Now copy the result to 'clause'. Substitute literals if they have a
// different representative. Skip duplicates and false literals. If a
// literal occurs in both phases or is assigned to true the clause is
// satisfied and can be marked as garbage.
CADICAL_assert (clause.empty ());
CADICAL_assert (lrat_chain.empty ());
CADICAL_assert (analyzed.empty ());
bool satisfied = false;
for (int k = 0; !satisfied && k < size; k++) {
const int lit = c->literals[k];
signed char tmp = val (lit);
if (tmp > 0)
satisfied = true;
else if (tmp < 0) {
if (!lrat)
continue;
Flags &f = flags (lit);
if (f.seen)
continue;
f.seen = true;
analyzed.push_back (lit);
int64_t id = unit_id (-lit);
lrat_chain.push_back (id);
continue;
} else {
const int other = reprs[vlit (lit)];
tmp = val (other);
if (tmp < 0) {
if (!lrat)
continue;
Flags &f = flags (other);
if (!f.seen) {
f.seen = true;
analyzed.push_back (other);
int64_t id = unit_id (-other);
lrat_chain.push_back (id);
}
if (other == lit)
continue;
int64_t id = decompose_ids[vlit (-lit)];
CADICAL_assert (id);
lrat_chain.push_back (id);
continue;
} else if (tmp > 0)
satisfied = true;
else {
tmp = marked (other);
if (tmp < 0)
satisfied = true;
else if (!tmp) {
mark (other);
clause.push_back (other);
}
if (other == lit)
continue;
if (!lrat)
continue;
int64_t id = decompose_ids[vlit (-lit)];
CADICAL_assert (id);
lrat_chain.push_back (id);
}
}
}
if (lrat)
lrat_chain.push_back (c->id);
clear_analyzed_literals ();
LOG (lrat_chain, "lrat_chain:");
if (satisfied) {
LOG (c, "satisfied after substitution (postponed)");
postponed_garbage.push_back (c);
#ifndef CADICAL_QUIET
garbage++;
#endif
} else if (!clause.size ()) {
LOG ("learned empty clause during decompose");
learn_empty_clause ();
} else if (clause.size () == 1) {
LOG (c, "unit %d after substitution", clause[0]);
assign_unit (clause[0]);
mark_garbage (c);
new_unit = true;
#ifndef CADICAL_QUIET
garbage++;
#endif
} else if (c->literals[0] != clause[0] || c->literals[1] != clause[1]) {
LOG ("need new clause since at least one watched literal changed");
if (clause.size () == 2)
new_binary_clause = true;
size_t d_clause_idx = clauses.size ();
Clause *d = new_clause_as (c);
CADICAL_assert (clauses[d_clause_idx] == d);
clauses[d_clause_idx] = c;
clauses[i] = d;
mark_garbage (c);
#ifndef CADICAL_QUIET
garbage++;
#endif
} else {
LOG ("simply shrinking clause since watches did not change");
CADICAL_assert (c->size > 2);
if (!c->redundant)
mark_removed (c);
if (proof) {
proof->add_derived_clause (++clause_id, c->redundant, clause,
lrat_chain);
proof->delete_clause (c);
c->id = clause_id;
}
size_t l;
int *literals = c->literals;
for (l = 2; l < clause.size (); l++)
literals[l] = clause[l];
int flushed = c->size - (int) l;
if (flushed) {
if (l == 2)
new_binary_clause = true;
LOG ("flushed %d literals", flushed);
(void) shrink_clause (c, l);
} else if (likely_to_be_kept_clause (c))
mark_added (c);
// we have shrunken c->size to l so even though there is an CADICAL_assertion
// for c->size > 2 at the beginning of this else block, the new size
// can be 2 now.
if (c->size == 2) { // cheaper to update only new binary clauses
CADICAL_assert (new_binary_clause);
update_watch_size (watches (c->literals[0]), c->literals[1], c);
update_watch_size (watches (c->literals[1]), c->literals[0], c);
}
LOG (c, "substituted");
}
while (!clause.empty ()) {
int lit = clause.back ();
clause.pop_back ();
CADICAL_assert (marked (lit) > 0);
unmark (lit);
}
lrat_chain.clear ();
}
if (proof) {
for (auto idx : vars) {
if (!substituted)
break;
if (!active (idx))
continue;
const int64_t id1 = decompose_ids[vlit (-idx)];
if (!id1)
continue;
int other = reprs[vlit (idx)];
CADICAL_assert (other != idx);
CADICAL_assert (!flags (other).eliminated ());
CADICAL_assert (!flags (other).substituted ());
clause.push_back (other);
clause.push_back (-idx);
proof->delete_clause (id1, false, clause);
clause.clear ();
clause.push_back (idx);
clause.push_back (-other);
const int64_t id2 = decompose_ids[vlit (idx)];
proof->delete_clause (id2, false, clause);
clause.clear ();
}
}
if (!unsat && !postponed_garbage.empty ()) {
LOG ("now marking %zd postponed garbage clauses",
postponed_garbage.size ());
for (const auto &c : postponed_garbage)
mark_garbage (c);
}
erase_vector (postponed_garbage);
PHASE ("decompose", stats.decompositions,
"%zd clauses replaced %.2f%% producing %zd garbage clauses %.2f%%",
replaced, percent (replaced, clauses_size), garbage,
percent (garbage, replaced));
erase_vector (scc);
// Propagate found units.
if (!unsat && propagated < trail.size () && !propagate ()) {
LOG ("empty clause after propagating units from substitution");
learn_empty_clause ();
}
for (auto idx : vars) {
if (!substituted)
break;
if (unsat)
break;
if (!active (idx))
continue;
int other = reprs[vlit (idx)];
if (other == idx)
continue;
CADICAL_assert (!flags (other).eliminated ());
CADICAL_assert (!flags (other).substituted ());
if (!flags (other).fixed ())
mark_substituted (idx);
}
reprs_delete.free ();
dfs_delete.free ();
erase_vector (dfs_chains);
if (substituted)
flush_all_occs_and_watches (); // particularly the 'blit's
bool success =
unsat || (substituted > 0 && (new_unit || new_binary_clause));
report ('d', !opts.reportall && !success);
STOP_SIMPLIFIER (decompose, DECOMP);
return success;
}
void Internal::decompose () {
for (int round = 1; round <= opts.decomposerounds; round++)
if (!decompose_round ())
break;
}
} // namespace CaDiCaL
ABC_NAMESPACE_IMPL_END

View File

@ -1,176 +0,0 @@
#include "global.h"
#include "internal.hpp"
ABC_NAMESPACE_IMPL_START
namespace CaDiCaL {
// Equivalent literal substitution in 'decompose' and shrinking in 'subsume'
// or 'vivify' might produce duplicated binary clauses. They can not be
// found in 'subsume' nor 'vivify' since we explicitly do not consider
// binary clauses as candidates to be shrunken or subsumed. They are
// detected here by a simple scan of watch lists and then marked as garbage.
// This is actually also quite fast.
// Further it might also be possible that two binary clauses can be resolved
// to produce a unit (we call it 'hyper unary resolution'). For example
// resolving the binary clauses '1 -2' and '1 2' produces the unit '1'.
// This could be found by probing in 'probe' unless '-1' also occurs in a
// binary clause (add the clause '-1 2' to those two clauses) in which case
// '1' as well as '2' both occur positively as well as negatively and none
// of them nor their negation is considered as probe
void Internal::mark_duplicated_binary_clauses_as_garbage () {
if (!opts.deduplicate)
return;
if (unsat)
return;
if (terminated_asynchronously ())
return;
START_SIMPLIFIER (deduplicate, DEDUP);
stats.deduplications++;
CADICAL_assert (!level);
CADICAL_assert (watching ());
vector<int> stack; // To save marked literals and unmark them later.
int64_t subsumed = 0;
int64_t units = 0;
for (auto idx : vars) {
if (unsat)
break;
if (!active (idx))
continue;
int unit = 0;
for (int sign = -1; !unit && sign <= 1; sign += 2) {
const int lit = sign * idx; // Consider all literals.
CADICAL_assert (stack.empty ());
Watches &ws = watches (lit);
// We are removing references to garbage clause. Thus no 'auto'.
const const_watch_iterator end = ws.end ();
watch_iterator j = ws.begin ();
const_watch_iterator i;
for (i = j; !unit && i != end; i++) {
Watch w = *j++ = *i;
if (!w.binary ())
continue;
int other = w.blit;
const int tmp = marked (other);
Clause *c = w.clause;
if (tmp > 0) { // Found duplicated binary clause.
if (c->garbage) {
j--;
continue;
}
LOG (c, "found duplicated");
// The previous identical clause 'd' might be redundant and if the
// second clause 'c' is not (so irredundant), then we have to keep
// 'c' instead of 'd', thus we search for it and replace it.
if (!c->redundant) {
watch_iterator k;
for (k = ws.begin ();; k++) {
CADICAL_assert (k != i);
if (!k->binary ())
continue;
if (k->blit != other)
continue;
Clause *d = k->clause;
if (d->garbage)
continue;
c = d;
break;
}
*k = w;
}
LOG (c, "mark garbage duplicated");
stats.subsumed++;
stats.deduplicated++;
subsumed++;
mark_garbage (c);
j--;
} else if (tmp < 0) { // Hyper unary resolution.
LOG ("found %d %d and %d %d which produces unit %d", lit, -other,
lit, other, lit);
unit = lit;
if (lrat) {
// taken from fradical
CADICAL_assert (lrat_chain.empty ());
lrat_chain.push_back (c->id);
// We've forgotten where the other binary clause is, so go find
// it again
for (watch_iterator k = ws.begin ();; k++) {
CADICAL_assert (k != i);
if (!k->binary ())
continue;
if (k->blit != -other)
continue;
lrat_chain.push_back (k->clause->id);
break;
}
}
j = ws.begin (); // Flush 'ws'.
units++;
} else {
if (c->garbage)
continue;
mark (other);
stack.push_back (other);
}
}
if (j == ws.begin ())
erase_vector (ws);
else if (j != end)
ws.resize (j - ws.begin ()); // Shrink watchers.
for (const auto &other : stack)
unmark (other);
stack.clear ();
}
// Propagation potentially messes up the watches and thus we can not
// propagate the unit immediately after finding it. Instead we break
// out of both loops and assign and propagate the unit here.
if (unit) {
stats.failed++;
stats.hyperunary++;
assign_unit (unit);
// lrat_chain.clear (); done in search_assign
if (!propagate ()) {
LOG ("empty clause after propagating unit");
learn_empty_clause ();
}
}
}
STOP_SIMPLIFIER (deduplicate, DEDUP);
report ('2', !opts.reportall && !(subsumed + units));
}
} // namespace CaDiCaL
ABC_NAMESPACE_IMPL_END

View File

@ -1,289 +0,0 @@
#include "global.h"
#include "internal.hpp"
ABC_NAMESPACE_IMPL_START
namespace CaDiCaL {
#define INVALID_LIT UINT_MAX
// functions below are passed to cadical_kitten
//
struct definition_extractor {
Eliminator *eliminator;
Internal *internal;
vector<Clause *> clauses[2];
int lit;
vector<vector<int>> implicants;
int unit;
};
extern "C" {
// used to extract definitions from cadical_kitten
//
static void traverse_definition_core (void *state, unsigned id) {
definition_extractor *extractor = (definition_extractor *) state;
Clause *clause;
const vector<Clause *> &clauses0 = extractor->clauses[0];
const vector<Clause *> &clauses1 = extractor->clauses[1];
Eliminator *eliminator = extractor->eliminator;
const size_t size_clauses0 = clauses0.size ();
const size_t size_clauses1 = clauses1.size ();
CADICAL_assert (size_clauses0 <= UINT_MAX);
unsigned sign;
CADICAL_assert (id < size_clauses0 + size_clauses1);
if (id < size_clauses0) {
clause = clauses0[id];
sign = 1;
} else {
unsigned tmp = id - size_clauses0;
#ifndef CADICAL_NDEBUG
CADICAL_assert (size_clauses1 <= UINT_MAX);
CADICAL_assert (tmp < size_clauses1);
#endif
clause = clauses1[tmp];
sign = 2;
}
(void) size_clauses1;
clause->gate = true;
eliminator->gates.push_back (clause);
#ifdef LOGGING
Internal *internal = extractor->internal;
LOG (clause, "extracted gate");
#endif
eliminator->definition_unit |= sign;
}
// extracts relevant learned clauses from kissat for drat proofs
//
static void traverse_one_sided_core_lemma (void *state, bool learned,
size_t size,
const unsigned *lits) {
if (!learned)
return;
definition_extractor *extractor = (definition_extractor *) state;
Eliminator *eliminator = extractor->eliminator;
Internal *internal = extractor->internal;
Proof *proof = internal->proof;
const int unit = extractor->unit;
vector<proof_clause> &proof_clauses = eliminator->proof_clauses;
if (size) {
proof_clause pc;
pc.id = ++(internal->clause_id);
pc.literals.push_back (unit);
const unsigned *end = lits + size;
for (const unsigned *p = lits; p != end; p++)
pc.literals.push_back (internal->citten2lit (*p)); // conversion
proof_clauses.push_back (pc);
CADICAL_assert (proof);
proof->add_derived_clause (pc.id, true, pc.literals, pc.chain);
} else {
internal->assign_unit (unit);
for (const auto &pc : proof_clauses) {
proof->delete_clause (pc.id, true, pc.literals);
}
proof_clauses.clear ();
}
}
// extract lrat proofs for relevant clauses
//
static void traverse_one_sided_core_lemma_with_lrat (
void *state, unsigned cid, unsigned id, bool learned, size_t size,
const unsigned *lits, size_t chain_size, const unsigned *chain) {
definition_extractor *extractor = (definition_extractor *) state;
Eliminator *eliminator = extractor->eliminator;
Internal *internal = extractor->internal;
Proof *proof = internal->proof;
const int unit = extractor->unit;
const vector<Clause *> &clauses0 = extractor->clauses[0];
const vector<Clause *> &clauses1 = extractor->clauses[1];
vector<proof_clause> &proof_clauses = eliminator->proof_clauses;
if (!learned) { // remember clauses for mapping to cadical_kitten internal
CADICAL_assert (size);
CADICAL_assert (!chain_size);
proof_clause pc;
pc.cid = cid;
pc.learned = false;
const size_t size_clauses0 = clauses0.size ();
CADICAL_assert (size_clauses0 <= UINT_MAX);
if (id < size_clauses0) {
pc.id = clauses0[id]->id;
} else {
unsigned tmp = id - size_clauses0;
#ifndef CADICAL_NDEBUG
const size_t size_clauses1 = clauses1.size ();
CADICAL_assert (size_clauses1 <= UINT_MAX);
CADICAL_assert (tmp < size_clauses1);
#endif
pc.id = clauses1[tmp]->id;
}
proof_clauses.push_back (pc);
} else { // actually add to proof
CADICAL_assert (chain_size);
if (size) {
proof_clause pc;
pc.id = ++(internal->clause_id);
pc.cid = cid;
pc.learned = true;
pc.literals.push_back (unit);
const unsigned *end = lits + size;
for (const unsigned *p = lits; p != end; p++)
pc.literals.push_back (internal->citten2lit (*p)); // conversion
for (const unsigned *p = chain + chain_size; p != chain; p--) {
int64_t id = 0;
for (const auto &cpc : proof_clauses) {
if (cpc.cid == *(p - 1)) {
id = cpc.id;
break;
}
}
CADICAL_assert (id);
pc.chain.push_back (id);
}
proof_clauses.push_back (pc);
CADICAL_assert (proof);
proof->add_derived_clause (pc.id, true, pc.literals, pc.chain);
} else { // learn unit finish proof
CADICAL_assert (internal->lrat_chain.empty ());
for (const unsigned *p = chain + chain_size; p != chain; p--) {
int64_t id = 0;
for (const auto &cpc : proof_clauses) {
if (cpc.cid == *(p - 1)) {
id = cpc.id;
break;
}
}
CADICAL_assert (id);
internal->lrat_chain.push_back (id);
}
internal->assign_unit (unit);
CADICAL_assert (internal->lrat_chain.empty ());
for (const auto &pc : proof_clauses) {
if (pc.learned)
proof->delete_clause (pc.id, true, pc.literals);
}
proof_clauses.clear ();
}
}
}
} // end extern C
// Code ported from kissat. Kitten (and kissat) use unsigned representation
// for literals whereas CaDiCaL uses signed representation. Conversion is
// necessary for communication using lit2citten and citten2lit.
// This code is called in elim and cadical_kitten is initialized beforehand.
// To avoid confusion all cadical interal definitions with cadical_kitten are called
// citten.
//
void Internal::find_definition (Eliminator &eliminator, int lit) {
if (!opts.elimdef)
return;
if (unsat)
return;
if (val (lit))
return;
if (!eliminator.gates.empty ())
return;
CADICAL_assert (!val (lit));
CADICAL_assert (!level);
CADICAL_assert (citten);
const int not_lit = -lit;
definition_extractor extractor;
extractor.lit = lit;
extractor.clauses[0] = occs (lit);
extractor.clauses[1] = occs (not_lit);
extractor.eliminator = &eliminator;
extractor.internal = internal;
citten_clear_track_log_terminate ();
unsigned exported = 0;
for (unsigned sign = 0; sign < 2; sign++) {
const unsigned except = sign ? lit2citten (not_lit) : lit2citten (lit);
for (auto c : extractor.clauses[sign]) {
// to avoid copying the literals of c in their unsigned
// representation we instead implement the translation in cadical_kitten
if (!c->garbage) {
LOG (c, "adding to cadical_kitten");
citten_clause_with_id_and_exception (citten, exported, c->size,
c->literals, except);
}
exported++;
}
}
stats.definitions_checked++;
const size_t limit = opts.elimdefticks;
cadical_kitten_set_ticks_limit (citten, limit);
int status = cadical_kitten_solve (citten);
if (!exported)
goto ABORT;
if (status == 20) {
LOG ("sub-solver result UNSAT shows definition exists");
uint64_t learned;
unsigned reduced = cadical_kitten_compute_clausal_core (citten, &learned);
LOG ("1st sub-solver core of size %u original clauses out of %u",
reduced, exported);
for (int i = 2; i <= opts.elimdefcores; i++) {
cadical_kitten_shrink_to_clausal_core (citten);
cadical_kitten_shuffle_clauses (citten);
cadical_kitten_set_ticks_limit (citten, 10 * limit);
int tmp = cadical_kitten_solve (citten);
CADICAL_assert (!tmp || tmp == 20);
if (!tmp) {
LOG ("aborting core extraction");
goto ABORT;
}
#ifndef CADICAL_NDEBUG
unsigned previous = reduced;
#endif
reduced = cadical_kitten_compute_clausal_core (citten, &learned);
LOG ("%d sub-solver core of size %u original clauses out of %u", i,
reduced, exported);
CADICAL_assert (reduced <= previous);
#if not defined(LOGGING) && defined(CADICAL_NDEBUG)
(void) reduced;
#endif
}
stats.definitions_extracted++;
eliminator.gatetype = DEF;
eliminator.definition_unit = 0;
cadical_kitten_traverse_core_ids (citten, &extractor, traverse_definition_core);
CADICAL_assert (eliminator.definition_unit);
int unit = 0;
if (eliminator.definition_unit == 2) {
unit = not_lit;
} else if (eliminator.definition_unit == 1)
unit = lit;
if (unit) {
stats.definition_units++;
VERBOSE (2, "one sided core "
"definition extraction yields "
"failed literal");
if (proof) {
if (lrat) {
extractor.unit = unit;
cadical_kitten_trace_core (citten, &extractor,
traverse_one_sided_core_lemma_with_lrat);
} else {
extractor.unit = unit;
cadical_kitten_traverse_core_clauses (citten, &extractor,
traverse_one_sided_core_lemma);
}
} else
assign_unit (unit);
elim_propagate (eliminator, unit);
}
} else {
ABORT:
LOG ("sub-solver failed to show that definition exists");
}
stats.definition_ticks += cadical_kitten_current_ticks (citten);
return;
}
} // namespace CaDiCaL
ABC_NAMESPACE_IMPL_END

View File

@ -1,159 +0,0 @@
#include "global.h"
#include "internal.hpp"
ABC_NAMESPACE_IMPL_START
namespace CaDiCaL {
/*------------------------------------------------------------------------*/
DratTracer::DratTracer (Internal *i, File *f, bool b)
: internal (i), file (f), binary (b)
#ifndef CADICAL_QUIET
,
added (0), deleted (0)
#endif
{
(void) internal;
}
void DratTracer::connect_internal (Internal *i) {
internal = i;
file->connect_internal (internal);
LOG ("DRAT TRACER connected to internal");
}
DratTracer::~DratTracer () {
LOG ("DRAT TRACER delete");
delete file;
}
/*------------------------------------------------------------------------*/
inline void DratTracer::put_binary_zero () {
CADICAL_assert (binary);
CADICAL_assert (file);
file->put ((unsigned char) 0);
}
inline void DratTracer::put_binary_lit (int lit) {
CADICAL_assert (binary);
CADICAL_assert (file);
CADICAL_assert (lit != INT_MIN);
unsigned idx = abs (lit);
CADICAL_assert (idx < (1u << 31));
unsigned x = 2u * idx + (lit < 0);
unsigned char ch;
while (x & ~0x7f) {
ch = (x & 0x7f) | 0x80;
file->put (ch);
x >>= 7;
}
ch = x;
file->put (ch);
}
/*------------------------------------------------------------------------*/
void DratTracer::drat_add_clause (const vector<int> &clause) {
if (binary)
file->put ('a');
for (const auto &external_lit : clause)
if (binary)
put_binary_lit (external_lit);
else
file->put (external_lit), file->put (' ');
if (binary)
put_binary_zero ();
else
file->put ("0\n");
}
void DratTracer::drat_delete_clause (const vector<int> &clause) {
if (binary)
file->put ('d');
else
file->put ("d ");
for (const auto &external_lit : clause)
if (binary)
put_binary_lit (external_lit);
else
file->put (external_lit), file->put (' ');
if (binary)
put_binary_zero ();
else
file->put ("0\n");
}
/*------------------------------------------------------------------------*/
void DratTracer::add_derived_clause (int64_t, bool,
const vector<int> &clause,
const vector<int64_t> &) {
if (file->closed ())
return;
LOG ("DRAT TRACER tracing addition of derived clause");
drat_add_clause (clause);
#ifndef CADICAL_QUIET
added++;
#endif
}
void DratTracer::delete_clause (int64_t, bool, const vector<int> &clause) {
if (file->closed ())
return;
LOG ("DRAT TRACER tracing deletion of clause");
drat_delete_clause (clause);
#ifndef CADICAL_QUIET
deleted++;
#endif
}
/*------------------------------------------------------------------------*/
bool DratTracer::closed () { return file->closed (); }
#ifndef CADICAL_QUIET
void DratTracer::print_statistics () {
uint64_t bytes = file->bytes ();
uint64_t total = added + deleted;
MSG ("DRAT %" PRId64 " added clauses %.2f%%", added,
percent (added, total));
MSG ("DRAT %" PRId64 " deleted clauses %.2f%%", deleted,
percent (deleted, total));
MSG ("DRAT %" PRId64 " bytes (%.2f MB)", bytes,
bytes / (double) (1 << 20));
}
#endif
void DratTracer::close (bool print) {
CADICAL_assert (!closed ());
file->close ();
#ifndef CADICAL_QUIET
if (print) {
MSG ("DRAT proof file '%s' closed", file->name ());
print_statistics ();
}
#else
(void) print;
#endif
}
void DratTracer::flush (bool print) {
CADICAL_assert (!closed ());
file->flush ();
#ifndef CADICAL_QUIET
if (print) {
MSG ("DRAT proof file '%s' flushed", file->name ());
print_statistics ();
}
#else
(void) print;
#endif
}
} // namespace CaDiCaL
ABC_NAMESPACE_IMPL_END

File diff suppressed because it is too large Load Diff

View File

@ -1,576 +0,0 @@
#include "global.h"
#include "internal.hpp"
ABC_NAMESPACE_IMPL_START
namespace CaDiCaL {
/*------------------------------------------------------------------------*/
// Implements a variant of elimination with a much lower limit to be run as
// preprocessing. See elim for comments
/*------------------------------------------------------------------------*/
// Flush garbage clause, check fast elimination limits and return number of
// remaining occurrences (or 'fastelimbound + 1' if some limit was hit).
int64_t Internal::flush_elimfast_occs (int lit) {
const int64_t occslim = opts.fastelimbound;
const int64_t clslim = opts.fastelimocclim;
const int64_t failed = occslim + 1;
Occs &os = occs (lit);
const const_occs_iterator end = os.end ();
occs_iterator j = os.begin (), i = j;
int64_t res = 0;
while (i != end) {
Clause *c = *i++;
if (c->collect ())
continue;
*j++ = c;
if (c->size > clslim) {
res = failed;
break;
}
if (++res > occslim) {
CADICAL_assert (opts.fastelimbound < 0 || res == failed);
break;
}
}
if (i != j) {
while (i != end)
*j++ = *i++;
os.resize (j - os.begin ());
shrink_occs (os);
}
return res;
}
/*------------------------------------------------------------------------*/
// Check whether the number of non-tautological resolvents on 'pivot' is
// smaller or equal to the number of clauses with 'pivot' or '-pivot'. This
// is the main criteria of bounded variable elimination. As a side effect
// it flushes garbage clauses with that variable, sorts its occurrence lists
// (smallest clauses first) and also negates pivot if it has more positive
// than negative occurrences.
bool Internal::elimfast_resolvents_are_bounded (Eliminator &eliminator,
int pivot) {
CADICAL_assert (eliminator.gates.empty ());
CADICAL_assert (!eliminator.definition_unit);
stats.elimtried++;
CADICAL_assert (!unsat);
CADICAL_assert (active (pivot));
const Occs &ps = occs (pivot);
const Occs &ns = occs (-pivot);
int64_t pos = ps.size ();
int64_t neg = ns.size ();
int64_t bound = opts.fastelimbound;
if (!pos || !neg)
return bound >= 0;
const int64_t sum = pos + neg;
const int64_t product = pos * neg;
if (bound > sum)
bound = sum;
LOG ("checking number resolvents on %d bounded by "
"%" PRId64 " = %" PRId64 " + %" PRId64 " + %d",
pivot, bound, pos, neg, opts.fastelimbound);
if (product <= bound) {
LOG ("fast elimination occurrence limits sufficiently small enough");
return true;
}
// Try all resolutions between a positive occurrence (outer loop) of
// 'pivot' and a negative occurrence of 'pivot' (inner loop) as long the
// bound on non-tautological resolvents is not hit and the size of the
// generated resolvents does not exceed the resolvent clause size limit.
int64_t resolvents = 0; // Non-tautological resolvents.
for (const auto &c : ps) {
CADICAL_assert (!c->redundant);
if (c->garbage)
continue;
for (const auto &d : ns) {
CADICAL_assert (!d->redundant);
if (d->garbage)
continue;
if (resolve_clauses (eliminator, c, pivot, d, true)) {
resolvents++;
int size = clause.size ();
clause.clear ();
LOG ("now at least %" PRId64
" non-tautological resolvents on pivot %d",
resolvents, pivot);
if (size > opts.fastelimclslim) {
LOG ("resolvent size %d too big after %" PRId64
" resolvents on %d",
size, resolvents, pivot);
return false;
}
if (resolvents > bound) {
LOG ("too many non-tautological resolvents on %d", pivot);
return false;
}
} else if (unsat)
return false;
else if (val (pivot))
return false;
}
}
LOG ("need %" PRId64 " <= %" PRId64 " non-tautological resolvents",
resolvents, bound);
return true;
}
/*------------------------------------------------------------------------*/
/*------------------------------------------------------------------------*/
// Add all resolvents on 'pivot' and connect them.
inline void Internal::elimfast_add_resolvents (Eliminator &eliminator,
int pivot) {
CADICAL_assert (eliminator.gates.empty ());
CADICAL_assert (!eliminator.definition_unit);
LOG ("adding all resolvents on %d", pivot);
CADICAL_assert (!val (pivot));
CADICAL_assert (!flags (pivot).eliminated ());
const Occs &ps = occs (pivot);
const Occs &ns = occs (-pivot);
#ifdef LOGGING
int64_t resolvents = 0;
#endif
for (auto &c : ps) {
if (unsat)
break;
if (c->garbage)
continue;
for (auto &d : ns) {
if (unsat)
break;
if (d->garbage)
continue;
if (!resolve_clauses (eliminator, c, pivot, d, false))
continue;
CADICAL_assert (!lrat || !lrat_chain.empty ());
Clause *r = new_resolved_irredundant_clause ();
elim_update_added_clause (eliminator, r);
eliminator.enqueue (r);
lrat_chain.clear ();
clause.clear ();
#ifdef LOGGING
resolvents++;
#endif
}
}
LOG ("added %" PRId64 " resolvents to eliminate %d", resolvents, pivot);
}
/*------------------------------------------------------------------------*/
// Try to eliminate 'pivot' by bounded variable elimination.
void Internal::try_to_fasteliminate_variable (Eliminator &eliminator,
int pivot,
bool &deleted_binary_clause) {
if (!active (pivot))
return;
CADICAL_assert (!frozen (pivot));
// First flush garbage clauses and check limits.
int64_t bound = opts.fastelimbound;
int64_t pos = flush_elimfast_occs (pivot);
if (pos > 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) {
LOG ("too many occurrences thus not eliminated %d", -pivot);
CADICAL_assert (!eliminator.schedule.contains (abs (pivot)));
return;
}
const int64_t product = pos * neg;
const int64_t sum = pos + neg;
if (bound > sum)
bound = sum;
if (pos > neg) {
pivot = -pivot;
swap (pos, neg);
}
LOG ("pivot %d occurs positively %" PRId64
" times and negatively %" PRId64 " times",
pivot, pos, neg);
CADICAL_assert (!eliminator.schedule.contains (abs (pivot)));
CADICAL_assert (pos <= neg);
LOG ("trying to eliminate %d", pivot);
CADICAL_assert (!flags (pivot).eliminated ());
// Sort occurrence lists, such that shorter clauses come first.
Occs &ps = occs (pivot);
stable_sort (ps.begin (), ps.end (), clause_smaller_size ());
Occs &ns = occs (-pivot);
stable_sort (ns.begin (), ns.end (), clause_smaller_size ());
if (!unsat && !val (pivot)) {
if (product <= bound ||
elimfast_resolvents_are_bounded (eliminator, pivot)) {
LOG ("number of resolvents on %d are bounded", pivot);
elimfast_add_resolvents (eliminator, pivot);
if (!unsat)
mark_eliminated_clauses_as_garbage (eliminator, pivot,
deleted_binary_clause);
if (active (pivot))
mark_eliminated (pivot);
} else {
LOG ("too many resolvents on %d so not eliminated", pivot);
}
}
unmark_gate_clauses (eliminator);
elim_backward_clauses (eliminator);
}
/*------------------------------------------------------------------------*/
// This function performs one round of bounded variable elimination and
// returns the number of eliminated variables. The additional result
// 'completed' is true if this elimination round ran to completion (all
// variables have been tried). Otherwise it was asynchronously terminated
// or the resolution limit was hit.
int Internal::elimfast_round (bool &completed,
bool &deleted_binary_clause) {
CADICAL_assert (opts.fastelim);
CADICAL_assert (!unsat);
START_SIMPLIFIER (fastelim, ELIM);
stats.elimfastrounds++;
CADICAL_assert (!level);
int64_t resolution_limit;
if (opts.elimlimited) {
int64_t delta = stats.propagations.search;
delta *= 1e-3 * opts.elimeffort;
if (delta < opts.elimmineff)
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);
resolution_limit = stats.elimres + delta;
} else {
PHASE ("fastelim-round", stats.elimfastrounds, "resolutions unlimited");
resolution_limit = LONG_MAX;
}
init_noccs ();
// First compute the number of occurrences of each literal and at the same
// time mark satisfied clauses and update 'elim' flags of variables in
// clauses with root level assigned literals (both false and true).
//
for (const auto &c : clauses) {
if (c->garbage || c->redundant)
continue;
bool satisfied = false, falsified = false;
for (const auto &lit : *c) {
const signed char tmp = val (lit);
if (tmp > 0)
satisfied = true;
else if (tmp < 0)
falsified = true;
else
CADICAL_assert (active (lit));
}
if (satisfied)
mark_garbage (c); // forces more precise counts
else {
for (const auto &lit : *c) {
if (!active (lit))
continue;
if (falsified)
mark_elim (lit); // simulate unit propagation
noccs (lit)++;
}
}
}
init_occs ();
Eliminator eliminator (this);
ElimSchedule &schedule = eliminator.schedule;
CADICAL_assert (schedule.empty ());
// Now find elimination candidates which occurred in clauses removed since
// the last time we ran bounded variable elimination, which in turned
// triggered their 'elim' bit to be set.
//
for (auto idx : vars) {
if (!active (idx))
continue;
if (frozen (idx))
continue;
if (!flags (idx).elim)
continue;
LOG ("scheduling %d for elimination initially", idx);
schedule.push_back (idx);
}
schedule.shrink ();
#ifndef CADICAL_QUIET
int64_t scheduled = schedule.size ();
#endif
PHASE ("fastelim-round", stats.elimfastrounds,
"scheduled %" PRId64 " variables %.0f%% for elimination",
scheduled, percent (scheduled, active ()));
// Connect irredundant clauses.
//
for (const auto &c : clauses)
if (!c->garbage && !c->redundant)
for (const auto &lit : *c)
if (active (lit))
occs (lit).push_back (c);
#ifndef CADICAL_QUIET
const int64_t old_resolutions = stats.elimres;
#endif
const int old_eliminated = stats.all.eliminated;
const int old_fixed = stats.all.fixed;
// Limit on garbage literals during variable elimination. If the limit is
// hit a garbage collection is performed.
//
const int64_t garbage_limit = (2 * stats.irrlits / 3) + (1 << 20);
// Main loops tries to eliminate variables according to the schedule. The
// schedule is updated dynamically and variables are potentially
// rescheduled to be tried again if they occur in a removed clause.
//
#ifndef CADICAL_QUIET
int64_t tried = 0;
#endif
while (!unsat && !terminated_asynchronously () &&
stats.elimres <= resolution_limit && !schedule.empty ()) {
int idx = schedule.front ();
schedule.pop_front ();
flags (idx).elim = false;
try_to_fasteliminate_variable (eliminator, idx, deleted_binary_clause);
#ifndef CADICAL_QUIET
tried++;
#endif
if (stats.garbage.literals <= garbage_limit)
continue;
mark_redundant_clauses_with_eliminated_variables_as_garbage ();
garbage_collection ();
}
// If the schedule is empty all variables have been tried (even
// rescheduled ones). Otherwise asynchronous termination happened or we
// ran into the resolution limit (or derived unsatisfiability).
//
completed = !schedule.size ();
PHASE ("fastelim-round", stats.elimfastrounds,
"tried to eliminate %" PRId64 " variables %.0f%% (%zd remain)",
tried, percent (tried, scheduled), schedule.size ());
schedule.erase ();
reset_occs ();
reset_noccs ();
// Mark all redundant clauses with eliminated variables as garbage.
//
if (!unsat)
mark_redundant_clauses_with_eliminated_variables_as_garbage ();
int eliminated = stats.all.eliminated - old_eliminated;
stats.all.fasteliminated += eliminated;
#ifndef CADICAL_QUIET
int64_t resolutions = stats.elimres - old_resolutions;
PHASE ("fastelim-round", stats.elimfastrounds,
"eliminated %d variables %.0f%% in %" PRId64 " resolutions",
eliminated, percent (eliminated, scheduled), resolutions);
#endif
const int units = stats.all.fixed - old_fixed;
report ('e', !opts.reportall && !(eliminated + units));
STOP_SIMPLIFIER (fastelim, ELIM);
return eliminated; // non-zero if successful
}
/*------------------------------------------------------------------------*/
void Internal::elimfast () {
if (unsat)
return;
if (level)
backtrack ();
if (!propagate ()) {
learn_empty_clause ();
return;
}
stats.elimfastphases++;
PHASE ("fastelim-phase", stats.elimfastphases,
"starting at most %d elimination rounds", opts.fastelimrounds);
if (external_prop) {
CADICAL_assert (!level);
private_steps = true;
}
#ifndef CADICAL_QUIET
int old_active_variables = active ();
int old_eliminated = stats.all.eliminated;
#endif
reset_watches (); // saves lots of memory
// Alternate one round of bounded variable elimination ('elim_round') and
// subsumption ('subsume_round'), blocked ('block') and covered clause
// elimination ('cover') until nothing changes, or the round limit is hit.
// The loop also aborts early if no variable could be eliminated, the
// empty clause is resolved, it is asynchronously terminated or a
// resolution limit is hit.
// This variable determines whether the whole loop of this bounded
// variable elimination phase ('elim') ran until completion. This
// potentially triggers an incremental increase of the elimination bound.
//
bool phase_complete = false, deleted_binary_clause = false;
int round = 1;
#ifndef CADICAL_QUIET
int eliminated = 0;
#endif
bool round_complete = false;
while (!unsat && !phase_complete && !terminated_asynchronously ()) {
#ifndef CADICAL_QUIET
int eliminated =
#endif
elimfast_round (round_complete, deleted_binary_clause);
if (!round_complete) {
PHASE ("fastelim-phase", stats.elimphases,
"last round %d incomplete %s", round,
eliminated ? "but successful" : "and unsuccessful");
CADICAL_assert (!phase_complete);
break;
}
if (round++ >= opts.fastelimrounds) {
PHASE ("fastelim-phase", stats.elimphases, "round limit %d hit (%s)",
round - 1,
eliminated ? "though last round successful"
: "last round unsuccessful anyhow");
CADICAL_assert (!phase_complete);
break;
}
// Prioritize 'subsumption' over blocked and covered clause elimination.
if (subsume_round ())
continue;
// Was not able to generate new variable elimination candidates after
// variable elimination round, neither through subsumption, nor blocked,
// nor covered clause elimination.
//
PHASE ("fastelim-phase", stats.elimphases,
"no new variable elimination candidates");
CADICAL_assert (round_complete);
phase_complete = true;
}
for (auto idx : vars) {
if (active (idx))
flags (idx).elim = true;
}
if (phase_complete) {
stats.elimcompleted++;
PHASE ("fastelim-phase", stats.elimphases,
"fully completed elimination %" PRId64
" at elimination bound %" PRId64 "",
stats.elimcompleted, lim.elimbound);
} else {
PHASE ("fastelim-phase", stats.elimphases,
"incomplete elimination %" PRId64
" at elimination bound %" PRId64 "",
stats.elimcompleted + 1, lim.elimbound);
}
if (deleted_binary_clause)
delete_garbage_clauses ();
init_watches ();
connect_watches ();
if (unsat)
LOG ("elimination derived empty clause");
else if (propagated < trail.size ()) {
LOG ("elimination produced %zd units",
(size_t) (trail.size () - propagated));
if (!propagate ()) {
LOG ("propagating units after elimination results in empty clause");
learn_empty_clause ();
}
}
#ifndef CADICAL_QUIET
eliminated = stats.all.eliminated - old_eliminated;
PHASE ("fastelim-phase", stats.elimphases,
"eliminated %d variables %.2f%%", eliminated,
percent (eliminated, old_active_variables));
#endif
if (external_prop) {
CADICAL_assert (!level);
private_steps = false;
}
}
} // namespace CaDiCaL
ABC_NAMESPACE_IMPL_END

View File

@ -1,101 +0,0 @@
#include "global.h"
#include "internal.hpp"
ABC_NAMESPACE_IMPL_START
namespace CaDiCaL {
// Updating an exponential moving average is placed here since we want to
// log both updates and phases of initialization, thus need 'LOG'.
//
// We now use initialization bias correction as in the ADAM method
// [KingmaBa-ICLR'15] instead of our ad-hoc initialization method used
// before. Our old variant used exponentially decreasing alphas:
//
// 1,
// 1/2, 1/2,
// 1/4, 1/4, 1/4, 1/4
// 1/8, 1/8, 1/8, 1/8, 1/8, 1/8, 1/8, 1/8,
// ...
// 2^-n, ..., 2^-n 'n' times
// alpha, alpha, ... now 'alpha' forever.
//
// where 2^-n is the smallest negative power of two above 'alpha'
//
// This old method is better than the initializations described in our
// [BiereFroehlich-POS'15] paper and actually faster than the ADAM method,
// but less precise. We consider this old method obsolete now but it
// could still be useful for implementations relying on integers instead
// of floating points because it only needs shifts and integer arithmetic.
//
// Our new method for unbiased initialization of the exponential averages
// works as follows. First the biased moving average is computed as usual.
// Note that (as already before) we use the simpler equation
//
// new_biased = old_biased + alpha * (y - old_biased);
//
// which in principle (and thus easy to remember) can be implemented as
//
// biased += alpha * (y - biased);
//
// The original formulation in the ADAM paper (with 'alpha = 1 - beta') is
//
// new_biased = beta * old_biased + (1 - beta) * y
//
// To show that these are equivalent (modulo floating point issues)
// consider the following equivalent expressions:
//
// old_biased + alpha * (y - old_biased)
// old_biased + alpha * y - alpha * old_biased
// (1 - alpha) * old_biased + alpha * y
// beta * old_biased + (1 - beta) * y
//
// The real new idea taken from the ADAM paper is however to fix the biased
// moving average with a correction term '1.0 / (1.0 - pow (beta, updated))'
// by multiplication to obtain an unbiased moving average (called simply
// 'value' in our 'code'). In order to avoid computing 'pow' every time, we
// use 'exp' which is multiplied in every update with 'beta'.
void EMA::update (Internal *internal, double y, const char *name) {
#ifdef LOGGING
updated++;
const double old_value = value;
#endif
const double old_biased = biased;
const double delta = y - old_biased;
const double scaled_delta = alpha * delta;
const double new_biased = old_biased + scaled_delta;
LOG ("update %" PRIu64 " of biased %s EMA %g with %g (delta %g) "
"yields %g (scaled delta %g)",
updated, name, old_biased, y, delta, new_biased, scaled_delta);
biased = new_biased;
const double old_exp = exp;
double new_exp, div, new_value;
if (old_exp) {
new_exp = old_exp * beta;
CADICAL_assert (new_exp < 1);
exp = new_exp;
div = 1 - new_exp;
CADICAL_assert (div > 0);
new_value = new_biased / div;
} else {
new_value = new_biased;
#ifdef LOGGING
new_exp = 0;
div = 1;
#endif
}
value = new_value;
LOG ("update %" PRIu64 " of corrected %s EMA %g with %g (delta %g) "
"yields %g (exponent %g, divisor %g)",
updated, name, old_value, y, delta, new_value, new_exp, div);
#ifndef LOGGING
(void) internal;
(void) name;
#endif
}
} // namespace CaDiCaL
ABC_NAMESPACE_IMPL_END

View File

@ -1,287 +0,0 @@
#include "global.h"
#include "internal.hpp"
ABC_NAMESPACE_IMPL_START
namespace CaDiCaL {
void External::push_zero_on_extension_stack () {
extension.push_back (0);
LOG ("pushing 0 on extension stack");
}
void External::push_id_on_extension_stack (int64_t id) {
const uint32_t higher_bits = static_cast<int> (id << 32);
const uint32_t lower_bits = (id & (((int64_t) 1 << 32) - 1));
extension.push_back (higher_bits);
extension.push_back (lower_bits);
LOG ("pushing id %" PRIu64 " = %d + %d", id, higher_bits, lower_bits);
}
void External::push_clause_literal_on_extension_stack (int ilit) {
CADICAL_assert (ilit);
const int elit = internal->externalize (ilit);
CADICAL_assert (elit);
extension.push_back (elit);
LOG ("pushing clause literal %d on extension stack (internal %d)", elit,
ilit);
}
void External::push_witness_literal_on_extension_stack (int ilit) {
CADICAL_assert (ilit);
const int elit = internal->externalize (ilit);
CADICAL_assert (elit);
extension.push_back (elit);
LOG ("pushing witness literal %d on extension stack (internal %d)", elit,
ilit);
if (marked (witness, elit))
return;
LOG ("marking witness %d", elit);
mark (witness, elit);
}
// The extension stack allows to reconstruct a satisfying assignment for the
// original formula after removing eliminated clauses. This was pioneered
// by Niklas Soerensson in MiniSAT and for instance is described in our
// inprocessing paper, published at IJCAR'12. This first function adds a
// clause to this stack. First the blocking or eliminated literal is added,
// and then the rest of the clause.
void External::push_clause_on_extension_stack (Clause *c) {
internal->stats.weakened++;
internal->stats.weakenedlen += c->size;
push_zero_on_extension_stack ();
push_id_on_extension_stack (c->id);
push_zero_on_extension_stack ();
for (const auto &lit : *c)
push_clause_literal_on_extension_stack (lit);
}
void External::push_clause_on_extension_stack (Clause *c, int pivot) {
push_zero_on_extension_stack ();
push_witness_literal_on_extension_stack (pivot);
push_clause_on_extension_stack (c);
}
void External::push_binary_clause_on_extension_stack (int64_t id, int pivot,
int other) {
internal->stats.weakened++;
internal->stats.weakenedlen += 2;
push_zero_on_extension_stack ();
push_witness_literal_on_extension_stack (pivot);
push_zero_on_extension_stack ();
push_id_on_extension_stack (id);
push_zero_on_extension_stack ();
push_clause_literal_on_extension_stack (pivot);
push_clause_literal_on_extension_stack (other);
}
/*------------------------------------------------------------------------*/
void External::push_external_clause_and_witness_on_extension_stack (
const vector<int> &c, const vector<int> &w, int64_t id) {
CADICAL_assert (id);
extension.push_back (0);
for (const auto &elit : w) {
CADICAL_assert (elit != INT_MIN);
init (abs (elit));
extension.push_back (elit);
mark (witness, elit);
}
extension.push_back (0);
const uint32_t higher_bits = static_cast<int> (id << 32);
const uint32_t lower_bits = (id & (((int64_t) 1 << 32) - 1));
extension.push_back (higher_bits);
extension.push_back (lower_bits);
extension.push_back (0);
for (const auto &elit : c) {
CADICAL_assert (elit != INT_MIN);
init (abs (elit));
extension.push_back (elit);
}
}
/*------------------------------------------------------------------------*/
// This is the actual extension process. It goes backward over the clauses
// on the extension stack and flips the assignment of one of the blocking
// literals in the conditional autarky stored before the clause. In the
// original algorithm for witness construction for variable elimination and
// blocked clause removal the conditional autarky consists of a single
// literal from the removed clause, while in general the autarky witness can
// contain an arbitrary set of literals. We are using the more general
// witness reconstruction here which for instance would also work for
// super-blocked or set-blocked clauses.
void External::extend () {
CADICAL_assert (!extended);
START (extend);
internal->stats.extensions++;
PHASE ("extend", internal->stats.extensions,
"mapping internal %d assignments to %d assignments",
internal->max_var, max_var);
#ifndef CADICAL_QUIET
int64_t updated = 0;
#endif
for (unsigned i = 1; i <= (unsigned) max_var; i++) {
const int ilit = e2i[i];
if (!ilit)
continue;
if (i >= vals.size ())
vals.resize (i + 1, false);
vals[i] = (internal->val (ilit) > 0);
#ifndef CADICAL_QUIET
updated++;
#endif
}
PHASE ("extend", internal->stats.extensions,
"updated %" PRId64 " external assignments", updated);
PHASE ("extend", internal->stats.extensions,
"extending through extension stack of size %zd",
extension.size ());
const auto begin = extension.begin ();
auto i = extension.end ();
#ifndef CADICAL_QUIET
int64_t flipped = 0;
#endif
while (i != begin) {
bool satisfied = false;
int lit;
CADICAL_assert (i != begin);
while ((lit = *--i)) {
if (satisfied)
continue;
if (ival (lit) == lit)
satisfied = true;
CADICAL_assert (i != begin);
}
CADICAL_assert (i != begin);
LOG ("id=%" PRId64, ((int64_t) *i << 32) + *(i - 1));
CADICAL_assert (*i || *(i - 1));
--i;
CADICAL_assert (i != begin);
--i;
CADICAL_assert (i != begin);
CADICAL_assert (!*i);
--i;
CADICAL_assert (i != begin);
if (satisfied)
while (*--i)
CADICAL_assert (i != begin);
else {
while ((lit = *--i)) {
const int tmp = ival (lit); // not 'signed char'!!!
if (tmp != lit) {
LOG ("flipping blocking literal %d", lit);
CADICAL_assert (lit);
CADICAL_assert (lit != INT_MIN);
size_t idx = abs (lit);
if (idx >= vals.size ())
vals.resize (idx + 1, false);
vals[idx] = !vals[idx];
internal->stats.extended++;
#ifndef CADICAL_QUIET
flipped++;
#endif
}
CADICAL_assert (i != begin);
}
}
}
PHASE ("extend", internal->stats.extensions,
"flipped %" PRId64 " literals during extension", flipped);
extended = true;
LOG ("extended");
STOP (extend);
}
/*------------------------------------------------------------------------*/
bool External::traverse_witnesses_backward (WitnessIterator &it) {
if (internal->unsat)
return true;
vector<int> clause, witness;
const auto begin = extension.begin ();
auto i = extension.end ();
while (i != begin) {
int lit;
while ((lit = *--i))
clause.push_back (lit);
CADICAL_assert (!lit);
--i;
const int64_t id =
((int64_t) * (i - 1) << 32) + static_cast<int64_t> (*i);
CADICAL_assert (id);
i -= 2;
CADICAL_assert (!*i);
CADICAL_assert (i != begin);
while ((lit = *--i))
witness.push_back (lit);
reverse (clause.begin (), clause.end ());
reverse (witness.begin (), witness.end ());
LOG (clause, "traversing clause");
if (!it.witness (clause, witness, id))
return false;
clause.clear ();
witness.clear ();
}
return true;
}
bool External::traverse_witnesses_forward (WitnessIterator &it) {
if (internal->unsat)
return true;
vector<int> clause, witness;
const auto end = extension.end ();
auto i = extension.begin ();
if (i != end) {
int lit = *i++;
do {
CADICAL_assert (!lit), (void) lit;
while ((lit = *i++))
witness.push_back (lit);
CADICAL_assert (!lit);
CADICAL_assert (i != end);
CADICAL_assert (!*i);
const int64_t id =
((int64_t) *i << 32) + static_cast<int64_t> (*(i + 1));
CADICAL_assert (id > 0);
i += 3;
CADICAL_assert (*i);
CADICAL_assert (i != end);
while (i != end && (lit = *i++))
clause.push_back (lit);
if (!it.witness (clause, witness, id))
return false;
clause.clear ();
witness.clear ();
} while (i != end);
}
return true;
}
/*------------------------------------------------------------------------*/
void External::conclude_sat () {
if (!internal->proof || concluded)
return;
concluded = true;
if (!extended)
extend ();
vector<int> model;
for (int idx = 1; idx <= max_var; idx++) {
if (ervars[idx])
continue;
const int lit = ival (idx);
model.push_back (lit);
}
internal->proof->conclude_sat (model);
}
} // namespace CaDiCaL
ABC_NAMESPACE_IMPL_END

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -1,927 +0,0 @@
#include "global.h"
#include "internal.hpp"
ABC_NAMESPACE_IMPL_START
namespace CaDiCaL {
#define FACTORS 1
#define QUOTIENT 2
#define NOUNTED 4
inline bool factor_occs_size::operator() (unsigned a, unsigned b) {
size_t s = internal->occs (internal->u2i (a)).size ();
size_t t = internal->occs (internal->u2i (b)).size ();
if (s > t)
return true;
if (s < t)
return false;
return a > b;
}
// do full occurence list as in elim.cpp but filter out useless clauses
void Internal::factor_mode () {
reset_watches ();
CADICAL_assert (!watching ());
init_occs ();
const int size_limit = opts.factorsize;
vector<unsigned> bincount, largecount;
const unsigned max_lit = 2 * (max_var + 1);
enlarge_zero (bincount, max_lit);
if (size_limit > 2)
enlarge_zero (largecount, max_lit);
vector<Clause *> candidates;
int64_t &ticks = stats.ticks.factor;
ticks += 1 + cache_lines (clauses.size (), sizeof (Clause *));
// push binary clauses on the occurrence stack.
for (const auto &c : clauses) {
ticks++;
if (c->garbage)
continue;
if (c->redundant && c->size > 2)
continue;
if (c->size > size_limit)
continue;
if (c->size == 2) {
const int lit = c->literals[0];
const int other = c->literals[1];
bincount[vlit (lit)]++;
bincount[vlit (other)]++;
occs (lit).push_back (c);
occs (other).push_back (c);
continue;
}
candidates.push_back (c);
for (const auto &lit : *c) {
largecount[vlit (lit)]++;
}
}
if (size_limit == 2)
return;
// iterate counts of larger clauses rounds often
const unsigned rounds = opts.factorcandrounds;
unsigned candidates_before = 0;
for (unsigned round = 1; round <= rounds; round++) {
LOG ("factor round %d", round);
if (candidates.size () == candidates_before)
break;
ticks += 1 + cache_lines (candidates.size (), sizeof (Clause *));
candidates_before = candidates.size ();
vector<unsigned> newlargecount;
enlarge_zero (newlargecount, max_lit);
const auto begin = candidates.begin ();
auto p = candidates.begin ();
auto q = p;
const auto end = candidates.end ();
while (p != end) {
Clause *c = *q++ = *p++;
ticks++;
for (const auto &lit : *c) {
const auto idx = vlit (lit);
if (bincount[idx] + largecount[idx] < 2) {
q--;
goto CONTINUE_WITH_NEXT_CLAUSE;
}
}
for (const auto &lit : *c) {
const auto idx = vlit (lit);
newlargecount[idx]++;
}
CONTINUE_WITH_NEXT_CLAUSE:
continue;
}
candidates.resize (q - begin);
largecount.swap (newlargecount);
}
// finally push remaining clause on the occurrence stack
for (const auto &c : candidates)
for (const auto &lit : *c)
occs (lit).push_back (c);
}
// go back to two watch scheme
void Internal::reset_factor_mode () {
reset_occs ();
init_watches ();
connect_watches ();
}
Factoring::Factoring (Internal *i, int64_t l)
: internal (i), limit (l), schedule (i) {
const unsigned max_var = internal->max_var;
const unsigned max_lit = 2 * (max_var + 1);
initial = max_var;
bound = internal->lim.elimbound;
enlarge_zero (count, max_lit);
quotients.first = quotients.last = 0;
}
Factoring::~Factoring () {
CADICAL_assert (counted.empty ());
CADICAL_assert (nounted.empty ());
CADICAL_assert (flauses.empty ());
internal->release_quotients (*this);
schedule.erase (); // actually not necessary
}
double Internal::tied_next_factor_score (int lit) {
double res = occs (lit).size ();
LOG ("watches score %g of %d", res, lit);
return res;
}
// the marks in cadical have 6 bits for marking but work on idx
// to mark everything (FACTORS, QUOTIENT, NOUNTED) we shift the bits
// depending on the sign of factor (+ bitmask)
// i.e. if factor is positive, we apply a bitmask to only get
// the first three bits (& 7u)
// otherwise we leftshift by 3 (>> 3) to get the bits 4,5,6
// use markfact, unmarkfact, getfact for this purpose.
//
Quotient *Internal::new_quotient (Factoring &factoring, int factor) {
CADICAL_assert (!getfact (factor, FACTORS));
markfact (factor, FACTORS);
Quotient *res = new Quotient (factor);
res->next = 0;
res->matched = 0;
Quotient *last = factoring.quotients.last;
res->bid = 0;
if (last) {
CADICAL_assert (factoring.quotients.first);
CADICAL_assert (!last->next);
last->next = res;
res->id = last->id + 1;
} else {
CADICAL_assert (!factoring.quotients.first);
factoring.quotients.first = res;
res->id = 0;
}
factoring.quotients.last = res;
res->prev = last;
LOG ("new quotient[%zu] with factor %d", res->id, factor);
return res;
}
void Internal::release_quotients (Factoring &factoring) {
for (Quotient *q = factoring.quotients.first, *next; q; q = next) {
next = q->next;
int factor = q->factor;
CADICAL_assert (getfact (factor, FACTORS));
unmarkfact (factor, FACTORS);
delete q;
}
factoring.quotients.first = factoring.quotients.last = 0;
}
size_t Internal::first_factor (Factoring &factoring, int factor) {
CADICAL_assert (!factoring.quotients.first);
Quotient *quotient = new_quotient (factoring, factor);
vector<Clause *> &qlauses = quotient->qlauses;
int64_t ticks = 0;
for (const auto &c : occs (factor)) {
qlauses.push_back (c);
ticks++;
}
size_t res = qlauses.size ();
LOG ("quotient[0] factor %d size %zu", factor, res);
// This invariant can of course be broken by previous factorings
// CADICAL_assert (res > 1);
stats.ticks.factor += ticks;
return res;
}
void Internal::clear_nounted (vector<int> &nounted) {
for (const auto &lit : nounted) {
CADICAL_assert (getfact (lit, NOUNTED));
unmarkfact (lit, NOUNTED);
}
nounted.clear ();
}
void Internal::clear_flauses (vector<Clause *> &flauses) {
for (auto c : flauses) {
CADICAL_assert (c->swept);
c->swept = false;
}
flauses.clear ();
}
Quotient *Internal::best_quotient (Factoring &factoring,
size_t *best_reduction_ptr) {
size_t factors = 1, best_reduction = 0;
Quotient *best = 0;
for (Quotient *q = factoring.quotients.first; q; q = q->next) {
size_t quotients = q->qlauses.size ();
size_t before_factorization = quotients * factors;
size_t after_factorization = quotients + factors;
if (before_factorization == after_factorization)
LOG ("quotient[%zu] factors %zu clauses into %zu thus no change",
factors - 1, before_factorization, after_factorization);
else if (before_factorization < after_factorization)
LOG ("quotient[%zu] factors %zu clauses into %zu thus %zu more",
factors - 1, before_factorization, after_factorization,
after_factorization - before_factorization);
else {
size_t delta = before_factorization - after_factorization;
LOG ("quotient[%zu] factors %zu clauses into %zu thus %zu less",
factors - 1, before_factorization, after_factorization, delta);
if (!best || best_reduction < delta) {
best_reduction = delta;
best = q;
}
}
factors++;
}
if (!best) {
LOG ("no decreasing quotient found");
return 0;
}
LOG ("best decreasing quotient[%zu] with reduction %zu", best->id,
best_reduction);
*best_reduction_ptr = best_reduction;
return best;
}
int Internal::next_factor (Factoring &factoring, unsigned *next_count_ptr) {
Quotient *last_quotient = factoring.quotients.last;
CADICAL_assert (last_quotient);
vector<Clause *> &last_clauses = last_quotient->qlauses;
vector<unsigned> &count = factoring.count;
vector<int> &counted = factoring.counted;
vector<Clause *> &flauses = factoring.flauses;
CADICAL_assert (counted.empty ());
CADICAL_assert (flauses.empty ());
const int initial = factoring.initial;
int64_t ticks = 1 + cache_lines (last_clauses.size (), sizeof (Clause *));
for (auto c : last_clauses) {
CADICAL_assert (!c->swept);
int min_lit = 0;
unsigned factors = 0;
size_t min_size = 0;
ticks++;
for (const auto &other : *c) {
if (getfact (other, FACTORS)) {
if (factors++)
break;
} else {
CADICAL_assert (!getfact (other, QUOTIENT));
markfact (other, QUOTIENT);
const size_t other_size = occs (other).size ();
if (!min_lit || other_size < min_size) {
min_lit = other;
min_size = other_size;
}
}
}
CADICAL_assert (factors);
if (factors == 1) {
CADICAL_assert (min_lit);
const int c_size = c->size;
vector<int> &nounted = factoring.nounted;
CADICAL_assert (nounted.empty ());
ticks += 1 + cache_lines (occs (min_lit).size (), sizeof (Clause *));
for (auto d : occs (min_lit)) {
if (c == d)
continue;
ticks++;
if (d->swept)
continue;
if (d->size != c_size)
continue;
int next = 0;
for (const auto &other : *d) {
if (getfact (other, QUOTIENT))
continue;
if (getfact (other, FACTORS))
goto CONTINUE_WITH_NEXT_MIN_WATCH;
if (getfact (other, NOUNTED))
goto CONTINUE_WITH_NEXT_MIN_WATCH;
if (next)
goto CONTINUE_WITH_NEXT_MIN_WATCH;
next = other;
}
CADICAL_assert (next);
if (abs (next) > abs (initial))
continue;
if (!active (next))
continue;
CADICAL_assert (!getfact (next, FACTORS));
CADICAL_assert (!getfact (next, NOUNTED));
markfact (next, NOUNTED);
nounted.push_back (next);
d->swept = true;
flauses.push_back (d);
if (!count[vlit (next)])
counted.push_back (next);
count[vlit (next)]++;
CONTINUE_WITH_NEXT_MIN_WATCH:;
}
clear_nounted (nounted);
}
for (const auto &other : *c)
if (getfact (other, QUOTIENT))
unmarkfact (other, QUOTIENT);
stats.ticks.factor += ticks;
ticks = 0;
if (stats.ticks.factor > factoring.limit)
break;
}
clear_flauses (flauses);
unsigned next_count = 0;
int next = 0;
if (stats.ticks.factor <= factoring.limit) {
unsigned ties = 0;
for (const auto &lit : counted) {
const unsigned lit_count = count[vlit (lit)];
if (lit_count < next_count)
continue;
if (lit_count == next_count) {
CADICAL_assert (lit_count);
ties++;
} else {
CADICAL_assert (lit_count > next_count);
next_count = lit_count;
next = lit;
ties = 1;
}
}
if (next_count < 2) {
LOG ("next factor count %u smaller than 2", next_count);
next = 0;
} else if (ties > 1) {
LOG ("found %u tied next factor candidate literals with count %u",
ties, next_count);
double next_score = -1;
for (const auto &lit : counted) {
const unsigned lit_count = count[vlit (lit)];
if (lit_count != next_count)
continue;
double lit_score = tied_next_factor_score (lit);
CADICAL_assert (lit_score >= 0);
LOG ("score %g of next factor candidate %d", lit_score, lit);
if (lit_score <= next_score)
continue;
next_score = lit_score;
next = lit;
}
CADICAL_assert (next_score >= 0);
CADICAL_assert (next);
LOG ("best score %g of next factor %d", next_score, next);
} else {
CADICAL_assert (ties == 1);
LOG ("single next factor %d with count %u", next, next_count);
}
}
for (const auto &lit : counted)
count[vlit (lit)] = 0;
counted.clear ();
CADICAL_assert (!next || next_count > 1);
*next_count_ptr = next_count;
return next;
}
void Internal::factorize_next (Factoring &factoring, int next,
unsigned expected_next_count) {
Quotient *last_quotient = factoring.quotients.last;
Quotient *next_quotient = new_quotient (factoring, next);
CADICAL_assert (last_quotient);
vector<Clause *> &last_clauses = last_quotient->qlauses;
vector<Clause *> &next_clauses = next_quotient->qlauses;
vector<size_t> &matches = next_quotient->matches;
vector<Clause *> &flauses = factoring.flauses;
CADICAL_assert (flauses.empty ());
int64_t ticks = 1 + cache_lines (last_clauses.size (), sizeof (Clause *));
size_t i = 0;
for (auto c : last_clauses) {
CADICAL_assert (!c->swept);
int min_lit = 0;
unsigned factors = 0;
size_t min_size = 0;
ticks++;
for (const auto &other : *c) {
if (getfact (other, FACTORS)) {
if (factors++)
break;
} else {
CADICAL_assert (!getfact (other, QUOTIENT));
markfact (other, QUOTIENT);
const size_t other_size = occs (other).size ();
if (!min_lit || other_size < min_size) {
min_lit = other;
min_size = other_size;
}
}
}
CADICAL_assert (factors);
if (factors == 1) {
CADICAL_assert (min_lit);
const int c_size = c->size;
ticks += 1 + cache_lines (occs (min_lit).size (), sizeof (Clause *));
for (auto d : occs (min_lit)) {
if (c == d)
continue;
ticks++;
if (d->swept)
continue;
if (d->size != c_size)
continue;
for (const auto &other : *d) {
if (getfact (other, QUOTIENT))
continue;
if (other != next)
goto CONTINUE_WITH_NEXT_MIN_WATCH;
}
LOG (c, "matched");
LOG (d, "keeping");
next_clauses.push_back (d);
matches.push_back (i);
flauses.push_back (d);
d->swept = true;
break;
CONTINUE_WITH_NEXT_MIN_WATCH:;
}
}
for (const auto &other : *c)
if (getfact (other, QUOTIENT))
unmarkfact (other, QUOTIENT);
i++;
}
clear_flauses (flauses);
stats.ticks.factor += ticks;
CADICAL_assert (expected_next_count <= next_clauses.size ());
(void) expected_next_count;
}
// We only need to enlarge factoring.count as everything else is
// initialized in internal
void Internal::resize_factoring (Factoring &factoring, int lit) {
CADICAL_assert (lit > 0);
size_t new_var_size = lit + 1;
size_t new_lit_size = 2 * new_var_size;
enlarge_zero (factoring.count, new_lit_size);
}
void Internal::flush_unmatched_clauses (Quotient *q) {
Quotient *prev = q->prev;
vector<size_t> &q_matches = q->matches, &prev_matches = prev->matches;
vector<Clause *> &q_clauses = q->qlauses, &prev_clauses = prev->qlauses;
const size_t n = q_clauses.size ();
CADICAL_assert (n == q_matches.size ());
bool prev_is_first = !prev->id;
size_t i = 0;
while (i < q_matches.size ()) {
size_t j = q_matches[i];
q_matches[i] = i;
CADICAL_assert (i <= j);
if (!prev_is_first) {
size_t matches = prev_matches[j];
prev_matches[i] = matches;
}
Clause *c = prev_clauses[j];
prev_clauses[i] = c;
i++;
}
LOG ("flushing %zu clauses of quotient[%zu]", prev_clauses.size () - n,
prev->id);
if (!prev_is_first)
prev_matches.resize (n);
prev_clauses.resize (n);
}
// special case when we have two quotients with negated factors.
// in this case, factoring does not make sense, and instead we
// can resolve the clauses of the two quotients.
// this subsumes all clauses in all quotients.
void Internal::add_self_subsuming_factor (Quotient *q, Quotient *p) {
const int factor = q->factor;
const int not_factor = p->factor;
CADICAL_assert (-factor == not_factor);
LOG (
"adding self subsuming factor because blocked clause is a tautology");
for (auto c : q->qlauses) {
for (const auto &lit : *c) {
if (lit == factor)
continue;
clause.push_back (lit);
}
if (lrat) {
for (auto d : p->qlauses) {
bool match = true;
for (const auto &lit : *d) {
if (lit == not_factor)
continue;
if (std::find (clause.begin (), clause.end (), lit) ==
clause.end ()) {
match = false;
break;
}
}
if (match) {
lrat_chain.push_back (d->id);
break;
}
}
lrat_chain.push_back (c->id);
CADICAL_assert (lrat_chain.size () == 2);
}
if (clause.size () > 1) {
new_factor_clause ();
} else {
const int unit = clause[0];
const signed char tmp = val (unit);
if (!tmp)
assign_unit (unit);
else if (tmp < 0) {
if (lrat) {
int64_t id = unit_id (-unit);
lrat_chain.push_back (id);
std::reverse (lrat_chain.begin (), lrat_chain.end ());
}
learn_empty_clause ();
clause.clear ();
lrat_chain.clear ();
break;
}
}
clause.clear ();
lrat_chain.clear ();
}
}
bool Internal::self_subsuming_factor (Quotient *q) {
Quotient *x = 0, *y = 0;
bool found = false;
for (Quotient *p = q; p; p = p->prev) {
const int factor = p->factor;
Flags &f = flags (factor);
if (f.seen) {
CADICAL_assert (std::find (analyzed.begin (), analyzed.end (), -factor) !=
analyzed.end ());
found = true;
x = p;
for (Quotient *r = q; r; r = r->prev) {
if (r->factor != -factor)
continue;
y = r;
break;
}
break;
}
analyzed.push_back (factor);
f.seen = true;
}
CADICAL_assert (!found || (x && y));
clear_analyzed_literals ();
if (found) {
add_self_subsuming_factor (x, y);
return true;
}
return false;
}
// this is a pure binary clauses containing fresh and one other literal
// it is added for all applicable quotients.
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.clear ();
if (lrat)
mini_chain.push_back (-clause_id);
}
// this clause is blocked on fresh, i.e., it contains all literals from
// the binaries above, but negated. This is only added to the proof, to
// make checking easier.
void Internal::blocked_clause (Quotient *q, int not_fresh) {
if (!proof)
return;
int64_t new_id = ++clause_id;
q->bid = new_id;
CADICAL_assert (clause.empty ());
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);
mini_chain.clear ();
clause.clear ();
}
// this is the other side of the factored clauses. To derive these,
// one can resolved the blocked clause on all matching clauses of
// one type
void Internal::add_factored_quotient (Quotient *q, int not_fresh) {
LOG ("adding factored quotient[%zu] clauses", q->id);
const int factor = q->factor;
CADICAL_assert (lrat_chain.empty ());
auto qlauses = q->qlauses;
for (unsigned idx = 0; idx < qlauses.size (); idx++) {
const auto c = qlauses[idx];
CADICAL_assert (clause.empty ());
for (const auto &other : *c) {
if (other == factor) {
continue;
}
clause.push_back (other);
}
if (lrat) {
CADICAL_assert (proof);
CADICAL_assert (q->bid);
unsigned idxtoo = idx;
for (Quotient *p = q; p; p = p->prev) {
lrat_chain.push_back (p->qlauses[idxtoo]->id);
if (p->prev)
idxtoo = p->matches[idx];
}
lrat_chain.push_back (q->bid);
}
clause.push_back (not_fresh);
new_factor_clause ();
clause.clear ();
lrat_chain.clear ();
}
if (proof) {
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 ();
}
}
// remove deleted clauses once factored.
void Internal::eagerly_remove_from_occurences (Clause *c) {
for (const auto &lit : *c) {
auto &occ = occs (lit);
auto p = occ.begin ();
auto q = occ.begin ();
auto begin = occ.begin ();
auto end = occ.end ();
while (p != end) {
*q = *p++;
if (*q != c)
q++;
}
CADICAL_assert (q + 1 == p);
occ.resize (q - begin);
}
}
// delete the factored clauses
void Internal::delete_unfactored (Quotient *q) {
LOG ("deleting unfactored quotient[%zu] clauses", q->id);
for (auto c : q->qlauses) {
eagerly_remove_from_occurences (c);
mark_garbage (c);
stats.literals_unfactored += c->size;
stats.clauses_unfactored++;
}
}
// update the priority queue for scheduling
void Internal::update_factored (Factoring &factoring, Quotient *q) {
const int factor = q->factor;
update_factor_candidate (factoring, factor);
update_factor_candidate (factoring, -factor);
for (auto c : q->qlauses) {
LOG (c, "deleting unfactored");
for (const auto &lit : *c)
if (lit != factor)
update_factor_candidate (factoring, lit);
}
}
bool Internal::apply_factoring (Factoring &factoring, Quotient *q) {
for (Quotient *p = q; p->prev; p = p->prev)
flush_unmatched_clauses (p);
if (self_subsuming_factor (q)) {
for (Quotient *p = q; p; p = p->prev)
delete_unfactored (p);
for (Quotient *p = q; p; p = p->prev)
update_factored (factoring, p);
return true;
}
const int fresh = get_new_extension_variable ();
if (!fresh)
return false;
stats.factored++;
factoring.fresh.push_back (fresh);
for (Quotient *p = q; p; p = p->prev)
add_factored_divider (p, fresh);
const int not_fresh = -fresh;
blocked_clause (q, not_fresh);
add_factored_quotient (q, not_fresh);
for (Quotient *p = q; p; p = p->prev)
delete_unfactored (p);
for (Quotient *p = q; p; p = p->prev)
update_factored (factoring, p);
CADICAL_assert (fresh > 0);
resize_factoring (factoring, fresh);
return true;
}
void Internal::update_factor_candidate (Factoring &factoring, int lit) {
FactorSchedule &schedule = factoring.schedule;
const size_t size = occs (lit).size ();
const unsigned idx = vlit (lit);
if (schedule.contains (idx))
schedule.update (idx);
else if (size > 1) {
schedule.push_back (idx);
}
}
void Internal::schedule_factorization (Factoring &factoring) {
for (const auto &idx : vars) {
if (active (idx)) {
Flags &f = flags (idx);
const int lit = idx;
const int not_lit = -lit;
if (f.factor & 1)
update_factor_candidate (factoring, lit);
if (f.factor & 2)
update_factor_candidate (factoring, not_lit);
}
}
#ifndef CADICAL_QUIET
size_t size_cands = factoring.schedule.size ();
VERBOSE (2, "scheduled %zu factorization candidate literals %.0f %%",
size_cands, percent (size_cands, max_var));
#endif
}
bool Internal::run_factorization (int64_t limit) {
Factoring factoring = Factoring (this, limit);
schedule_factorization (factoring);
bool done = false;
#ifndef CADICAL_QUIET
unsigned factored = 0;
#endif
int64_t *ticks = &stats.ticks.factor;
VERBOSE (3, "factorization limit of %" PRIu64 " ticks", limit - *ticks);
while (!unsat && !done && !factoring.schedule.empty ()) {
const unsigned ufirst = factoring.schedule.pop_front ();
LOG ("next factor candidate %d", ufirst);
const int first = u2i (ufirst);
const int first_idx = vidx (first);
if (!active (first_idx))
continue;
if (!occs (first).size ()) {
factoring.schedule.clear ();
break;
}
if (*ticks > limit) {
VERBOSE (2, "factorization ticks limit hit");
break;
}
if (terminated_asynchronously ())
break;
Flags &f = flags (first_idx);
const unsigned bit = 1u << (first < 0);
if (!(f.factor & bit))
continue;
f.factor &= ~bit;
const size_t first_count = first_factor (factoring, first);
if (first_count > 1) {
for (;;) {
unsigned next_count;
const int next = next_factor (factoring, &next_count);
if (next == 0)
break;
CADICAL_assert (next_count > 1);
if (next_count < 2)
break;
factorize_next (factoring, next, next_count);
}
size_t reduction;
Quotient *q = best_quotient (factoring, &reduction);
if (q && (int) reduction > factoring.bound) {
if (apply_factoring (factoring, q)) {
#ifndef CADICAL_QUIET
factored++;
#endif
} else
done = true;
}
}
release_quotients (factoring);
}
// since we cannot remove elements from the heap we check wether the
// first element in the heap has occurences
bool completed = factoring.schedule.empty ();
if (!completed) {
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
#ifndef CADICAL_QUIET
report ('f', !factored);
#endif
return completed;
}
int Internal::get_new_extension_variable () {
const int current_max_external = external->max_var;
const int new_external = current_max_external + 1;
const int new_internal = external->internalize (new_external, true);
// one sideeffect of internalize is enlarging the internal datastructures
// which can initialize the watches (wtab)
if (watching ())
reset_watches ();
// it does not enlarge otab, however, so we do this manually
init_occs ();
CADICAL_assert (vlit (new_internal));
return new_internal;
}
bool Internal::factor () {
if (unsat)
return false;
if (terminated_asynchronously ())
return false;
if (!opts.factor)
return false;
// The following CADICAL_assertion fails if there are *only* user propagator
// clauses (which are redundant).
// CADICAL_assert (stats.mark.factor || clauses.empty ());
if (last.factor.marked >= stats.mark.factor) {
VERBOSE (3,
"factorization skipped as no literals have been"
"marked to be added (%" PRIu64 " < %" PRIu64 ")",
last.factor.marked, stats.mark.factor);
return false;
}
CADICAL_assert (!level);
SET_EFFORT_LIMIT (limit, factor, stats.factor);
if (!stats.factor)
limit += opts.factoriniticks * 1e6;
START_SIMPLIFIER (factor, FACTOR);
stats.factor++;
#ifndef CADICAL_QUIET
struct {
int64_t variables, clauses, ticks;
} before, after, delta;
before.variables = stats.variables_extension + stats.variables_original;
before.ticks = stats.ticks.factor;
before.clauses = stats.current.irredundant;
#endif
factor_mode ();
bool completed = run_factorization (limit);
reset_factor_mode ();
propagated = 0;
if (!unsat && !propagate ()) {
learn_empty_clause ();
}
#ifndef CADICAL_QUIET
after.variables = stats.variables_extension + stats.variables_original;
after.clauses = stats.current.irredundant;
after.ticks = stats.ticks.factor;
delta.variables = after.variables - before.variables;
delta.clauses = before.clauses - after.clauses;
delta.ticks = after.ticks - before.ticks;
VERBOSE (2, "used %f million factorization ticks", delta.ticks * 1e-6);
phase ("factorization", stats.factor,
"introduced %" PRId64 " extension variables %.0f%%",
delta.variables, percent (delta.variables, before.variables));
phase ("factorization", stats.factor,
"removed %" PRId64 " irredundant clauses %.0f%%", delta.clauses,
percent (delta.clauses, before.clauses));
#endif
if (completed)
last.factor.marked = stats.mark.factor;
STOP_SIMPLIFIER (factor, FACTOR);
return true;
}
} // namespace CaDiCaL
ABC_NAMESPACE_IMPL_END

View File

@ -1,527 +0,0 @@
#include "global.h"
#include "internal.hpp"
/*------------------------------------------------------------------------*/
// Some more low-level 'C' headers.
extern "C" {
#include <errno.h>
#include <fcntl.h>
#include <stdlib.h>
#include <string.h>
#include <sys/stat.h>
#include <sys/types.h>
}
#ifdef WIN32
#include <windows.h>
#include <io.h>
#include <cstdio>
#define access _access
#define popen _popen
#define pclose _pclose
#define R_OK 4
#define W_OK 2
#define S_IFIFO _S_IFIFO
#define S_ISFIFO(mode) (((mode) & S_IFMT) == S_IFIFO)
#define S_ISDIR(mode) (((mode) & S_IFMT) == S_IFDIR)
#else
extern "C" {
#include <sys/wait.h>
#include <unistd.h>
}
#endif
#if defined(__APPLE__) || defined(__MACH__)
extern "C" {
#include <libproc.h>
#include <sys/proc_info.h>
}
#include <mutex>
#endif
ABC_NAMESPACE_IMPL_START
/*------------------------------------------------------------------------*/
namespace CaDiCaL {
/*------------------------------------------------------------------------*/
// Private constructor.
File::File (Internal *i, bool w, int c, int p, FILE *f, const char *n)
: internal (i),
#if !defined(CADICAL_QUIET) || !defined(CADICAL_NDEBUG)
writing (w),
#endif
close_file (c), child_pid (p), file (f), _name (strdup (n)),
_lineno (1), _bytes (0) {
(void) w;
CADICAL_assert (f), CADICAL_assert (n);
}
/*------------------------------------------------------------------------*/
bool File::exists (const char *path) {
struct stat buf;
if (stat (path, &buf))
return false;
if (access (path, R_OK))
return false;
return true;
}
bool File::writable (const char *path) {
int res;
if (!path)
res = 1;
else if (!strcmp (path, "/dev/null"))
res = 0;
else {
if (!*path)
res = 2;
else {
struct stat buf;
const char *p = strrchr (path, '/');
if (!p) {
if (stat (path, &buf))
res = ((errno == ENOENT) ? 0 : -2);
else if (S_ISDIR (buf.st_mode))
res = 3;
else
res = (access (path, W_OK) ? 4 : 0);
} else if (!p[1])
res = 5;
else {
size_t len = p - path;
char *dirname = new char[len + 1];
strncpy (dirname, path, len);
dirname[len] = 0;
if (stat (dirname, &buf))
res = 6;
else if (!S_ISDIR (buf.st_mode))
res = 7;
else if (access (dirname, W_OK))
res = 8;
else if (stat (path, &buf))
res = (errno == ENOENT) ? 0 : -3;
else
res = access (path, W_OK) ? 9 : 0;
delete[] dirname;
}
}
}
return !res;
}
bool File::piping () {
struct stat stat;
int fd = fileno (file);
if (fstat (fd, &stat))
return true;
return S_ISFIFO (stat.st_mode);
}
// These are signatures for supported compressed file types. In 2018 the
// SAT Competition was running on StarExec and used internally 'bzip2'
// compressed files, but gave them uncompressed to the solver using exactly
// the same path (with '.bz2' suffix). Then 'CaDiCaL' tried to read that
// actually uncompressed file through 'bzip2', which of course failed. Now
// we double check and fall back to reading the file as is, if the signature
// does not match after issuing a warning.
static int xzsig[] = {0xFD, 0x37, 0x7A, 0x58, 0x5A, 0x00, 0x00, EOF};
static int bz2sig[] = {0x42, 0x5A, 0x68, EOF};
static int gzsig[] = {0x1F, 0x8B, EOF};
static int sig7z[] = {0x37, 0x7A, 0xBC, 0xAF, 0x27, 0x1C, EOF};
static int lzmasig[] = {0x5D, EOF};
bool File::match (Internal *internal, const char *path, const int *sig) {
CADICAL_assert (path);
FILE *tmp = fopen (path, "r");
if (!tmp) {
WARNING ("failed to open '%s' to check signature", path);
return false;
}
bool res = true;
for (const int *p = sig; res && (*p != EOF); p++)
res = (cadical_getc_unlocked (tmp) == *p);
fclose (tmp);
if (!res)
WARNING ("file type signature check for '%s' failed", path);
return res;
}
size_t File::size (const char *path) {
struct stat buf;
if (stat (path, &buf))
return 0;
return (size_t) buf.st_size;
}
// Check that 'prg' is in the 'PATH' and thus can be found if executed
// through 'popen' or 'exec'.
char *File::find_program (const char *prg) {
size_t prglen = strlen (prg);
const char *c = getenv ("PATH");
if (!c)
return 0;
size_t len = strlen (c);
char *e = new char[len + 1];
strcpy (e, c);
char *res = 0;
for (char *p = e, *q; !res && p < e + len; p = q) {
for (q = p; *q && *q != ':'; q++)
;
*q++ = 0;
size_t pathlen = (q - p) + prglen;
char *path = new char[pathlen + 1];
snprintf (path, pathlen + 1, "%s/%s", p, prg);
CADICAL_assert (strlen (path) == pathlen);
if (exists (path))
res = path;
else
delete[] path;
}
delete[] e;
return res;
}
/*------------------------------------------------------------------------*/
FILE *File::open_file (Internal *internal, const char *path,
const char *mode) {
(void) internal;
return fopen (path, mode);
}
FILE *File::read_file (Internal *internal, const char *path) {
MSG ("opening file to read '%s'", path);
return open_file (internal, path, "r");
}
FILE *File::write_file (Internal *internal, const char *path) {
MSG ("opening file to write '%s'", path);
return open_file (internal, path, "wb");
}
/*------------------------------------------------------------------------*/
void File::split_str (const char *command, std::vector<char *> &argv) {
const char *c = command;
while (*c && *c == ' ')
c++;
while (*c) {
const char *p = c;
while (*p && *p != ' ')
p++;
const size_t bytes = p - c;
char *arg = new char[bytes + 1];
(void) strncpy (arg, c, bytes);
arg[bytes] = 0;
argv.push_back (arg);
while (*p && *p == ' ')
p++;
c = p;
}
}
void File::delete_str_vector (std::vector<char *> &argv) {
for (auto str : argv)
delete[] str;
}
FILE *File::open_pipe (Internal *internal, const char *fmt,
const char *path, const char *mode) {
#ifdef CADICAL_QUIET
(void) internal;
#endif
size_t prglen = 0;
while (fmt[prglen] && fmt[prglen] != ' ')
prglen++;
char *prg = new char[prglen + 1];
strncpy (prg, fmt, prglen);
prg[prglen] = 0;
char *found = find_program (prg);
if (found)
MSG ("found '%s' in path for '%s'", found, prg);
if (!found)
MSG ("did not find '%s' in path", prg);
delete[] prg;
if (!found)
return 0;
delete[] found;
size_t cmd_size = strlen (fmt) + strlen (path);
char *cmd = new char[cmd_size];
snprintf (cmd, cmd_size, fmt, path);
FILE *res = popen (cmd, mode);
delete[] cmd;
return res;
}
FILE *File::read_pipe (Internal *internal, const char *fmt, const int *sig,
const char *path) {
if (!File::exists (path)) {
LOG ("file '%s' does not exist", path);
return 0;
}
LOG ("file '%s' exists", path);
if (sig && !File::match (internal, path, sig))
return 0;
LOG ("file '%s' matches signature for '%s'", path, fmt);
MSG ("opening pipe to read '%s'", path);
return open_pipe (internal, fmt, path, "r");
}
#ifndef _WIN32
#if defined(__APPLE__) || defined(__MACH__)
static std::mutex compressed_file_writing_mutex;
#endif
FILE *File::write_pipe (Internal *internal, const char *command,
const char *path, int &child_pid) {
CADICAL_assert (command[0] && command[0] != ' ');
MSG ("writing through command '%s' to '%s'", command, path);
#ifdef CADICAL_QUIET
(void) internal;
#endif
std::vector<char *> args;
split_str (command, args);
CADICAL_assert (!args.empty ());
args.push_back (0);
char **argv = args.data ();
char *absolute_command_path = find_program (argv[0]);
int pipe_fds[2], out;
FILE *res = 0;
#if defined(__APPLE__) || defined(__MACH__)
compressed_file_writing_mutex.lock ();
#endif
if (!absolute_command_path)
MSG ("could not find '%s' in 'PATH' environment variable", argv[0]);
else if (::pipe (pipe_fds) < 0)
MSG ("could not generate pipe to '%s' command", command);
else if ((out = ::open (path, O_CREAT | O_TRUNC | O_WRONLY, 0644)) < 0)
MSG ("could not open '%s' for writing", path);
else if ((child_pid = ::fork ()) < 0) {
MSG ("could not fork process to execute '%s' command", command);
::close (out);
} else if (child_pid) {
::close (pipe_fds[0]);
res = ::fdopen (pipe_fds[1], "wb");
} else {
// Connect stdin and stdout in child
::dup2 (pipe_fds[0], 0);
::dup2 (out, 1);
// Make sure to close all non-required fds to not cause hangs.
// This is handled now by closefrom and remains for documentation
// purposes:
//
// ::close (pipe_fds[0]);
// ::close (pipe_fds[1]);
// ::close (out);
// Surpress '7z' verbose output on 'stderr'.
if (command[0] == '7') {
::close (2);
}
// Before the fork another thread could have created more fds. These
// fds are cloned into the child process. As this inhibits pipes to
// be closed by the parent process we have to close all of the
// erroneously cloned fds here.
#ifndef CADICAL_NCLOSEFROM
::closefrom (3);
#else
// Simplistic replacement on Unix without 'closefrom'.
for (int fd = 3; fd != FD_SETSIZE; fd++)
::close (fd);
#endif
execv (absolute_command_path, argv);
_exit (1);
}
if (absolute_command_path)
delete[] absolute_command_path;
delete_str_vector (args);
#ifdef CADICAL_QUIET
(void) internal;
#endif
#if defined(__APPLE__) || defined(__MACH__)
if (!res)
compressed_file_writing_mutex.unlock ();
#endif
return res;
}
#endif
/*------------------------------------------------------------------------*/
File *File::read (Internal *internal, FILE *f, const char *n) {
return new File (internal, false, 0, 0, f, n);
}
File *File::write (Internal *internal, FILE *f, const char *n) {
return new File (internal, true, 0, 0, f, n);
}
File *File::read (Internal *internal, const char *path) {
FILE *file;
int close_input = 2;
if (has_suffix (path, ".xz")) {
file = read_pipe (internal, "xz -c -d %s", xzsig, path);
if (!file)
goto READ_FILE;
} else if (has_suffix (path, ".lzma")) {
file = read_pipe (internal, "lzma -c -d %s", lzmasig, path);
if (!file)
goto READ_FILE;
} else if (has_suffix (path, ".bz2")) {
file = read_pipe (internal, "bzip2 -c -d %s", bz2sig, path);
if (!file)
goto READ_FILE;
} else if (has_suffix (path, ".gz")) {
file = read_pipe (internal, "gzip -c -d %s", gzsig, path);
if (!file)
goto READ_FILE;
} else if (has_suffix (path, ".7z")) {
file = read_pipe (internal, "7z x -so %s 2>/dev/null", sig7z, path);
if (!file)
goto READ_FILE;
} else {
READ_FILE:
file = read_file (internal, path);
close_input = 1;
}
if (!file)
return 0;
return new File (internal, false, close_input, 0, file, path);
}
File *File::write (Internal *internal, const char *path) {
FILE *file;
int close_output = 3, child_pid = 0;
#ifndef _WIN32
if (has_suffix (path, ".xz"))
file = write_pipe (internal, "xz -c", path, child_pid);
else if (has_suffix (path, ".bz2"))
file = write_pipe (internal, "bzip2 -c", path, child_pid);
else if (has_suffix (path, ".gz"))
file = write_pipe (internal, "gzip -c", path, child_pid);
else if (has_suffix (path, ".7z"))
file = write_pipe (internal, "7z a -an -txz -si -so", path, child_pid);
else
#endif
file = write_file (internal, path), close_output = 1;
if (!file)
return 0;
return new File (internal, true, close_output, child_pid, file, path);
}
void File::close (bool print) {
CADICAL_assert (file);
#ifndef CADICAL_QUIET
if (internal->opts.quiet)
print = false;
else if (internal->opts.verbose > 0)
print = true;
#endif
if (close_file == 0) {
if (print)
MSG ("disconnecting from '%s'", name ());
}
if (close_file == 1) {
if (print)
MSG ("closing file '%s'", name ());
fclose (file);
}
if (close_file == 2) {
if (print)
MSG ("closing input pipe to read '%s'", name ());
pclose (file);
}
#ifndef _WIN32
if (close_file == 3) {
if (print)
MSG ("closing output pipe to write '%s'", name ());
fclose (file);
waitpid (child_pid, 0, 0);
#if defined(__APPLE__) || defined(__MACH__)
compressed_file_writing_mutex.unlock ();
#endif
}
#endif
file = 0; // mark as closed
// TODO what about error checking for 'fclose', 'pclose' or 'waitpid'?
#ifndef CADICAL_QUIET
if (print) {
if (writing) {
uint64_t written_bytes = bytes ();
double written_mb = written_bytes / (double) (1 << 20);
MSG ("after writing %" PRIu64 " bytes %.1f MB", written_bytes,
written_mb);
if (close_file == 3) {
size_t actual_bytes = size (name ());
if (actual_bytes) {
double actual_mb = actual_bytes / (double) (1 << 20);
MSG ("deflated to %zd bytes %.1f MB", actual_bytes, actual_mb);
MSG ("factor %.2f (%.2f%% compression)",
relative (written_bytes, actual_bytes),
percent (actual_bytes, written_bytes));
} else
MSG ("but could not determine actual size of written file");
}
} else {
uint64_t read_bytes = bytes ();
double read_mb = read_bytes / (double) (1 << 20);
MSG ("after reading %" PRIu64 " bytes %.1f MB", read_bytes, read_mb);
if (close_file == 2) {
size_t actual_bytes = size (name ());
double actual_mb = actual_bytes / (double) (1 << 20);
MSG ("inflated from %zd bytes %.1f MB", actual_bytes, actual_mb);
MSG ("factor %.2f (%.2f%% compression)",
relative (read_bytes, actual_bytes),
percent (actual_bytes, read_bytes));
}
}
}
#endif
}
void File::flush () {
CADICAL_assert (file);
fflush (file);
}
File::~File () {
if (file)
close ();
free (_name);
}
} // namespace CaDiCaL
ABC_NAMESPACE_IMPL_END

View File

@ -1,141 +0,0 @@
#include "global.h"
#include "internal.hpp"
ABC_NAMESPACE_IMPL_START
namespace CaDiCaL {
void Internal::mark_fixed (int lit) {
if (external->fixed_listener) {
int elit = externalize (lit);
CADICAL_assert (elit);
const int eidx = abs (elit);
if (!external->ervars[eidx])
external->fixed_listener->notify_fixed_assignment (elit);
}
Flags &f = flags (lit);
CADICAL_assert (f.status == Flags::ACTIVE);
f.status = Flags::FIXED;
LOG ("fixed %d", abs (lit));
stats.all.fixed++;
stats.now.fixed++;
stats.inactive++;
CADICAL_assert (stats.active);
stats.active--;
CADICAL_assert (!active (lit));
CADICAL_assert (f.fixed ());
if (external_prop && private_steps) {
// If pre/inprocessing found a fixed assignment, we want the propagator
// 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);
}
}
void Internal::mark_eliminated (int lit) {
Flags &f = flags (lit);
CADICAL_assert (f.status == Flags::ACTIVE);
f.status = Flags::ELIMINATED;
LOG ("eliminated %d", abs (lit));
stats.all.eliminated++;
stats.now.eliminated++;
stats.inactive++;
CADICAL_assert (stats.active);
stats.active--;
CADICAL_assert (!active (lit));
CADICAL_assert (f.eliminated ());
}
void Internal::mark_pure (int lit) {
Flags &f = flags (lit);
CADICAL_assert (f.status == Flags::ACTIVE);
f.status = Flags::PURE;
LOG ("pure %d", abs (lit));
stats.all.pure++;
stats.now.pure++;
stats.inactive++;
CADICAL_assert (stats.active);
stats.active--;
CADICAL_assert (!active (lit));
CADICAL_assert (f.pure ());
}
void Internal::mark_substituted (int lit) {
Flags &f = flags (lit);
CADICAL_assert (f.status == Flags::ACTIVE);
f.status = Flags::SUBSTITUTED;
LOG ("substituted %d", abs (lit));
stats.all.substituted++;
stats.now.substituted++;
stats.inactive++;
CADICAL_assert (stats.active);
stats.active--;
CADICAL_assert (!active (lit));
CADICAL_assert (f.substituted ());
}
void Internal::mark_active (int lit) {
Flags &f = flags (lit);
CADICAL_assert (f.status == Flags::UNUSED);
f.status = Flags::ACTIVE;
LOG ("activate %d previously unused", abs (lit));
CADICAL_assert (stats.inactive);
stats.inactive--;
CADICAL_assert (stats.unused);
stats.unused--;
stats.active++;
CADICAL_assert (active (lit));
}
void Internal::reactivate (int lit) {
CADICAL_assert (!active (lit));
Flags &f = flags (lit);
CADICAL_assert (f.status != Flags::FIXED);
CADICAL_assert (f.status != Flags::UNUSED);
#ifdef LOGGING
const char *msg = 0;
#endif
switch (f.status) {
default:
case Flags::ELIMINATED:
CADICAL_assert (f.status == Flags::ELIMINATED);
CADICAL_assert (stats.now.eliminated > 0);
stats.now.eliminated--;
#ifdef LOGGING
msg = "eliminated";
#endif
break;
case Flags::SUBSTITUTED:
#ifdef LOGGING
msg = "substituted";
#endif
CADICAL_assert (stats.now.substituted > 0);
stats.now.substituted--;
break;
case Flags::PURE:
#ifdef LOGGING
msg = "pure literal";
#endif
CADICAL_assert (stats.now.pure > 0);
stats.now.pure--;
break;
}
#ifdef LOGGING
CADICAL_assert (msg);
LOG ("reactivate previously %s %d", msg, abs (lit));
#endif
f.status = Flags::ACTIVE;
f.sweep = false;
CADICAL_assert (active (lit));
stats.reactivated++;
CADICAL_assert (stats.inactive > 0);
stats.inactive--;
stats.active++;
}
} // namespace CaDiCaL
ABC_NAMESPACE_IMPL_END

View File

@ -1,275 +0,0 @@
#include "global.h"
#include "internal.hpp"
ABC_NAMESPACE_IMPL_START
namespace CaDiCaL {
bool Internal::flip (int lit) {
// Do not try to flip inactive literals except for unused variables.
if (!active (lit) && !flags (lit).unused ())
return false;
/*
if (flags (lit).unused ()) {
CADICAL_assert (lit <= max_var);
mark_active (lit);
set_val (lit, 1);
return true;
}
*/
// TODO: Unused case is not handled yet.
// if (flags (lit).unused ()) return false;
// Need to reestablish proper watching invariants as if there are no
// blocking literals as flipping in principle does not work with them.
if (propergated < trail.size ())
propergate ();
LOG ("trying to flip %d", lit);
const int idx = vidx (lit);
const signed char original_value = vals[idx];
CADICAL_assert (original_value);
lit = original_value < 0 ? -idx : idx;
CADICAL_assert (val (lit) > 0);
// Here we go over all the clauses in which 'lit' is watched by 'lit' and
// check whether assigning 'lit' to false would break watching invariants
// or even make the clause false. We also try to find replacement
// watches in case this fixes the watching invariant. This code is very
// similar to propagation of a literal in 'Internal::propagate'.
bool res = true;
Watches &ws = watches (lit);
const const_watch_iterator eow = ws.end ();
watch_iterator bow = ws.begin ();
// We first go over binary watches/clauses first as this is cheaper and
// has higher chance of failure and we can not use blocking literals.
for (const_watch_iterator i = bow; i != eow; i++) {
const Watch w = *i;
if (!w.binary ())
continue;
const signed char b = val (w.blit);
if (b > 0)
continue;
CADICAL_assert (b < 0);
res = false;
break;
}
if (res) {
const_watch_iterator i = bow;
watch_iterator j = bow;
while (i != eow) {
const Watch w = *j++ = *i++;
if (w.binary ())
continue;
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)
continue;
const int size = w.clause->size;
const literal_iterator middle = lits + w.clause->pos;
const const_literal_iterator end = lits + size;
literal_iterator k = middle;
int r = 0;
signed char v = -1;
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++;
}
if (v < 0) {
res = false;
break;
}
CADICAL_assert (v > 0);
CADICAL_assert (lits + 2 <= k), CADICAL_assert (k <= w.clause->end ());
w.clause->pos = k - lits;
lits[0] = other, lits[1] = r, *k = lit;
watch_literal (r, lit, w.clause);
j--;
}
if (j != i) {
while (i != eow)
*j++ = *i++;
ws.resize (j - ws.begin ());
}
}
#ifdef LOGGING
if (res)
LOG ("literal %d can be flipped", lit);
else
LOG ("literal %d can not be flipped", lit);
#endif
if (res) {
const int idx = vidx (lit);
const signed char original_value = vals[idx];
CADICAL_assert (original_value);
lit = original_value < 0 ? -idx : idx;
CADICAL_assert (val (lit) > 0);
LOG ("flipping value of %d = 1 to %d = -1", lit, lit);
set_val (idx, -original_value);
CADICAL_assert (val (-lit) > 0);
CADICAL_assert (val (lit) < 0);
Var &v = var (idx);
CADICAL_assert (trail[v.trail] == lit);
trail[v.trail] = -lit;
if (opts.ilb) {
if (!tainted_literal)
tainted_literal = lit;
else {
CADICAL_assert (val (tainted_literal));
if (v.level < var (tainted_literal).level) {
tainted_literal = lit;
}
}
}
} else
LOG ("flipping value of %d failed", lit);
return res;
}
bool Internal::flippable (int lit) {
// Can not check inactive literals except for unused variables.
if (!active (lit) && !flags (lit).unused ())
return false;
/*
if (flags (lit).unused ()) {
CADICAL_assert (lit <= max_var);
mark_active (lit);
return true;
}
*/
// TODO: Unused case is not handled yet
// if (flags (lit).unused ()) return false;
// Need to reestablish proper watching invariants as if there are no
// blocking literals as flipping in principle does not work with them.
if (propergated < trail.size ())
propergate ();
LOG ("checking whether %d is flippable", lit);
const int idx = vidx (lit);
const signed char original_value = vals[idx];
CADICAL_assert (original_value);
lit = original_value < 0 ? -idx : idx;
CADICAL_assert (val (lit) > 0);
// Here we go over all the clauses in which 'lit' is watched by 'lit' and
// check whether assigning 'lit' to false would break watching invariants
// or even make the clause false. In contrast to 'flip' we do not try to
// find replacement literals but do use blocking literals'. Therefore we
// also do not split the traversal code into two parts.
bool res = true;
Watches &ws = watches (lit);
const const_watch_iterator eow = ws.end ();
for (watch_iterator i = ws.begin (); i != eow; i++) {
const Watch w = *i;
const signed char b = val (w.blit);
if (b > 0)
continue;
CADICAL_assert (b < 0);
if (w.binary ()) {
res = false;
break;
}
if (w.clause->garbage)
continue;
literal_iterator lits = w.clause->begin ();
const int other = lits[0] ^ lits[1] ^ lit;
const signed char u = val (other);
if (u > 0) {
i->blit = other;
continue;
}
const int size = w.clause->size;
const literal_iterator middle = lits + w.clause->pos;
const const_literal_iterator end = lits + size;
literal_iterator k = middle;
int r = 0;
signed char v = -1;
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++;
}
if (v < 0) {
res = false;
break;
}
CADICAL_assert (v > 0);
CADICAL_assert (lits + 2 <= k);
CADICAL_assert (k <= w.clause->end ());
w.clause->pos = k - lits;
i->blit = r;
}
#ifdef LOGGING
if (res)
LOG ("literal %d can be flipped", lit);
else
LOG ("literal %d can not be flipped", lit);
#endif
return res;
}
} // namespace CaDiCaL
ABC_NAMESPACE_IMPL_END

View File

@ -1,95 +0,0 @@
#include "global.h"
#include "internal.hpp"
ABC_NAMESPACE_IMPL_START
namespace CaDiCaL {
void Format::enlarge () {
char *old = buffer;
buffer = new char[size = size ? 2 * size : 1];
memcpy (buffer, old, count);
delete[] old;
}
inline void Format::push_char (char ch) {
if (size == count)
enlarge ();
buffer[count++] = ch;
}
void Format::push_string (const char *s) {
char ch;
while ((ch = *s++))
push_char (ch);
}
void Format::push_int (int d) {
char tmp[16];
snprintf (tmp, sizeof tmp, "%d", d);
push_string (tmp);
}
void Format::push_uint64 (uint64_t u) {
char tmp[16];
snprintf (tmp, sizeof tmp, "%" PRIu64, u);
push_string (tmp);
}
static bool match_format (const char *&str, const char *pattern) {
CADICAL_assert (pattern);
const char *p = str;
const char *q = pattern;
while (*q)
if (*q++ != *p++)
return false;
str = p;
return true;
}
const char *Format::add (const char *fmt, va_list &ap) {
const char *p = fmt;
char ch;
while ((ch = *p++)) {
if (ch != '%')
push_char (ch);
else if (*p == 'c')
push_char (va_arg (ap, int)), p++;
else if (*p == 'd')
push_int (va_arg (ap, int)), p++;
else if (*p == 's')
push_string (va_arg (ap, const char *)), p++;
else if (match_format (p, PRIu64))
push_uint64 (va_arg (ap, uint64_t));
else {
push_char ('%');
push_char (*p);
break;
} // unsupported
}
push_char (0);
count--; // thus automatic append in subsequent calls.
return buffer;
}
const char *Format::init (const char *fmt, ...) {
count = 0;
va_list ap;
va_start (ap, fmt);
const char *res = add (fmt, ap);
va_end (ap);
return res;
}
const char *Format::append (const char *fmt, ...) {
va_list ap;
va_start (ap, fmt);
const char *res = add (fmt, ap);
va_end (ap);
return res;
}
} // namespace CaDiCaL
ABC_NAMESPACE_IMPL_END

View File

@ -1,283 +0,0 @@
#include "global.h"
#include "internal.hpp"
ABC_NAMESPACE_IMPL_START
namespace CaDiCaL {
/*------------------------------------------------------------------------*/
FratTracer::FratTracer (Internal *i, File *f, bool b, bool a)
: internal (i), file (f), binary (b), with_antecedents (a)
#ifndef CADICAL_QUIET
,
added (0), deleted (0), finalized (0), original (0)
#endif
{
(void) internal;
}
void FratTracer::connect_internal (Internal *i) {
internal = i;
file->connect_internal (internal);
LOG ("FRAT TRACER connected to internal");
}
FratTracer::~FratTracer () {
LOG ("FRAT TRACER delete");
delete file;
}
/*------------------------------------------------------------------------*/
inline void FratTracer::put_binary_zero () {
CADICAL_assert (binary);
CADICAL_assert (file);
file->put ((unsigned char) 0);
}
inline void FratTracer::put_binary_lit (int lit) {
CADICAL_assert (binary);
CADICAL_assert (file);
CADICAL_assert (lit != INT_MIN);
unsigned x = 2 * abs (lit) + (lit < 0);
unsigned char ch;
while (x & ~0x7f) {
ch = (x & 0x7f) | 0x80;
file->put (ch);
x >>= 7;
}
ch = x;
file->put (ch);
}
inline void FratTracer::put_binary_id (int64_t id, bool can_be_negative) {
CADICAL_assert (binary);
CADICAL_assert (file);
uint64_t x = abs (id);
if (can_be_negative) {
x = 2 * x + (id < 0);
}
unsigned char ch;
while (x & ~0x7f) {
ch = (x & 0x7f) | 0x80;
file->put (ch);
x >>= 7;
}
ch = x;
file->put (ch);
}
/*------------------------------------------------------------------------*/
void FratTracer::frat_add_original_clause (int64_t id,
const vector<int> &clause) {
if (binary)
file->put ('o');
else
file->put ("o ");
if (binary)
put_binary_id (id);
else
file->put (id), file->put (" ");
for (const auto &external_lit : clause)
if (binary)
put_binary_lit (external_lit);
else
file->put (external_lit), file->put (' ');
if (binary)
put_binary_zero ();
else
file->put ("0\n");
}
void FratTracer::frat_add_derived_clause (int64_t id,
const vector<int> &clause) {
if (binary)
file->put ('a');
else
file->put ("a ");
if (binary)
put_binary_id (id);
else
file->put (id), file->put (" ");
for (const auto &external_lit : clause)
if (binary)
put_binary_lit (external_lit);
else
file->put (external_lit), file->put (' ');
if (binary)
put_binary_zero ();
else
file->put ("0\n");
}
void FratTracer::frat_add_derived_clause (int64_t id,
const vector<int> &clause,
const vector<int64_t> &chain) {
if (binary)
file->put ('a');
else
file->put ("a ");
if (binary)
put_binary_id (id);
else
file->put (id), file->put (" ");
for (const auto &external_lit : clause)
if (binary)
put_binary_lit (external_lit);
else
file->put (external_lit), file->put (' ');
if (binary)
put_binary_zero (), file->put ('l');
else
file->put ("0 l ");
for (const auto &c : chain)
if (binary)
put_binary_id (c, true); // LRAT can have negative ids
else
file->put (c), file->put (' '); // in proof chain, so they get
if (binary)
put_binary_zero (); // since cadical has no rat-steps
else
file->put ("0\n"); // this is just 2c here
}
void FratTracer::frat_delete_clause (int64_t id,
const vector<int> &clause) {
if (binary)
file->put ('d');
else
file->put ("d ");
if (binary)
put_binary_id (id);
else
file->put (id), file->put (" ");
for (const auto &external_lit : clause)
if (binary)
put_binary_lit (external_lit);
else
file->put (external_lit), file->put (' ');
if (binary)
put_binary_zero ();
else
file->put ("0\n");
}
void FratTracer::frat_finalize_clause (int64_t id,
const vector<int> &clause) {
if (binary)
file->put ('f');
else
file->put ("f ");
if (binary)
put_binary_id (id);
else
file->put (id), file->put (" ");
for (const auto &external_lit : clause)
if (binary)
put_binary_lit (external_lit);
else
file->put (external_lit), file->put (' ');
if (binary)
put_binary_zero ();
else
file->put ("0\n");
}
/*------------------------------------------------------------------------*/
void FratTracer::add_original_clause (int64_t id, bool,
const vector<int> &clause, bool) {
if (file->closed ())
return;
LOG ("FRAT TRACER tracing addition of original clause");
frat_add_original_clause (id, clause);
}
void FratTracer::add_derived_clause (int64_t id, bool,
const vector<int> &clause,
const vector<int64_t> &chain) {
if (file->closed ())
return;
LOG ("FRAT TRACER tracing addition of derived clause");
if (with_antecedents)
frat_add_derived_clause (id, clause, chain);
else
frat_add_derived_clause (id, clause);
#ifndef CADICAL_QUIET
added++;
#endif
}
void FratTracer::delete_clause (int64_t id, bool,
const vector<int> &clause) {
if (file->closed ())
return;
LOG ("FRAT TRACER tracing deletion of clause");
frat_delete_clause (id, clause);
#ifndef CADICAL_QUIET
deleted++;
#endif
}
void FratTracer::finalize_clause (int64_t id, const vector<int> &clause) {
if (file->closed ())
return;
LOG ("FRAT TRACER tracing finalization of clause");
frat_finalize_clause (id, clause);
}
/*------------------------------------------------------------------------*/
bool FratTracer::closed () { return file->closed (); }
#ifndef CADICAL_QUIET
void FratTracer::print_statistics () {
uint64_t bytes = file->bytes ();
uint64_t total = original + added + deleted + finalized;
MSG ("FRAT %" PRId64 " original clauses %.2f%%", original,
percent (original, total));
MSG ("FRAT %" PRId64 " added clauses %.2f%%", added,
percent (added, total));
MSG ("FRAT %" PRId64 " deleted clauses %.2f%%", deleted,
percent (deleted, total));
MSG ("FRAT %" PRId64 " finalized clauses %.2f%%", finalized,
percent (finalized, total));
MSG ("FRAT %" PRId64 " bytes (%.2f MB)", bytes,
bytes / (double) (1 << 20));
}
#endif
void FratTracer::close (bool print) {
CADICAL_assert (!closed ());
file->close ();
#ifndef CADICAL_QUIET
if (print) {
MSG ("FRAT proof file '%s' closed", file->name ());
print_statistics ();
}
#else
(void) print;
#endif
}
void FratTracer::flush (bool print) {
CADICAL_assert (!closed ());
file->flush ();
#ifndef CADICAL_QUIET
if (print) {
MSG ("FRAT proof file '%s' flushed", file->name ());
print_statistics ();
}
#else
(void) print;
#endif
}
} // namespace CaDiCaL
ABC_NAMESPACE_IMPL_END

View File

@ -1,772 +0,0 @@
#include "global.h"
#include "internal.hpp"
ABC_NAMESPACE_IMPL_START
namespace CaDiCaL {
/*------------------------------------------------------------------------*/
// As in our original SATeLite published at SAT'05 we are trying to find
// gates in order to restrict the number of resolutions that need to be
// tried. If there is such a gate, we only need to consider resolvents
// among gate and one non-gate clauses. Resolvents between definitions will
// be tautological anyhow and resolvents among non-gates can actually be
// shown to be redundant too.
/*------------------------------------------------------------------------*/
// The next function returns a non-zero if the clause 'c', which is assumed
// to contain the literal 'first', after removing falsified literals is a
// binary clause. Then the actual second literal is returned.
int Internal::second_literal_in_binary_clause (Eliminator &eliminator,
Clause *c, int first) {
CADICAL_assert (!c->garbage);
int second = 0;
for (const auto &lit : *c) {
if (lit == first)
continue;
const signed char tmp = val (lit);
if (tmp < 0)
continue;
if (tmp > 0) {
mark_garbage (c);
elim_update_removed_clause (eliminator, c);
return 0;
}
if (second) {
second = INT_MIN;
break;
}
second = lit;
}
if (!second)
return 0;
if (second == INT_MIN)
return 0;
CADICAL_assert (active (second));
#ifdef LOGGING
if (c->size == 2)
LOG (c, "found binary");
else
LOG (c, "found actual binary %d %d", first, second);
#endif
return second;
}
/*------------------------------------------------------------------------*/
// need a copy from above that does not care about garbage
int Internal::second_literal_in_binary_clause_lrat (Clause *c, int first) {
if (c->garbage)
return 0;
int second = 0;
for (const auto &lit : *c) {
if (lit == first)
continue;
const signed char tmp = val (lit);
if (tmp < 0)
continue;
if (tmp > 0)
return 0;
if (!tmp) {
if (second) {
second = INT_MIN;
break;
}
second = lit;
}
}
if (!second)
return 0;
if (second == INT_MIN)
return 0;
return second;
}
// I needed to find the second clause for hyper unary resolution to build
// LRAT this is not efficient but I could not find a better way then just
// finding the corresponding clause in all possible clauses
//
Clause *Internal::find_binary_clause (int first, int second) {
int best = first;
int other = second;
if (occs (first).size () > occs (second).size ()) {
best = second;
other = first;
}
for (auto c : occs (best))
if (second_literal_in_binary_clause_lrat (c, best) == other)
return c;
return 0;
}
/*------------------------------------------------------------------------*/
// Mark all other literals in binary clauses with 'first'. During this
// marking we might also detect hyper unary resolvents producing a unit.
// If such a unit is found we propagate it and return immediately.
void Internal::mark_binary_literals (Eliminator &eliminator, int first) {
if (unsat)
return;
if (val (first))
return;
if (!eliminator.gates.empty ())
return;
CADICAL_assert (!marked (first));
CADICAL_assert (eliminator.marked.empty ());
const Occs &os = occs (first);
for (const auto &c : os) {
if (c->garbage)
continue;
const int second =
second_literal_in_binary_clause (eliminator, c, first);
if (!second)
continue;
const int tmp = marked (second);
if (tmp < 0) {
// had a bug where units could occur multiple times here
// solved with flags
LOG ("found binary resolved unit %d", first);
if (lrat) {
Clause *d = find_binary_clause (first, -second);
CADICAL_assert (d);
for (auto &lit : *d) {
if (lit == first || lit == -second)
continue;
CADICAL_assert (val (lit) < 0);
Flags &f = flags (lit);
if (f.seen)
continue;
analyzed.push_back (lit);
f.seen = true;
int64_t id = unit_id (-lit);
lrat_chain.push_back (id);
// LOG ("gates added id %" PRId64, id);
}
for (auto &lit : *c) {
if (lit == first || lit == second)
continue;
CADICAL_assert (val (lit) < 0);
Flags &f = flags (lit);
if (f.seen)
continue;
analyzed.push_back (lit);
f.seen = true;
int64_t id = unit_id (-lit);
lrat_chain.push_back (id);
// LOG ("gates added id %" PRId64, id);
}
lrat_chain.push_back (c->id);
lrat_chain.push_back (d->id);
// LOG ("gates added id %" PRId64, c->id);
// LOG ("gates added id %" PRId64, d->id);
clear_analyzed_literals ();
}
assign_unit (first);
elim_propagate (eliminator, first);
return;
}
if (tmp > 0) {
LOG (c, "duplicated actual binary clause");
elim_update_removed_clause (eliminator, c);
mark_garbage (c);
continue;
}
eliminator.marked.push_back (second);
mark (second);
LOG ("marked second literal %d in binary clause %d %d", second, first,
second);
}
}
// Unmark all literals saved on the 'marked' stack.
void Internal::unmark_binary_literals (Eliminator &eliminator) {
LOG ("unmarking %zd literals", eliminator.marked.size ());
for (const auto &lit : eliminator.marked)
unmark (lit);
eliminator.marked.clear ();
}
/*------------------------------------------------------------------------*/
// Find equivalence for 'pivot'. Requires that all other literals in binary
// clauses with 'pivot' are marked (through 'mark_binary_literals');
void Internal::find_equivalence (Eliminator &eliminator, int pivot) {
if (!opts.elimequivs)
return;
CADICAL_assert (opts.elimsubst);
if (unsat)
return;
if (val (pivot))
return;
if (!eliminator.gates.empty ())
return;
mark_binary_literals (eliminator, pivot);
if (unsat || val (pivot))
goto DONE;
for (const auto &c : occs (-pivot)) {
if (c->garbage)
continue;
const int second =
second_literal_in_binary_clause (eliminator, c, -pivot);
if (!second)
continue;
const int tmp = marked (second);
if (tmp > 0) {
LOG ("found binary resolved unit %d", second);
// did not find a bug where units could occur multiple times here
// still solved potential issues with flags
if (lrat) {
Clause *d = find_binary_clause (pivot, second);
CADICAL_assert (d);
for (auto &lit : *d) {
if (lit == pivot || lit == second)
continue;
CADICAL_assert (val (lit) < 0);
Flags &f = flags (lit);
if (f.seen)
continue;
analyzed.push_back (lit);
f.seen = true;
int64_t id = unit_id (-lit);
lrat_chain.push_back (id);
// LOG ("gates added id %" PRId64, id);
}
for (auto &lit : *c) {
if (lit == -pivot || lit == second)
continue;
CADICAL_assert (val (lit) < 0);
Flags &f = flags (lit);
if (f.seen)
continue;
analyzed.push_back (lit);
f.seen = true;
int64_t id = unit_id (-lit);
lrat_chain.push_back (id);
// LOG ("gates added id %" PRId64, id);
}
lrat_chain.push_back (c->id);
lrat_chain.push_back (d->id);
clear_analyzed_literals ();
// LOG ("gates added id %" PRId64, c->id);
// LOG ("gates added id %" PRId64, d->id);
}
assign_unit (second);
elim_propagate (eliminator, second);
if (val (pivot))
break;
if (unsat)
break;
}
if (tmp >= 0)
continue;
LOG ("found equivalence %d = %d", pivot, -second);
stats.elimequivs++;
stats.elimgates++;
LOG (c, "first gate clause");
CADICAL_assert (!c->gate);
c->gate = true;
eliminator.gates.push_back (c);
Clause *d = 0;
const Occs &ps = occs (pivot);
for (const auto &e : ps) {
if (e->garbage)
continue;
const int other =
second_literal_in_binary_clause (eliminator, e, pivot);
if (other == -second) {
d = e;
break;
}
}
CADICAL_assert (d);
LOG (d, "second gate clause");
CADICAL_assert (!d->gate);
d->gate = true;
eliminator.gates.push_back (d);
eliminator.gatetype = EQUI;
break;
}
DONE:
unmark_binary_literals (eliminator);
}
/*------------------------------------------------------------------------*/
// Find and gates for 'pivot' with a long clause, in which the pivot occurs
// positively. Requires that all other literals in binary clauses with
// 'pivot' are marked (through 'mark_binary_literals');
void Internal::find_and_gate (Eliminator &eliminator, int pivot) {
if (!opts.elimands)
return;
CADICAL_assert (opts.elimsubst);
if (unsat)
return;
if (val (pivot))
return;
if (!eliminator.gates.empty ())
return;
mark_binary_literals (eliminator, pivot);
if (unsat || val (pivot))
goto DONE;
for (const auto &c : occs (-pivot)) {
if (c->garbage)
continue;
if (c->size < 3)
continue;
bool all_literals_marked = true;
unsigned arity = 0;
int satisfied = 0;
for (const auto &lit : *c) {
if (lit == -pivot)
continue;
CADICAL_assert (lit != pivot);
signed char tmp = val (lit);
if (tmp < 0)
continue;
if (tmp > 0) {
satisfied = lit;
break;
}
tmp = marked (lit);
if (tmp < 0) {
arity++;
continue;
}
all_literals_marked = false;
break;
}
if (!all_literals_marked)
continue;
if (satisfied) {
LOG (c, "satisfied by %d candidate base clause", satisfied);
mark_garbage (c);
continue;
}
#ifdef LOGGING
if (opts.log) {
Logger::print_log_prefix (this);
tout.magenta ();
printf ("found arity %u AND gate %d = ", arity, -pivot);
bool first = true;
for (const auto &lit : *c) {
if (lit == -pivot)
continue;
CADICAL_assert (lit != pivot);
if (!first)
fputs (" & ", stdout);
printf ("%d", -lit);
first = false;
}
fputc ('\n', stdout);
tout.normal ();
fflush (stdout);
}
#endif
stats.elimands++;
stats.elimgates++;
eliminator.gatetype = AND;
(void) arity;
CADICAL_assert (!c->gate);
c->gate = true;
eliminator.gates.push_back (c);
for (const auto &lit : *c) {
if (lit == -pivot)
continue;
CADICAL_assert (lit != pivot);
signed char tmp = val (lit);
if (tmp < 0)
continue;
CADICAL_assert (!tmp);
CADICAL_assert (marked (lit) < 0);
marks[vidx (lit)] *= 2;
}
unsigned count = 0;
for (const auto &d : occs (pivot)) {
if (d->garbage)
continue;
const int other =
second_literal_in_binary_clause (eliminator, d, pivot);
if (!other)
continue;
const int tmp = marked (other);
if (tmp != 2)
continue;
LOG (d, "AND gate binary side clause");
CADICAL_assert (!d->gate);
d->gate = true;
eliminator.gates.push_back (d);
count++;
}
CADICAL_assert (count >= arity);
(void) count;
break;
}
DONE:
unmark_binary_literals (eliminator);
}
/*------------------------------------------------------------------------*/
// Find and extract ternary clauses.
bool Internal::get_ternary_clause (Clause *d, int &a, int &b, int &c) {
if (d->garbage)
return false;
if (d->size < 3)
return false;
int found = 0;
a = b = c = 0;
for (const auto &lit : *d) {
if (val (lit))
continue;
if (++found == 1)
a = lit;
else if (found == 2)
b = lit;
else if (found == 3)
c = lit;
else
return false;
}
return found == 3;
}
// This function checks whether 'd' exists as ternary clause.
bool Internal::match_ternary_clause (Clause *d, int a, int b, int c) {
if (d->garbage)
return false;
int found = 0;
for (const auto &lit : *d) {
if (val (lit))
continue;
if (a != lit && b != lit && c != lit)
return false;
found++;
}
return found == 3;
}
Clause *Internal::find_ternary_clause (int a, int b, int c) {
if (occs (b).size () > occs (c).size ())
swap (b, c);
if (occs (a).size () > occs (b).size ())
swap (a, b);
for (auto d : occs (a))
if (match_ternary_clause (d, a, b, c))
return d;
return 0;
}
/*------------------------------------------------------------------------*/
// Find if-then-else gate.
void Internal::find_if_then_else (Eliminator &eliminator, int pivot) {
if (!opts.elimites)
return;
CADICAL_assert (opts.elimsubst);
if (unsat)
return;
if (val (pivot))
return;
if (!eliminator.gates.empty ())
return;
const Occs &os = occs (pivot);
const auto end = os.end ();
for (auto i = os.begin (); i != end; i++) {
Clause *di = *i;
int ai, bi, ci;
if (!get_ternary_clause (di, ai, bi, ci))
continue;
if (bi == pivot)
swap (ai, bi);
if (ci == pivot)
swap (ai, ci);
CADICAL_assert (ai == pivot);
for (auto j = i + 1; j != end; j++) {
Clause *dj = *j;
int aj, bj, cj;
if (!get_ternary_clause (dj, aj, bj, cj))
continue;
if (bj == pivot)
swap (aj, bj);
if (cj == pivot)
swap (aj, cj);
CADICAL_assert (aj == pivot);
if (abs (bi) == abs (cj))
swap (bj, cj);
if (abs (ci) == abs (cj))
continue;
if (bi != -bj)
continue;
Clause *d1 = find_ternary_clause (-pivot, bi, -ci);
if (!d1)
continue;
Clause *d2 = find_ternary_clause (-pivot, bj, -cj);
if (!d2)
continue;
LOG (di, "1st if-then-else");
LOG (dj, "2nd if-then-else");
LOG (d1, "3rd if-then-else");
LOG (d2, "4th if-then-else");
LOG ("found ITE gate %d == (%d ? %d : %d)", pivot, -bi, -ci, -cj);
CADICAL_assert (!di->gate);
CADICAL_assert (!dj->gate);
CADICAL_assert (!d1->gate);
CADICAL_assert (!d2->gate);
di->gate = true;
dj->gate = true;
d1->gate = true;
d2->gate = true;
eliminator.gates.push_back (di);
eliminator.gates.push_back (dj);
eliminator.gates.push_back (d1);
eliminator.gates.push_back (d2);
stats.elimgates++;
stats.elimites++;
eliminator.gatetype = ITE;
return;
}
}
}
/*------------------------------------------------------------------------*/
// Find and extract clause.
bool Internal::get_clause (Clause *c, vector<int> &l) {
if (c->garbage)
return false;
l.clear ();
for (const auto &lit : *c) {
if (val (lit) < 0)
continue;
if (val (lit) > 0) {
l.clear ();
return false;
}
l.push_back (lit);
}
return true;
}
// Check whether 'c' contains only the literals in 'l'.
bool Internal::is_clause (Clause *c, const vector<int> &lits) {
if (c->garbage)
return false;
int size = lits.size ();
if (c->size < size)
return false;
int found = 0;
for (const auto &lit : *c) {
if (val (lit) < 0)
continue;
if (val (lit) > 0)
return false;
const auto it = find (lits.begin (), lits.end (), lit);
if (it == lits.end ())
return false;
if (++found > size)
return false;
}
return found == size;
}
Clause *Internal::find_clause (const vector<int> &lits) {
int best = 0;
size_t len = 0;
for (const auto &lit : lits) {
size_t l = occs (lit).size ();
if (best && l >= len)
continue;
len = l, best = lit;
}
for (auto c : occs (best))
if (is_clause (c, lits))
return c;
return 0;
}
void Internal::find_xor_gate (Eliminator &eliminator, int pivot) {
if (!opts.elimxors)
return;
CADICAL_assert (opts.elimsubst);
if (unsat)
return;
if (val (pivot))
return;
if (!eliminator.gates.empty ())
return;
vector<int> lits;
for (auto d : occs (pivot)) {
if (!get_clause (d, lits))
continue;
const int size = lits.size (); // clause size
const int arity = size - 1; // arity of XOR
if (size < 3)
continue;
if (arity > opts.elimxorlim)
continue;
CADICAL_assert (eliminator.gates.empty ());
unsigned needed = (1u << arity) - 1; // additional clauses
unsigned signs = 0; // literals to negate
do {
const unsigned prev = signs;
while (parity (++signs))
;
for (int j = 0; j < size; j++) {
const unsigned bit = 1u << j;
int lit = lits[j];
if ((prev & bit) != (signs & bit))
lits[j] = lit = -lit;
}
Clause *e = find_clause (lits);
if (!e)
break;
eliminator.gates.push_back (e);
} while (--needed);
if (needed) {
eliminator.gates.clear ();
continue;
}
eliminator.gates.push_back (d);
CADICAL_assert (eliminator.gates.size () == (1u << arity));
#ifdef LOGGING
if (opts.log) {
Logger::print_log_prefix (this);
tout.magenta ();
printf ("found arity %u XOR gate %d = ", arity, -pivot);
bool first = true;
for (const auto &lit : *d) {
if (lit == pivot)
continue;
CADICAL_assert (lit != -pivot);
if (!first)
fputs (" ^ ", stdout);
printf ("%d", lit);
first = false;
}
fputc ('\n', stdout);
tout.normal ();
fflush (stdout);
}
#endif
stats.elimgates++;
stats.elimxors++;
const auto end = eliminator.gates.end ();
auto j = eliminator.gates.begin ();
for (auto i = j; i != end; i++) {
Clause *e = *i;
if (e->gate)
continue;
e->gate = true;
LOG (e, "contributing");
*j++ = e;
}
eliminator.gates.resize (j - eliminator.gates.begin ());
eliminator.gatetype = XOR;
break;
}
}
/*------------------------------------------------------------------------*/
// Find a gate for 'pivot'. If such a gate is found, the gate clauses are
// marked and pushed on the stack of gates. Further hyper unary resolution
// might detect units, which are propagated. This might assign the pivot or
// even produce the empty clause.
void Internal::find_gate_clauses (Eliminator &eliminator, int pivot) {
if (!opts.elimsubst)
return;
if (unsat)
return;
if (val (pivot))
return;
CADICAL_assert (eliminator.gates.empty ());
find_equivalence (eliminator, pivot);
find_and_gate (eliminator, pivot);
find_and_gate (eliminator, -pivot);
find_if_then_else (eliminator, pivot);
find_xor_gate (eliminator, pivot);
find_definition (eliminator, pivot);
}
void Internal::unmark_gate_clauses (Eliminator &eliminator) {
LOG ("unmarking %zd gate clauses", eliminator.gates.size ());
for (const auto &c : eliminator.gates) {
CADICAL_assert (c->gate);
c->gate = false;
}
eliminator.gates.clear ();
eliminator.definition_unit = 0;
}
/*------------------------------------------------------------------------*/
} // namespace CaDiCaL
ABC_NAMESPACE_IMPL_END

View File

@ -1,572 +0,0 @@
#include "global.h"
#include "internal.hpp"
ABC_NAMESPACE_IMPL_START
namespace CaDiCaL {
/*------------------------------------------------------------------------*/
IdrupTracer::IdrupTracer (Internal *i, File *f, bool b)
: internal (i), file (f), binary (b), num_clauses (0), size_clauses (0),
clauses (0), last_hash (0), last_id (0), last_clause (0)
#ifndef CADICAL_QUIET
,
added (0), deleted (0)
#endif
{
(void) internal;
// Initialize random number table for hash function.
//
Random random (42);
for (unsigned n = 0; n < num_nonces; n++) {
uint64_t nonce = random.next ();
if (!(nonce & 1))
nonce++;
CADICAL_assert (nonce), CADICAL_assert (nonce & 1);
nonces[n] = nonce;
}
#ifndef CADICAL_NDEBUG
binary = b;
#else
(void) b;
#endif
piping = file->piping ();
}
void IdrupTracer::connect_internal (Internal *i) {
internal = i;
file->connect_internal (internal);
LOG ("IDRUP TRACER connected to internal");
}
IdrupTracer::~IdrupTracer () {
LOG ("IDRUP TRACER delete");
delete file;
for (size_t i = 0; i < size_clauses; i++)
for (IdrupClause *c = clauses[i], *next; c; c = next)
next = c->next, delete_clause (c);
delete[] clauses;
}
/*------------------------------------------------------------------------*/
void IdrupTracer::enlarge_clauses () {
CADICAL_assert (num_clauses == size_clauses);
const uint64_t new_size_clauses = size_clauses ? 2 * size_clauses : 1;
LOG ("IDRUP Tracer enlarging clauses of tracer from %" PRIu64
" to %" PRIu64,
(uint64_t) size_clauses, (uint64_t) new_size_clauses);
IdrupClause **new_clauses;
new_clauses = new IdrupClause *[new_size_clauses];
clear_n (new_clauses, new_size_clauses);
for (uint64_t i = 0; i < size_clauses; i++) {
for (IdrupClause *c = clauses[i], *next; c; c = next) {
next = c->next;
const uint64_t h = reduce_hash (c->hash, new_size_clauses);
c->next = new_clauses[h];
new_clauses[h] = c;
}
}
delete[] clauses;
clauses = new_clauses;
size_clauses = new_size_clauses;
}
IdrupClause *IdrupTracer::new_clause () {
const size_t size = imported_clause.size ();
CADICAL_assert (size <= UINT_MAX);
const int off = size ? -1 : 0;
const size_t bytes = sizeof (IdrupClause) + (size - off) * sizeof (int);
IdrupClause *res = (IdrupClause *) new char[bytes];
res->next = 0;
res->hash = last_hash;
res->id = last_id;
res->size = size;
int *literals = res->literals, *p = literals;
for (const auto &lit : imported_clause) {
*p++ = lit;
}
last_clause = res;
num_clauses++;
return res;
}
void IdrupTracer::delete_clause (IdrupClause *c) {
CADICAL_assert (c);
num_clauses--;
delete[] (char *) c;
}
uint64_t IdrupTracer::reduce_hash (uint64_t hash, uint64_t size) {
CADICAL_assert (size > 0);
unsigned shift = 32;
uint64_t res = hash;
while ((((uint64_t) 1) << shift) > size) {
res ^= res >> shift;
shift >>= 1;
}
res &= size - 1;
CADICAL_assert (res < size);
return res;
}
uint64_t IdrupTracer::compute_hash (const int64_t id) {
CADICAL_assert (id > 0);
unsigned j = id % num_nonces;
uint64_t tmp = nonces[j] * (uint64_t) id;
return last_hash = tmp;
}
bool IdrupTracer::find_and_delete (const int64_t id) {
if (!num_clauses)
return false;
IdrupClause **res = 0, *c;
const uint64_t hash = compute_hash (id);
const uint64_t h = reduce_hash (hash, size_clauses);
for (res = clauses + h; (c = *res); res = &c->next) {
if (c->hash == hash && c->id == id) {
break;
}
if (!c->next)
return false;
}
if (!c)
return false;
CADICAL_assert (c && res);
*res = c->next;
int *begin = c->literals;
for (size_t i = 0; i < c->size; i++) {
imported_clause.push_back (begin[i]);
}
delete_clause (c);
return true;
}
void IdrupTracer::insert () {
if (num_clauses == size_clauses)
enlarge_clauses ();
const uint64_t h = reduce_hash (compute_hash (last_id), size_clauses);
IdrupClause *c = new_clause ();
c->next = clauses[h];
clauses[h] = c;
}
/*------------------------------------------------------------------------*/
inline void IdrupTracer::flush_if_piping () {
if (piping)
file->flush ();
}
inline void IdrupTracer::put_binary_zero () {
CADICAL_assert (binary);
CADICAL_assert (file);
file->put ((unsigned char) 0);
}
inline void IdrupTracer::put_binary_lit (int lit) {
CADICAL_assert (binary);
CADICAL_assert (file);
CADICAL_assert (lit != INT_MIN);
unsigned x = 2 * abs (lit) + (lit < 0);
unsigned char ch;
while (x & ~0x7f) {
ch = (x & 0x7f) | 0x80;
file->put (ch);
x >>= 7;
}
ch = x;
file->put (ch);
}
inline void IdrupTracer::put_binary_id (int64_t id, bool can_be_negative) {
CADICAL_assert (binary);
CADICAL_assert (file);
uint64_t x = abs (id);
if (can_be_negative) {
x = 2 * x + (id < 0);
}
unsigned char ch;
while (x & ~0x7f) {
ch = (x & 0x7f) | 0x80;
file->put (ch);
x >>= 7;
}
ch = x;
file->put (ch);
}
/*------------------------------------------------------------------------*/
void IdrupTracer::idrup_add_restored_clause (const vector<int> &clause) {
if (binary)
file->put ('r');
else
file->put ("r ");
for (const auto &external_lit : clause)
if (binary)
put_binary_lit (external_lit);
else
file->put (external_lit), file->put (' ');
if (binary)
put_binary_zero ();
else
file->put ("0\n");
// flush_if_piping ();
}
void IdrupTracer::idrup_add_derived_clause (const vector<int> &clause) {
if (binary)
file->put ('l');
else
file->put ("l ");
for (const auto &external_lit : clause)
if (binary)
put_binary_lit (external_lit);
else
file->put (external_lit), file->put (' ');
if (binary)
put_binary_zero ();
else
file->put ("0\n");
// flush_if_piping ();
}
void IdrupTracer::idrup_add_original_clause (const vector<int> &clause) {
if (binary)
file->put ('i');
else
file->put ("i ");
for (const auto &external_lit : clause)
if (binary)
put_binary_lit (external_lit);
else
file->put (external_lit), file->put (' ');
if (binary)
put_binary_zero ();
else
file->put ("0\n");
// flush_if_piping ();
}
void IdrupTracer::idrup_delete_clause (int64_t id,
const vector<int> &clause) {
if (find_and_delete (id)) {
CADICAL_assert (imported_clause.empty ());
if (binary)
file->put ('w');
else
file->put ("w ");
#ifndef CADICAL_QUIET
weakened++;
#endif
} else {
if (binary)
file->put ('d');
else
file->put ("d ");
#ifndef CADICAL_QUIET
deleted++;
#endif
}
for (const auto &external_lit : clause)
if (binary)
put_binary_lit (external_lit);
else
file->put (external_lit), file->put (' ');
if (binary)
put_binary_zero ();
else
file->put ("0\n");
// flush_if_piping ();
}
void IdrupTracer::idrup_conclude_and_delete (
const vector<int64_t> &conclusion) {
uint64_t size = conclusion.size ();
if (size > 1) {
if (binary) {
file->put ('U');
put_binary_id (size);
} else {
file->put ("U ");
file->put (size), file->put ("\n");
}
}
for (auto &id : conclusion) {
if (binary)
file->put ('u');
else
file->put ("u ");
(void) find_and_delete (id);
for (const auto &external_lit : imported_clause) {
// flip sign...
const auto not_elit = -external_lit;
if (binary)
put_binary_lit (not_elit);
else
file->put (not_elit), file->put (' ');
}
if (binary)
put_binary_zero ();
else
file->put ("0\n");
imported_clause.clear ();
}
flush_if_piping ();
}
void IdrupTracer::idrup_report_status (int status) {
if (binary)
file->put ('s');
else
file->put ("s ");
if (status == SATISFIABLE)
file->put ("SATISFIABLE");
else if (status == UNSATISFIABLE)
file->put ("UNSATISFIABLE");
else
file->put ("UNKNOWN");
if (!binary)
file->put ("\n");
flush_if_piping ();
}
void IdrupTracer::idrup_conclude_sat (const vector<int> &model) {
if (binary)
file->put ('m');
else
file->put ("m ");
for (auto &lit : model) {
if (binary)
put_binary_lit (lit);
else
file->put (lit), file->put (' ');
}
if (binary)
put_binary_zero ();
else
file->put ("0\n");
flush_if_piping ();
}
void IdrupTracer::idrup_conclude_unknown (const vector<int> &trail) {
if (binary)
file->put ('e');
else
file->put ("e ");
for (auto &lit : trail) {
if (binary)
put_binary_lit (lit);
else
file->put (lit), file->put (' ');
}
if (binary)
put_binary_zero ();
else
file->put ("0\n");
flush_if_piping ();
}
void IdrupTracer::idrup_solve_query () {
if (binary)
file->put ('q');
else
file->put ("q ");
for (auto &lit : assumptions) {
if (binary)
put_binary_lit (lit);
else
file->put (lit), file->put (' ');
}
if (binary)
put_binary_zero ();
else
file->put ("0\n");
flush_if_piping ();
}
/*------------------------------------------------------------------------*/
void IdrupTracer::add_derived_clause (int64_t, bool,
const vector<int> &clause,
const vector<int64_t> &) {
if (file->closed ())
return;
CADICAL_assert (imported_clause.empty ());
LOG (clause, "IDRUP TRACER tracing addition of derived clause");
idrup_add_derived_clause (clause);
#ifndef CADICAL_QUIET
added++;
#endif
}
void IdrupTracer::add_assumption_clause (int64_t id,
const vector<int> &clause,
const vector<int64_t> &) {
if (file->closed ())
return;
CADICAL_assert (imported_clause.empty ());
LOG (clause, "IDRUP TRACER tracing addition of assumption clause");
for (auto &lit : clause)
imported_clause.push_back (lit);
last_id = id;
insert ();
imported_clause.clear ();
}
void IdrupTracer::delete_clause (int64_t id, bool,
const vector<int> &clause) {
if (file->closed ())
return;
CADICAL_assert (imported_clause.empty ());
LOG ("IDRUP TRACER tracing deletion of clause[%" PRId64 "]", id);
idrup_delete_clause (id, clause);
}
void IdrupTracer::weaken_minus (int64_t id, const vector<int> &) {
if (file->closed ())
return;
CADICAL_assert (imported_clause.empty ());
LOG ("IDRUP TRACER tracing weaken minus of clause[%" PRId64 "]", id);
last_id = id;
insert ();
#ifndef CADICAL_QUIET
weakened++;
#endif
}
void IdrupTracer::conclude_unsat (ConclusionType,
const vector<int64_t> &conclusion) {
if (file->closed ())
return;
CADICAL_assert (imported_clause.empty ());
LOG (conclusion, "IDRUP TRACER tracing conclusion of clause(s)");
idrup_conclude_and_delete (conclusion);
}
void IdrupTracer::add_original_clause (int64_t id, bool,
const vector<int> &clause,
bool restored) {
if (file->closed ())
return;
if (!restored) {
LOG (clause, "IDRUP TRACER tracing addition of original clause");
#ifndef CADICAL_QUIET
original++;
#endif
return idrup_add_original_clause (clause);
}
CADICAL_assert (restored);
if (find_and_delete (id)) {
LOG (clause,
"IDRUP TRACER the clause was not yet weakened, so no restore");
return;
}
LOG (clause, "IDRUP TRACER tracing addition of restored clause");
idrup_add_restored_clause (clause);
#ifndef CADICAL_QUIET
restore++;
#endif
}
void IdrupTracer::report_status (int status, int64_t) {
if (file->closed ())
return;
LOG ("IDRUP TRACER tracing report of status %d", status);
idrup_report_status (status);
}
void IdrupTracer::conclude_sat (const vector<int> &model) {
if (file->closed ())
return;
LOG (model, "IDRUP TRACER tracing conclusion of model");
idrup_conclude_sat (model);
}
void IdrupTracer::conclude_unknown (const vector<int> &trail) {
if (file->closed ())
return;
LOG (trail, "IDRUP TRACER tracing conclusion of unknown state");
idrup_conclude_unknown (trail);
}
void IdrupTracer::solve_query () {
if (file->closed ())
return;
LOG (assumptions, "IDRUP TRACER tracing solve query with assumptions");
idrup_solve_query ();
#ifndef CADICAL_QUIET
solved++;
#endif
}
void IdrupTracer::add_assumption (int lit) {
LOG ("IDRUP TRACER tracing addition of assumption %d", lit);
assumptions.push_back (lit);
}
void IdrupTracer::reset_assumptions () {
LOG (assumptions, "IDRUP TRACER tracing reset of assumptions");
assumptions.clear ();
}
/*------------------------------------------------------------------------*/
bool IdrupTracer::closed () { return file->closed (); }
#ifndef CADICAL_QUIET
void IdrupTracer::print_statistics () {
// TODO complete this.
uint64_t bytes = file->bytes ();
uint64_t total = added + deleted + weakened + restore + original;
MSG ("LIDRUP %" PRId64 " original clauses %.2f%%", original,
percent (original, total));
MSG ("LIDRUP %" PRId64 " learned clauses %.2f%%", added,
percent (added, total));
MSG ("LIDRUP %" PRId64 " deleted clauses %.2f%%", deleted,
percent (deleted, total));
MSG ("LIDRUP %" PRId64 " weakened clauses %.2f%%", weakened,
percent (weakened, total));
MSG ("LIDRUP %" PRId64 " restored clauses %.2f%%", restore,
percent (restore, total));
MSG ("LIDRUP %" PRId64 " queries %.2f", solved, relative (solved, total));
MSG ("IDRUP %" PRId64 " bytes (%.2f MB)", bytes,
bytes / (double) (1 << 20));
}
#endif
void IdrupTracer::close (bool print) {
CADICAL_assert (!closed ());
file->close ();
#ifndef CADICAL_QUIET
if (print) {
MSG ("IDRUP proof file '%s' closed", file->name ());
print_statistics ();
}
#else
(void) print;
#endif
}
void IdrupTracer::flush (bool print) {
CADICAL_assert (!closed ());
file->flush ();
#ifndef CADICAL_QUIET
if (print) {
MSG ("IDRUP proof file '%s' flushed", file->name ());
print_statistics ();
}
#else
(void) print;
#endif
}
} // namespace CaDiCaL
ABC_NAMESPACE_IMPL_END

View File

@ -1,371 +0,0 @@
#include "global.h"
#include "internal.hpp"
ABC_NAMESPACE_IMPL_START
namespace CaDiCaL {
/*------------------------------------------------------------------------*/
// This provides an implementation of variable instantiation, a technique
// for removing literals with few occurrence (see also 'instantiate.hpp').
/*------------------------------------------------------------------------*/
// Triggered at the end of a variable elimination round ('elim_round').
void Internal::collect_instantiation_candidates (
Instantiator &instantiator) {
CADICAL_assert (occurring ());
for (auto idx : vars) {
if (frozen (idx))
continue;
if (!active (idx))
continue;
if (flags (idx).elim)
continue; // BVE attempt pending
for (int sign = -1; sign <= 1; sign += 2) {
const int lit = sign * idx;
if (noccs (lit) > opts.instantiateocclim)
continue;
Occs &os = occs (lit);
for (const auto &c : os) {
if (c->garbage)
continue;
if (opts.instantiateonce && c->instantiated)
continue;
if (c->size < opts.instantiateclslim)
continue;
bool satisfied = false;
int unassigned = 0;
for (const auto &other : *c) {
const signed char tmp = val (other);
if (tmp > 0)
satisfied = true;
if (!tmp)
unassigned++;
}
if (satisfied)
continue;
if (unassigned < 3)
continue; // avoid learning units
size_t negoccs = occs (-lit).size ();
LOG (c,
"instantiation candidate literal %d "
"with %zu negative occurrences in",
lit, negoccs);
instantiator.candidate (lit, c, c->size, negoccs);
}
}
}
}
/*------------------------------------------------------------------------*/
// Specialized propagation and assignment routines for instantiation.
inline void Internal::inst_assign (int lit) {
LOG ("instantiate assign %d", lit);
CADICAL_assert (!val (lit));
CADICAL_assert ((int) num_assigned < max_var);
num_assigned++;
set_val (lit, 1);
trail.push_back (lit);
}
// Conflict analysis is only needed to do valid resolution proofs.
// We remember propagated clauses in order of assignment (in inst_chain)
// which allows us to do a variant of conflict analysis if the instantiation
// attempt succeeds.
//
bool Internal::inst_propagate () { // Adapted from 'propagate'.
START (propagate);
int64_t before = propagated;
bool ok = true;
while (ok && propagated != trail.size ()) {
const int lit = -trail[propagated++];
LOG ("instantiate propagating %d", -lit);
Watches &ws = watches (lit);
const const_watch_iterator eow = ws.end ();
const_watch_iterator i = ws.begin ();
watch_iterator j = ws.begin ();
while (i != eow) {
const Watch w = *j++ = *i++;
const signed char b = val (w.blit);
if (b > 0)
continue;
if (w.binary ()) {
if (b < 0) {
ok = false;
LOG (w.clause, "conflict");
if (lrat) {
inst_chain.push_back (w.clause);
}
break;
} else {
if (lrat) {
inst_chain.push_back (w.clause);
}
inst_assign (w.blit);
}
} else {
literal_iterator lits = w.clause->begin ();
const int other = lits[0] ^ lits[1] ^ lit;
lits[0] = other, 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[1] = r;
*k = lit;
watch_literal (r, lit, w.clause);
j--;
} else if (!u) {
CADICAL_assert (v < 0);
if (lrat) {
inst_chain.push_back (w.clause);
}
inst_assign (other);
} else {
CADICAL_assert (u < 0);
CADICAL_assert (v < 0);
if (lrat) {
inst_chain.push_back (w.clause);
}
LOG (w.clause, "conflict");
ok = false;
break;
}
}
}
}
if (j != i) {
while (i != eow)
*j++ = *i++;
ws.resize (j - ws.begin ());
}
}
int64_t delta = propagated - before;
stats.propagations.instantiate += delta;
STOP (propagate);
return ok;
}
/*------------------------------------------------------------------------*/
// This is the instantiation attempt.
bool Internal::instantiate_candidate (int lit, Clause *c) {
stats.instried++;
if (c->garbage)
return false;
CADICAL_assert (!level);
bool found = false, satisfied = false, inactive = false;
int unassigned = 0;
for (const auto &other : *c) {
if (other == lit)
found = true;
const signed char tmp = val (other);
if (tmp > 0) {
satisfied = true;
break;
}
if (!tmp && !active (other)) {
inactive = true;
break;
}
if (!tmp)
unassigned++;
}
if (!found)
return false;
if (inactive)
return false;
if (satisfied)
return false;
if (unassigned < 3)
return false;
size_t before = trail.size ();
CADICAL_assert (propagated == before);
CADICAL_assert (active (lit));
CADICAL_assert (inst_chain.empty ());
LOG (c, "trying to instantiate %d in", lit);
CADICAL_assert (!c->garbage);
c->instantiated = true;
CADICAL_assert (lrat_chain.empty ());
level++;
inst_assign (lit); // Assume 'lit' to true.
for (const auto &other : *c) {
if (other == lit)
continue;
const signed char tmp = val (other);
if (tmp) {
CADICAL_assert (tmp < 0);
continue;
}
inst_assign (-other); // Assume other to false.
}
bool ok = inst_propagate (); // Propagate.
CADICAL_assert (lrat_chain.empty ()); // chain will be built here
if (ok) {
inst_chain.clear ();
} else if (lrat) { // analyze conflict for lrat
CADICAL_assert (inst_chain.size ());
Clause *reason = inst_chain.back ();
inst_chain.pop_back ();
lrat_chain.push_back (reason->id);
for (const auto &other : *reason) {
Flags &f = flags (other);
CADICAL_assert (!f.seen);
f.seen = true;
analyzed.push_back (other);
}
}
while (trail.size () > before) { // Backtrack.
const int other = trail.back ();
LOG ("instantiate unassign %d", other);
trail.pop_back ();
CADICAL_assert (val (other) > 0);
num_assigned--;
set_val (other, 0);
// this is a variant of conflict analysis which is only needed for lrat
if (!ok && inst_chain.size () && lrat) {
Flags &f = flags (other);
if (f.seen) {
Clause *reason = inst_chain.back ();
lrat_chain.push_back (reason->id);
for (const auto &other : *reason) {
Flags &f = flags (other);
if (f.seen)
continue;
f.seen = true;
analyzed.push_back (other);
}
f.seen = false;
}
inst_chain.pop_back ();
}
}
CADICAL_assert (inst_chain.empty ());
// post processing step for lrat
if (!ok && lrat) {
if (flags (lit).seen)
lrat_chain.push_back (c->id);
for (const auto &other : *c) {
Flags &f = flags (other);
f.seen = false;
}
for (int other : analyzed) {
Flags &f = flags (other);
if (!f.seen) {
f.seen = true;
continue;
}
int64_t id = unit_id (-other);
lrat_chain.push_back (id);
}
clear_analyzed_literals ();
reverse (lrat_chain.begin (), lrat_chain.end ());
}
CADICAL_assert (analyzed.empty ());
propagated = before;
CADICAL_assert (level == 1);
level = 0;
if (ok) {
CADICAL_assert (lrat_chain.empty ());
LOG ("instantiation failed");
return false;
}
unwatch_clause (c);
LOG (lrat_chain, "instantiate proof chain");
strengthen_clause (c, lit);
watch_clause (c);
lrat_chain.clear ();
CADICAL_assert (c->size > 1);
LOG ("instantiation succeeded");
stats.instantiated++;
return true;
}
/*------------------------------------------------------------------------*/
// Try to instantiate all candidates collected before through the
// 'collect_instantiation_candidates' routine.
void Internal::instantiate (Instantiator &instantiator) {
CADICAL_assert (opts.instantiate);
START (instantiate);
stats.instrounds++;
#ifndef CADICAL_QUIET
const int64_t candidates = instantiator.candidates.size ();
int64_t tried = 0;
#endif
int64_t instantiated = 0;
init_watches ();
connect_watches ();
if (propagated < trail.size ()) {
if (!propagate ()) {
LOG ("propagation after connecting watches failed");
learn_empty_clause ();
CADICAL_assert (unsat);
}
}
PHASE ("instantiate", stats.instrounds,
"attempting to instantiate %" PRId64
" candidate literal clause pairs",
candidates);
while (!unsat && !terminated_asynchronously () &&
!instantiator.candidates.empty ()) {
Instantiator::Candidate cand = instantiator.candidates.back ();
instantiator.candidates.pop_back ();
#ifndef CADICAL_QUIET
tried++;
#endif
if (!active (cand.lit))
continue;
LOG (cand.clause,
"trying to instantiate %d with "
"%zd negative occurrences in",
cand.lit, cand.negoccs);
if (!instantiate_candidate (cand.lit, cand.clause))
continue;
instantiated++;
VERBOSE (2,
"instantiation %" PRId64 " (%.1f%%) succeeded "
"(%.1f%%) with %zd negative occurrences in size %d clause",
tried, percent (tried, candidates),
percent (instantiated, tried), cand.negoccs, cand.size);
}
PHASE ("instantiate", stats.instrounds,
"instantiated %" PRId64 " candidate successfully "
"out of %" PRId64 " tried %.1f%%",
instantiated, tried, percent (instantiated, tried));
report ('I', !instantiated);
reset_watches ();
STOP (instantiate);
}
} // namespace CaDiCaL
ABC_NAMESPACE_IMPL_END

File diff suppressed because it is too large Load Diff

View File

@ -1,46 +0,0 @@
#include "global.h"
#include "ipasir.h"
#include "ccadical.h"
ABC_NAMESPACE_IMPL_START
const char *ipasir_signature () { return ccadical_signature (); }
void *ipasir_init () { return ccadical_init (); }
void ipasir_release (void *solver) {
ccadical_release ((CCaDiCaL *) solver);
}
void ipasir_add (void *solver, int lit) {
ccadical_add ((CCaDiCaL *) solver, lit);
}
void ipasir_assume (void *solver, int lit) {
ccadical_assume ((CCaDiCaL *) solver, lit);
}
int ipasir_solve (void *solver) {
return ccadical_solve ((CCaDiCaL *) solver);
}
int ipasir_val (void *solver, int lit) {
return ccadical_val ((CCaDiCaL *) solver, lit);
}
int ipasir_failed (void *solver, int lit) {
return ccadical_failed ((CCaDiCaL *) solver, lit);
}
void ipasir_set_terminate (void *solver, void *state,
int (*terminate) (void *state)) {
ccadical_set_terminate ((CCaDiCaL *) solver, state, terminate);
}
void ipasir_set_learn (void *solver, void *state, int max_length,
void (*learn) (void *state, int *clause)) {
ccadical_set_learn ((CCaDiCaL *) solver, state, max_length, learn);
}
ABC_NAMESPACE_IMPL_END

File diff suppressed because it is too large Load Diff

View File

@ -1,662 +0,0 @@
#include "global.h"
#include "internal.hpp"
ABC_NAMESPACE_IMPL_START
namespace CaDiCaL {
/*------------------------------------------------------------------------*/
LidrupTracer::LidrupTracer (Internal *i, File *f, bool b)
: internal (i), file (f), binary (b), num_clauses (0), size_clauses (0),
clauses (0), last_hash (0), last_id (0), last_clause (0)
#ifndef CADICAL_QUIET
,
added (0), deleted (0)
#endif
{
(void) internal;
// Initialize random number table for hash function.
//
Random random (42);
for (unsigned n = 0; n < num_nonces; n++) {
uint64_t nonce = random.next ();
if (!(nonce & 1))
nonce++;
CADICAL_assert (nonce), CADICAL_assert (nonce & 1);
nonces[n] = nonce;
}
#ifndef CADICAL_NDEBUG
binary = b;
#else
(void) b;
#endif
piping = file->piping ();
}
void LidrupTracer::connect_internal (Internal *i) {
internal = i;
file->connect_internal (internal);
LOG ("LIDRUP TRACER connected to internal");
}
LidrupTracer::~LidrupTracer () {
LOG ("LIDRUP TRACER delete");
delete file;
for (size_t i = 0; i < size_clauses; i++)
for (LidrupClause *c = clauses[i], *next; c; c = next)
next = c->next, delete_clause (c);
delete[] clauses;
}
/*------------------------------------------------------------------------*/
void LidrupTracer::enlarge_clauses () {
CADICAL_assert (num_clauses == size_clauses);
const uint64_t new_size_clauses = size_clauses ? 2 * size_clauses : 1;
LOG ("LIDRUP Tracer enlarging clauses of tracer from %" PRIu64
" to %" PRIu64,
(uint64_t) size_clauses, (uint64_t) new_size_clauses);
LidrupClause **new_clauses;
new_clauses = new LidrupClause *[new_size_clauses];
clear_n (new_clauses, new_size_clauses);
for (uint64_t i = 0; i < size_clauses; i++) {
for (LidrupClause *c = clauses[i], *next; c; c = next) {
next = c->next;
const uint64_t h = reduce_hash (c->hash, new_size_clauses);
c->next = new_clauses[h];
new_clauses[h] = c;
}
}
delete[] clauses;
clauses = new_clauses;
size_clauses = new_size_clauses;
}
LidrupClause *LidrupTracer::new_clause () {
LidrupClause *res = new LidrupClause;
res->next = 0;
res->hash = last_hash;
res->id = last_id;
for (const auto &id : imported_chain) {
res->chain.push_back (id);
}
for (const auto &lit : imported_clause) {
res->literals.push_back (lit);
}
last_clause = res;
num_clauses++;
return res;
}
void LidrupTracer::delete_clause (LidrupClause *c) {
CADICAL_assert (c);
num_clauses--;
delete c;
}
uint64_t LidrupTracer::reduce_hash (uint64_t hash, uint64_t size) {
CADICAL_assert (size > 0);
unsigned shift = 32;
uint64_t res = hash;
while ((((uint64_t) 1) << shift) > size) {
res ^= res >> shift;
shift >>= 1;
}
res &= size - 1;
CADICAL_assert (res < size);
return res;
}
uint64_t LidrupTracer::compute_hash (const int64_t id) {
CADICAL_assert (id > 0);
unsigned j = id % num_nonces;
uint64_t tmp = nonces[j] * (uint64_t) id;
return last_hash = tmp;
}
bool LidrupTracer::find_and_delete (const int64_t id) {
if (!num_clauses)
return false;
LidrupClause **res = 0, *c;
const uint64_t hash = compute_hash (id);
const uint64_t h = reduce_hash (hash, size_clauses);
for (res = clauses + h; (c = *res); res = &c->next) {
if (c->hash == hash && c->id == id) {
break;
}
if (!c->next)
return false;
}
if (!c)
return false;
CADICAL_assert (c && res);
*res = c->next;
for (auto &lit : c->literals) {
imported_clause.push_back (lit);
}
for (auto &cid : c->chain) {
imported_chain.push_back (cid);
}
delete_clause (c);
return true;
}
void LidrupTracer::insert () {
if (num_clauses == size_clauses)
enlarge_clauses ();
const uint64_t h = reduce_hash (compute_hash (last_id), size_clauses);
LidrupClause *c = new_clause ();
c->next = clauses[h];
clauses[h] = c;
}
/*------------------------------------------------------------------------*/
inline void LidrupTracer::flush_if_piping () {
if (piping)
file->flush ();
}
inline void LidrupTracer::put_binary_zero () {
CADICAL_assert (binary);
CADICAL_assert (file);
file->put ((unsigned char) 0);
}
inline void LidrupTracer::put_binary_lit (int lit) {
CADICAL_assert (binary);
CADICAL_assert (file);
CADICAL_assert (lit != INT_MIN);
unsigned x = 2 * abs (lit) + (lit < 0);
unsigned char ch;
while (x & ~0x7f) {
ch = (x & 0x7f) | 0x80;
file->put (ch);
x >>= 7;
}
ch = x;
file->put (ch);
}
inline void LidrupTracer::put_binary_id (int64_t id, bool can_be_negative) {
CADICAL_assert (binary);
CADICAL_assert (file);
uint64_t x = abs (id);
if (can_be_negative) {
x = 2 * x + (id < 0);
}
unsigned char ch;
while (x & ~0x7f) {
ch = (x & 0x7f) | 0x80;
file->put (ch);
x >>= 7;
}
ch = x;
file->put (ch);
}
/*------------------------------------------------------------------------*/
void LidrupTracer::lidrup_add_restored_clause (int64_t id) {
if (!batch_weaken.empty () || !batch_delete.empty ())
lidrup_batch_weaken_restore_and_delete ();
batch_restore.push_back (id);
}
void LidrupTracer::lidrup_add_derived_clause (
int64_t id, const vector<int> &clause, const vector<int64_t> &chain) {
lidrup_batch_weaken_restore_and_delete ();
if (binary) {
file->put ('l');
put_binary_id (id);
} else {
file->put ("l ");
file->put (id);
file->put (' ');
}
for (const auto &external_lit : clause)
if (binary)
put_binary_lit (external_lit);
else
file->put (external_lit), file->put (' ');
if (binary)
put_binary_zero ();
else
file->put ("0 ");
for (const auto &cid : chain)
if (binary)
put_binary_id (cid);
else
file->put (cid), file->put (' ');
if (binary)
put_binary_zero ();
else
file->put ("0\n");
}
void LidrupTracer::lidrup_add_original_clause (int64_t id,
const vector<int> &clause) {
lidrup_batch_weaken_restore_and_delete ();
if (binary) {
file->put ('i');
put_binary_id (id);
} else {
file->put ("i ");
file->put (id);
file->put (' ');
}
for (const auto &external_lit : clause)
if (binary)
put_binary_lit (external_lit);
else
file->put (external_lit), file->put (' ');
if (binary)
put_binary_zero ();
else
file->put ("0\n");
}
void LidrupTracer::lidrup_batch_weaken_restore_and_delete () {
CADICAL_assert (batch_weaken.empty () || batch_delete.empty ());
if (!batch_weaken.empty ()) {
if (binary) {
file->put ('w');
} else {
file->put ("w ");
}
for (const auto &id : batch_weaken) {
if (binary)
put_binary_id (id);
else
file->put (id), file->put (' ');
}
batch_weaken.clear ();
if (binary)
put_binary_zero ();
else
file->put ("0\n");
#ifndef CADICAL_QUIET
batched++;
#endif
}
if (!batch_delete.empty ()) {
if (binary) {
file->put ('d');
} else {
file->put ("d ");
}
for (const auto &id : batch_delete) {
if (binary)
put_binary_id (id);
else
file->put (id), file->put (' ');
}
batch_delete.clear ();
if (binary)
put_binary_zero ();
else
file->put ("0\n");
#ifndef CADICAL_QUIET
batched++;
#endif
}
if (!batch_restore.empty ()) {
if (binary) {
file->put ('r');
} else {
file->put ("r ");
}
for (const auto &id : batch_restore) {
if (binary)
put_binary_id (id);
else
file->put (id), file->put (' ');
}
batch_restore.clear ();
if (binary)
put_binary_zero ();
else
file->put ("0\n");
#ifndef CADICAL_QUIET
batched++;
#endif
}
}
void LidrupTracer::lidrup_conclude_and_delete (
const vector<int64_t> &conclusion) {
lidrup_batch_weaken_restore_and_delete ();
int64_t size = conclusion.size ();
if (size > 1) {
if (binary) {
file->put ('U');
put_binary_id (size);
} else {
file->put ("U ");
file->put (size), file->put ("\n");
}
}
for (auto &id : conclusion) {
if (binary)
file->put ('u');
else
file->put ("u ");
if (!find_and_delete (id)) {
CADICAL_assert (imported_clause.empty ());
CADICAL_assert (conclusion.size () == 1);
if (binary) {
put_binary_zero ();
put_binary_id (id);
put_binary_zero ();
} else {
file->put ("0 ");
file->put (id);
file->put (" 0\n");
}
} else {
for (const auto &external_lit : imported_clause) {
// flip sign...
const auto not_elit = -external_lit;
if (binary)
put_binary_lit (not_elit);
else
file->put (not_elit), file->put (' ');
}
if (binary)
put_binary_zero ();
else
file->put ("0 ");
for (const auto &cid : imported_chain) {
if (binary)
put_binary_id (cid);
else
file->put (cid), file->put (' ');
}
if (binary)
put_binary_zero ();
else
file->put ("0\n");
imported_clause.clear ();
imported_chain.clear ();
}
}
flush_if_piping ();
}
void LidrupTracer::lidrup_report_status (int status) {
lidrup_batch_weaken_restore_and_delete ();
if (binary)
file->put ('s');
else
file->put ("s ");
if (status == SATISFIABLE)
file->put ("SATISFIABLE");
else if (status == UNSATISFIABLE)
file->put ("UNSATISFIABLE");
else
file->put ("UNKNOWN");
if (!binary)
file->put ("\n");
flush_if_piping ();
}
void LidrupTracer::lidrup_conclude_sat (const vector<int> &model) {
lidrup_batch_weaken_restore_and_delete ();
if (binary)
file->put ('m');
else
file->put ("m ");
for (auto &lit : model) {
if (binary)
put_binary_lit (lit);
else
file->put (lit), file->put (' ');
}
if (binary)
put_binary_zero ();
else
file->put ("0\n");
flush_if_piping ();
}
void LidrupTracer::lidrup_conclude_unknown (const vector<int> &trail) {
lidrup_batch_weaken_restore_and_delete ();
if (binary)
file->put ('e');
else
file->put ("e ");
for (auto &lit : trail) {
if (binary)
put_binary_lit (lit);
else
file->put (lit), file->put (' ');
}
if (binary)
put_binary_zero ();
else
file->put ("0\n");
flush_if_piping ();
}
void LidrupTracer::lidrup_solve_query () {
lidrup_batch_weaken_restore_and_delete ();
if (binary)
file->put ('q');
else
file->put ("q ");
for (auto &lit : assumptions) {
if (binary)
put_binary_lit (lit);
else
file->put (lit), file->put (' ');
}
if (binary)
put_binary_zero ();
else
file->put ("0\n");
flush_if_piping ();
}
/*------------------------------------------------------------------------*/
void LidrupTracer::add_derived_clause (int64_t id, bool,
const vector<int> &clause,
const vector<int64_t> &chain) {
if (file->closed ())
return;
CADICAL_assert (imported_clause.empty ());
LOG (clause, "LIDRUP TRACER tracing addition of derived clause");
lidrup_add_derived_clause (id, clause, chain);
#ifndef CADICAL_QUIET
added++;
#endif
}
void LidrupTracer::add_assumption_clause (int64_t id,
const vector<int> &clause,
const vector<int64_t> &chain) {
if (file->closed ())
return;
CADICAL_assert (imported_clause.empty ());
LOG (clause,
"LIDRUP TRACER tracing addition of assumption clause[%" PRId64 "]",
id);
for (auto &lit : clause)
imported_clause.push_back (lit);
for (auto &cid : chain)
imported_chain.push_back (cid);
last_id = id;
insert ();
imported_clause.clear ();
imported_chain.clear ();
}
void LidrupTracer::delete_clause (int64_t id, bool, const vector<int> &) {
if (file->closed ())
return;
CADICAL_assert (imported_clause.empty ());
LOG ("LIDRUP TRACER tracing deletion of clause[%" PRId64 "]", id);
if (find_and_delete (id)) {
CADICAL_assert (imported_clause.empty ());
if (!batch_delete.empty () || !batch_restore.empty ())
lidrup_batch_weaken_restore_and_delete ();
batch_weaken.push_back (id);
#ifndef CADICAL_QUIET
weakened++;
#endif
} else {
if (!batch_weaken.empty () || !batch_restore.empty ())
lidrup_batch_weaken_restore_and_delete ();
batch_delete.push_back (id);
#ifndef CADICAL_QUIET
deleted++;
#endif
}
}
void LidrupTracer::weaken_minus (int64_t id, const vector<int> &) {
if (file->closed ())
return;
CADICAL_assert (imported_clause.empty ());
LOG ("LIDRUP TRACER tracing weaken minus of clause[%" PRId64 "]", id);
last_id = id;
insert ();
}
void LidrupTracer::conclude_unsat (ConclusionType,
const vector<int64_t> &conclusion) {
if (file->closed ())
return;
CADICAL_assert (imported_clause.empty ());
LOG (conclusion, "LIDRUP TRACER tracing conclusion of clause(s)");
lidrup_conclude_and_delete (conclusion);
}
void LidrupTracer::add_original_clause (int64_t id, bool,
const vector<int> &clause,
bool restored) {
if (file->closed ())
return;
if (!restored) {
LOG (clause, "LIDRUP TRACER tracing addition of original clause");
#ifndef CADICAL_QUIET
original++;
#endif
return lidrup_add_original_clause (id, clause);
}
CADICAL_assert (restored);
if (find_and_delete (id)) {
LOG (clause,
"LIDRUP TRACER the clause was not yet weakened, so no restore");
return;
}
LOG (clause, "LIDRUP TRACER tracing addition of restored clause");
lidrup_add_restored_clause (id);
#ifndef CADICAL_QUIET
restore++;
#endif
}
void LidrupTracer::report_status (int status, int64_t) {
if (file->closed ())
return;
LOG ("LIDRUP TRACER tracing report of status %d", status);
lidrup_report_status (status);
}
void LidrupTracer::conclude_sat (const vector<int> &model) {
if (file->closed ())
return;
LOG (model, "LIDRUP TRACER tracing conclusion of model");
lidrup_conclude_sat (model);
}
void LidrupTracer::conclude_unknown (const vector<int> &entrailed) {
if (file->closed ())
return;
LOG (entrailed, "LIDRUP TRACER tracing conclusion of UNK");
lidrup_conclude_unknown (entrailed);
}
void LidrupTracer::solve_query () {
if (file->closed ())
return;
LOG (assumptions, "LIDRUP TRACER tracing solve query with assumptions");
lidrup_solve_query ();
#ifndef CADICAL_QUIET
solved++;
#endif
}
void LidrupTracer::add_assumption (int lit) {
LOG ("LIDRUP TRACER tracing addition of assumption %d", lit);
assumptions.push_back (lit);
}
void LidrupTracer::reset_assumptions () {
LOG (assumptions, "LIDRUP TRACER tracing reset of assumptions");
assumptions.clear ();
}
/*------------------------------------------------------------------------*/
bool LidrupTracer::closed () { return file->closed (); }
#ifndef CADICAL_QUIET
void LidrupTracer::print_statistics () {
// TODO complete this.
uint64_t bytes = file->bytes ();
uint64_t total = added + deleted + weakened + restore + original;
MSG ("LIDRUP %" PRId64 " original clauses %.2f%%", original,
percent (original, total));
MSG ("LIDRUP %" PRId64 " learned clauses %.2f%%", added,
percent (added, total));
MSG ("LIDRUP %" PRId64 " deleted clauses %.2f%%", deleted,
percent (deleted, total));
MSG ("LIDRUP %" PRId64 " weakened clauses %.2f%%", weakened,
percent (weakened, total));
MSG ("LIDRUP %" PRId64 " restored clauses %.2f%%", restore,
percent (restore, total));
MSG ("LIDRUP %" PRId64 " batches of deletions, weaken and restores %.2f",
batched, relative (batched, deleted + restore + weakened));
MSG ("LIDRUP %" PRId64 " queries %.2f", solved, relative (solved, total));
MSG ("LIDRUP %" PRId64 " bytes (%.2f MB)", bytes,
bytes / (double) (1 << 20));
}
#endif
void LidrupTracer::close (bool print) {
CADICAL_assert (!closed ());
file->close ();
#ifndef CADICAL_QUIET
if (print) {
MSG ("LIDRUP proof file '%s' closed", file->name ());
print_statistics ();
}
#else
(void) print;
#endif
}
void LidrupTracer::flush (bool print) {
CADICAL_assert (!closed ());
lidrup_batch_weaken_restore_and_delete ();
file->flush ();
#ifndef CADICAL_QUIET
if (print) {
MSG ("LIDRUP proof file '%s' flushed", file->name ());
print_statistics ();
}
#else
(void) print;
#endif
}
} // namespace CaDiCaL
ABC_NAMESPACE_IMPL_END

View File

@ -1,135 +0,0 @@
#include "global.h"
#include "internal.hpp"
ABC_NAMESPACE_IMPL_START
namespace CaDiCaL {
Limit::Limit () { memset (this, 0, sizeof *this); }
/*------------------------------------------------------------------------*/
double Internal::scale (double v) const {
const double ratio = clause_variable_ratio ();
const double factor = (ratio <= 2) ? 1.0 : log (ratio) / log (2);
double res = factor * v;
if (res < 1)
res = 1;
return res;
}
/*------------------------------------------------------------------------*/
Last::Last () { memset (this, 0, sizeof *this); }
/*------------------------------------------------------------------------*/
Inc::Inc () {
memset (this, 0, sizeof *this);
decisions = conflicts = -1; // unlimited
}
void Internal::limit_terminate (int l) {
if (l <= 0 && !lim.terminate.forced) {
LOG ("keeping unbounded terminate limit");
} else if (l <= 0) {
LOG ("reset terminate limit to be unbounded");
lim.terminate.forced = 0;
} else {
lim.terminate.forced = l;
LOG ("new terminate limit of %d calls", l);
}
}
void Internal::limit_conflicts (int l) {
if (l < 0 && inc.conflicts < 0) {
LOG ("keeping unbounded conflict limit");
} else if (l < 0) {
LOG ("reset conflict limit to be unbounded");
inc.conflicts = -1;
} else {
inc.conflicts = l;
LOG ("new conflict limit of %d conflicts", l);
}
}
void Internal::limit_decisions (int l) {
if (l < 0 && inc.decisions < 0) {
LOG ("keeping unbounded decision limit");
} else if (l < 0) {
LOG ("reset decision limit to be unbounded");
inc.decisions = -1;
} else {
inc.decisions = l;
LOG ("new decision limit of %d decisions", l);
}
}
void Internal::limit_preprocessing (int l) {
if (l < 0) {
LOG ("ignoring invalid preprocessing limit %d", l);
} else if (!l) {
LOG ("reset preprocessing limit to no preprocessing");
inc.preprocessing = 0;
} else {
inc.preprocessing = l;
LOG ("new preprocessing limit of %d preprocessing rounds", l);
}
}
void Internal::limit_local_search (int l) {
if (l < 0) {
LOG ("ignoring invalid local search limit %d", l);
} else if (!l) {
LOG ("reset local search limit to no local search");
inc.localsearch = 0;
} else {
inc.localsearch = l;
LOG ("new local search limit of %d local search rounds", l);
}
}
bool Internal::is_valid_limit (const char *name) {
if (!strcmp (name, "terminate"))
return true;
if (!strcmp (name, "conflicts"))
return true;
if (!strcmp (name, "decisions"))
return true;
if (!strcmp (name, "preprocessing"))
return true;
if (!strcmp (name, "localsearch"))
return true;
return false;
}
bool Internal::limit (const char *name, int l) {
bool res = true;
if (!strcmp (name, "terminate"))
limit_terminate (l);
else if (!strcmp (name, "conflicts"))
limit_conflicts (l);
else if (!strcmp (name, "decisions"))
limit_decisions (l);
else if (!strcmp (name, "preprocessing"))
limit_preprocessing (l);
else if (!strcmp (name, "localsearch"))
limit_local_search (l);
else
res = false;
return res;
}
void Internal::reset_limits () {
LOG ("reset limits");
limit_terminate (0);
limit_conflicts (-1);
limit_decisions (-1);
limit_preprocessing (0);
limit_local_search (0);
}
} // namespace CaDiCaL
ABC_NAMESPACE_IMPL_END

View File

@ -1,221 +0,0 @@
#include "global.h"
#ifdef LOGGING
#include "internal.hpp"
ABC_NAMESPACE_IMPL_START
namespace CaDiCaL {
void Logger::print_log_prefix (Internal *internal) {
internal->print_prefix ();
tout.magenta ();
fputs ("LOG ", stdout);
tout.magenta (true);
printf ("%d ", internal->level);
tout.normal ();
}
void Logger::log_empty_line (Internal *internal) {
internal->print_prefix ();
tout.magenta ();
const int len = internal->prefix.size (), max = 78 - len;
for (int i = 0; i < max; i++)
fputc ('-', stdout);
fputc ('\n', stdout);
tout.normal ();
fflush (stdout);
}
void Logger::log (Internal *internal, const char *fmt, ...) {
print_log_prefix (internal);
tout.magenta ();
va_list ap;
va_start (ap, fmt);
vprintf (fmt, ap);
va_end (ap);
fputc ('\n', stdout);
tout.normal ();
fflush (stdout);
}
// It is hard to factor out the common part between the two clause loggers,
// since they are also used in slightly different contexts. Our attempt to
// do so were not more readable than the current version. See the header
// for an explanation of the difference between the following two functions.
void Logger::log (Internal *internal, const Clause *c, const char *fmt,
...) {
print_log_prefix (internal);
tout.magenta ();
va_list ap;
va_start (ap, fmt);
vprintf (fmt, ap);
va_end (ap);
if (c) {
if (c->redundant)
printf (" glue %d redundant", c->glue);
else
printf (" irredundant");
printf (" size %d clause[%" PRId64 "]", c->size, c->id);
if (c->moved)
printf (" ... (moved)");
else {
if (internal->opts.logsort) {
vector<int> s;
for (const auto &lit : *c)
s.push_back (lit);
sort (s.begin (), s.end (), clause_lit_less_than ());
for (const auto &lit : s)
printf (" %d", lit);
} else {
for (const auto &lit : *c) {
printf (" %s", loglit (internal, lit).c_str ());
}
}
}
} else if (internal->level)
printf (" decision");
else
printf (" unit");
fputc ('\n', stdout);
tout.normal ();
fflush (stdout);
}
void Logger::log (Internal *internal, const Gate *g, const char *fmt, ...) {
print_log_prefix (internal);
tout.magenta ();
va_list ap;
va_start (ap, 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-" : "",
g->garbage ? " garbage" : "", g->id, g->arity (),
loglit (internal, g->lhs).c_str (),
string_of_gate (g->tag).c_str ());
for (const auto &lit : g->rhs) {
printf (" %s", loglit (internal, lit).c_str ());
}
} else
printf (" null gate");
fputc ('\n', stdout);
tout.normal ();
fflush (stdout);
}
// Same as above, but for the global clause 'c' (which is not a reason).
void Logger::log (Internal *internal, const vector<int> &c, const char *fmt,
...) {
print_log_prefix (internal);
tout.magenta ();
va_list ap;
va_start (ap, fmt);
vprintf (fmt, ap);
va_end (ap);
if (internal->opts.logsort) {
vector<int> s;
for (const auto &lit : c)
s.push_back (lit);
sort (s.begin (), s.end (), clause_lit_less_than ());
for (const auto &lit : s)
printf (" %d", lit);
} else {
for (const auto &lit : c)
printf (" %d", lit);
}
fputc ('\n', stdout);
tout.normal ();
fflush (stdout);
}
// Now for 'restore_clause' to avoid copying (without logging).
void Logger::log (Internal *internal,
const vector<int>::const_iterator &begin,
const vector<int>::const_iterator &end, const char *fmt,
...) {
print_log_prefix (internal);
tout.magenta ();
va_list ap;
va_start (ap, fmt);
vprintf (fmt, ap);
va_end (ap);
if (internal->opts.logsort) {
vector<int> s;
for (auto p = begin; p != end; p++)
s.push_back (*p);
sort (s.begin (), s.end (), clause_lit_less_than ());
for (const auto &lit : s)
printf (" %d", lit);
} else {
for (auto p = begin; p != end; p++)
printf (" %d", *p);
}
fputc ('\n', stdout);
tout.normal ();
fflush (stdout);
}
// for LRAT proof chains
void Logger::log (Internal *internal, const vector<int64_t> &c,
const char *fmt, ...) {
print_log_prefix (internal);
tout.magenta ();
va_list ap;
va_start (ap, fmt);
vprintf (fmt, ap);
va_end (ap);
for (const auto &id : c)
printf (" %" PRId64, id);
fputc ('\n', stdout);
tout.normal ();
fflush (stdout);
}
// for LRAT proof clauses
void Logger::log (Internal *internal, const int *literals,
const unsigned size, const char *fmt, ...) {
print_log_prefix (internal);
tout.magenta ();
va_list ap;
va_start (ap, fmt);
vprintf (fmt, ap);
va_end (ap);
for (unsigned i = 0; i < size; i++) {
const int lit = literals[i];
printf (" %d", lit);
}
fputc ('\n', stdout);
tout.normal ();
fflush (stdout);
}
string Logger::loglit (Internal *internal, int lit) {
std::string v = std::to_string (lit);
if (lit && -internal->max_var <= lit && internal->max_var >= lit) {
const int va = internal->val (lit);
if (va) {
v = v + "@" + std::to_string (internal->var (lit).level);
if (!internal->var (lit).reason)
v = v + "+";
}
if (va > 0)
v += "=1";
else if (va < 0)
v += "=-1";
}
return v;
}
} // namespace CaDiCaL
ABC_NAMESPACE_IMPL_END
#endif

View File

@ -1,526 +0,0 @@
#include "global.h"
#include "internal.hpp"
ABC_NAMESPACE_IMPL_START
namespace CaDiCaL {
struct literal_occ {
int lit;
int count;
bool operator< (const literal_occ &locc) const {
return (count > locc.count) || (count == locc.count && lit < locc.lit);
}
literal_occ operator++ () {
++count;
return *this;
}
};
std::vector<int> Internal::lookahead_populate_locc () {
std::vector<literal_occ> loccs ((std::size_t) max_var + 1);
for (std::size_t lit = 0; lit < loccs.size (); ++lit) {
loccs[lit].lit = lit;
}
for (const auto &c : clauses)
if (!c->redundant)
for (const auto &lit : *c)
if (active (lit))
++loccs[std::abs (lit)];
std::sort (begin (loccs), end (loccs));
std::vector<int> locc_map;
locc_map.reserve (max_var);
for (const auto &locc : loccs)
locc_map.push_back (locc.lit);
return locc_map;
}
int Internal::lookahead_locc (const std::vector<int> &loccs) {
for (auto lit : loccs)
if (active (abs (lit)) && !assumed (lit) && !assumed (-lit) &&
!val (lit))
return lit;
return 0;
}
// This calculates the literal that appears the most often reusing the
// available datastructures and iterating over the clause set. This is too
// slow to be called iteratively. A faster (but inexact) version is
// lookahead_populate_loc and lookahead_loc.
int Internal::most_occurring_literal () {
init_noccs ();
for (const auto &c : clauses)
if (!c->redundant)
for (const auto &lit : *c)
if (active (lit))
noccs (lit)++;
int64_t max_noccs = 0;
int res = 0;
if (unsat)
return INT_MIN;
propagate ();
for (int idx = 1; idx <= max_var; idx++) {
if (!active (idx) || assumed (idx) || assumed (-idx) || val (idx))
continue;
for (int sign = -1; sign <= 1; sign += 2) {
const int lit = sign * idx;
if (!active (lit))
continue;
int64_t tmp = noccs (lit);
if (tmp <= max_noccs)
continue;
max_noccs = tmp;
res = lit;
}
}
MSG ("maximum occurrence %" PRId64 " of literal %d", max_noccs, res);
reset_noccs ();
return res;
}
// We probe on literals first, which occur more often negated and thus we
// sort the 'probes' stack in such a way that literals which occur negated
// less frequently come first. Probes are taken from the back of the stack.
struct probe_negated_noccs_rank {
Internal *internal;
probe_negated_noccs_rank (Internal *i) : internal (i) {}
typedef size_t Type;
Type operator() (int a) const { return internal->noccs (-a); }
};
// Follow the ideas in 'generate_probes' but flush non root probes and
// reorder remaining probes.
void Internal::lookahead_flush_probes () {
CADICAL_assert (!probes.empty ());
init_noccs ();
for (const auto &c : clauses) {
int a, b;
if (!is_binary_clause (c, a, b))
continue;
noccs (a)++;
noccs (b)++;
}
const auto eop = probes.end ();
auto j = probes.begin ();
for (auto i = j; i != eop; i++) {
int lit = *i;
if (!active (lit))
continue;
const bool have_pos_bin_occs = noccs (lit) > 0;
const bool have_neg_bin_occs = noccs (-lit) > 0;
if (have_pos_bin_occs == have_neg_bin_occs)
continue;
if (have_pos_bin_occs)
lit = -lit;
CADICAL_assert (!noccs (lit)), CADICAL_assert (noccs (-lit) > 0);
if (propfixed (lit) >= stats.all.fixed)
continue;
MSG ("keeping probe %d negated occs %" PRId64 "", lit, noccs (-lit));
*j++ = lit;
}
size_t remain = j - probes.begin ();
#ifndef CADICAL_QUIET
size_t flushed = probes.size () - remain;
#endif
probes.resize (remain);
rsort (probes.begin (), probes.end (), probe_negated_noccs_rank (this));
reset_noccs ();
shrink_vector (probes);
PHASE ("probe-round", stats.probingrounds,
"flushed %zd literals %.0f%% remaining %zd", flushed,
percent (flushed, remain + flushed), remain);
}
void Internal::lookahead_generate_probes () {
CADICAL_assert (probes.empty ());
// First determine all the literals which occur in binary clauses. It is
// way faster to go over the clauses once, instead of walking the watch
// lists for each literal.
//
init_noccs ();
for (const auto &c : clauses) {
int a, b;
if (!is_binary_clause (c, a, b))
continue;
noccs (a)++;
noccs (b)++;
}
for (int idx = 1; idx <= max_var; idx++) {
// Then focus on roots of the binary implication graph, which are
// literals occurring negatively in a binary clause, but not positively.
// If neither 'idx' nor '-idx' is a root it makes less sense to probe
// this variable.
// This argument requires that equivalent literal substitution through
// 'decompose' is performed, because otherwise there might be 'cyclic
// roots' which are not tried, i.e., -1 2 0, 1 -2 0, 1 2 3 0, 1 2 -3 0.
const bool have_pos_bin_occs = noccs (idx) > 0;
const bool have_neg_bin_occs = noccs (-idx) > 0;
// if (have_pos_bin_occs == have_neg_bin_occs) continue;
if (have_pos_bin_occs) {
int probe = -idx;
// See the discussion where 'propfixed' is used below.
//
if (propfixed (probe) >= stats.all.fixed)
continue;
MSG ("scheduling probe %d negated occs %" PRId64 "", probe,
noccs (-probe));
probes.push_back (probe);
}
if (have_neg_bin_occs) {
int probe = idx;
// See the discussion where 'propfixed' is used below.
//
if (propfixed (probe) >= stats.all.fixed)
continue;
MSG ("scheduling probe %d negated occs %" PRId64 "", probe,
noccs (-probe));
probes.push_back (probe);
}
}
rsort (probes.begin (), probes.end (), probe_negated_noccs_rank (this));
reset_noccs ();
shrink_vector (probes);
PHASE ("probe-round", stats.probingrounds,
"scheduled %zd literals %.0f%%", probes.size (),
percent (probes.size (), 2 * max_var));
}
int Internal::lookahead_next_probe () {
int generated = 0;
for (;;) {
if (probes.empty ()) {
if (generated++)
return 0;
lookahead_generate_probes ();
}
while (!probes.empty ()) {
int probe = probes.back ();
probes.pop_back ();
// Eliminated or assigned.
//
if (!active (probe) || assumed (probe) || assumed (-probe))
continue;
// There is now new unit since the last time we propagated this probe,
// thus we propagated it before without obtaining a conflict and
// nothing changed since then. Thus there is no need to propagate it
// again. This observation was independently made by Partik Simons
// et.al. in the context of implementing 'smodels' (see for instance
// Alg. 4 in his JAIR article from 2002) and it has also been
// contributed to the thesis work of Yacine Boufkhad.
//
if (propfixed (probe) >= stats.all.fixed)
continue;
return probe;
}
}
}
bool non_tautological_cube (std::vector<int> cube) {
std::sort (begin (cube), end (cube), clause_lit_less_than ());
for (size_t i = 0, j = 1; j < cube.size (); ++i, ++j)
if (cube[i] == cube[j])
return false;
else if (cube[i] == -cube[j])
return false;
else if (cube[i] == 0)
return false;
return true;
}
bool Internal::terminating_asked () {
if (external->terminator && external->terminator->terminate ()) {
MSG ("connected terminator forces termination");
return true;
}
if (termination_forced) {
MSG ("termination forced");
return true;
}
return false;
}
// We run probing on all literals with some differences:
//
// * no limit on the number of propagations. We rely on terminating to
// stop()
// * we run only one round
//
// The run can be expensive, so we actually first run the cheaper
// occurrence version and only then run lookahead.
//
int Internal::lookahead_probing () {
if (!active ())
return 0;
MSG ("lookahead-probe-round %" PRId64
" without propagations limit and %zu assumptions",
stats.probingrounds, assumptions.size ());
termination_forced = false;
#ifndef CADICAL_QUIET
int old_failed = stats.failed;
int64_t old_probed = stats.probed;
#endif
int64_t old_hbrs = stats.hbrs;
if (unsat)
return INT_MIN;
if (level)
backtrack ();
if (!propagate ()) {
MSG ("empty clause before probing");
learn_empty_clause ();
return INT_MIN;
}
if (terminating_asked ())
return most_occurring_literal ();
decompose ();
if (ternary ()) // If we derived a binary clause
decompose (); // then start another round of ELS.
// Remove duplicated binary clauses and perform in essence hyper unary
// resolution, i.e., derive the unit '2' from '1 2' and '-1 2'.
//
mark_duplicated_binary_clauses_as_garbage ();
lim.conflicts = -1;
if (!probes.empty ())
lookahead_flush_probes ();
// We reset 'propfixed' since there was at least another conflict thus
// a new learned clause, which might produce new propagations (and hyper
// binary resolvents). During 'generate_probes' we keep the old value.
//
for (int idx = 1; idx <= max_var; idx++)
propfixed (idx) = propfixed (-idx) = -1;
CADICAL_assert (unsat || propagated == trail.size ());
propagated = propagated2 = trail.size ();
int probe;
int res = most_occurring_literal ();
int max_hbrs = -1;
set_mode (PROBE);
MSG ("unsat = %d, terminating_asked () = %d ", unsat,
terminating_asked ());
init_probehbr_lrat ();
while (!unsat && !terminating_asked () &&
(probe = lookahead_next_probe ())) {
stats.probed++;
int hbrs;
probe_assign_decision (probe);
if (probe_propagate ())
hbrs = trail.size (), backtrack ();
else
hbrs = 0, failed_literal (probe);
clean_probehbr_lrat ();
if (max_hbrs < hbrs ||
(max_hbrs == hbrs &&
internal->bumped (probe) > internal->bumped (res))) {
res = probe;
max_hbrs = hbrs;
}
}
reset_mode (PROBE);
if (unsat) {
MSG ("probing derived empty clause");
res = INT_MIN;
} else if (propagated < trail.size ()) {
MSG ("probing produced %zd units",
(size_t) (trail.size () - propagated));
if (!propagate ()) {
MSG ("propagating units after probing results in empty clause");
learn_empty_clause ();
res = INT_MIN;
} else
sort_watches ();
}
#ifndef CADICAL_QUIET
int failed = stats.failed - old_failed;
int64_t probed = stats.probed - old_probed;
#endif
int64_t hbrs = stats.hbrs - old_hbrs;
MSG ("lookahead-probe-round %" PRId64 " probed %" PRId64
" and found %d failed literals",
stats.probingrounds, probed, failed);
if (hbrs)
PHASE ("lookahead-probe-round", stats.probingrounds,
"found %" PRId64 " hyper binary resolvents", hbrs);
MSG ("lookahead literal %d with %d\n", res, max_hbrs);
return res;
}
CubesWithStatus Internal::generate_cubes (int depth, int min_depth) {
if (!active () || depth == 0) {
CubesWithStatus cubes;
cubes.status = 0;
cubes.cubes.push_back (std::vector<int> ());
return cubes;
}
lookingahead = true;
START (lookahead);
MSG ("Generating cubes of depth %i", depth);
// presimplify required due to assumptions
termination_forced = false;
int res = already_solved ();
if (res == 0)
res = restore_clauses ();
if (unsat)
res = 10;
if (res != 0)
res = solve (true);
if (res != 0) {
MSG ("Solved during preprocessing");
CubesWithStatus cubes;
cubes.status = res;
lookingahead = false;
STOP (lookahead);
return cubes;
}
reset_limits ();
MSG ("generate cubes with %zu assumptions\n", assumptions.size ());
CADICAL_assert (ntab.empty ());
std::vector<int> current_assumptions{assumptions};
std::vector<std::vector<int>> cubes{{assumptions}};
auto loccs{lookahead_populate_locc ()};
LOG ("loccs populated\n");
CADICAL_assert (ntab.empty ());
for (int i = 0; i < depth; ++i) {
LOG ("Probing at depth %i, currently %zu have been generated", i,
cubes.size ());
std::vector<std::vector<int>> cubes2{std::move (cubes)};
cubes.clear ();
for (size_t j = 0; j < cubes2.size (); ++j) {
CADICAL_assert (ntab.empty ());
CADICAL_assert (!unsat);
reset_assumptions ();
for (auto lit : cubes2[j])
assume (lit);
restore_clauses ();
propagate ();
// preprocess_round(0); //uncomment maybe
if (unsat) {
LOG ("current cube is unsat; skipping");
unsat = false;
continue;
}
int res = terminating_asked () ? lookahead_locc (loccs)
: lookahead_probing ();
if (unsat) {
LOG ("current cube is unsat; skipping");
unsat = false;
continue;
}
if (res == 0) {
LOG ("no lit to split %i", res);
cubes.push_back (cubes2[j]);
continue;
}
CADICAL_assert (res != 0);
LOG ("splitting on lit %i", res);
std::vector<int> cube1{cubes2[j]};
cube1.push_back (res);
std::vector<int> cube2{std::move (cubes2[j])};
cube2.push_back (-res);
cubes.push_back (cube1);
cubes.push_back (cube2);
}
if (terminating_asked () && i >= min_depth)
break;
}
CADICAL_assert (std::for_each (
std::begin (cubes), std::end (cubes),
[] (std::vector<int> cube) { return non_tautological_cube (cube); }));
reset_assumptions ();
for (auto lit : current_assumptions)
assume (lit);
STOP (lookahead);
lookingahead = false;
if (unsat) {
LOG ("Solved during preprocessing");
CubesWithStatus cubes;
cubes.status = 20;
return cubes;
}
CubesWithStatus rcubes;
rcubes.status = 0;
rcubes.cubes = cubes;
return rcubes;
}
} // namespace CaDiCaL
ABC_NAMESPACE_IMPL_END

View File

@ -1,835 +0,0 @@
#include "global.h"
#include "internal.hpp"
ABC_NAMESPACE_IMPL_START
namespace CaDiCaL {
/*------------------------------------------------------------------------*/
inline unsigned LratChecker::l2u (int lit) {
CADICAL_assert (lit);
CADICAL_assert (lit != INT_MIN);
unsigned res = 2 * (abs (lit) - 1);
if (lit < 0)
res++;
return res;
}
signed char &LratChecker::mark (int lit) {
const unsigned u = l2u (lit);
CADICAL_assert (u < marks.size ());
return marks[u];
}
signed char &LratChecker::checked_lit (int lit) {
const unsigned u = l2u (lit);
CADICAL_assert (u < checked_lits.size ());
return checked_lits[u];
}
/*------------------------------------------------------------------------*/
LratCheckerClause *LratChecker::new_clause () {
const size_t size = imported_clause.size ();
CADICAL_assert (size <= UINT_MAX);
const int off = size ? 1 : 0;
const size_t bytes =
sizeof (LratCheckerClause) + (size - off) * sizeof (int);
LratCheckerClause *res = (LratCheckerClause *) new char[bytes];
res->garbage = false;
res->next = 0;
res->hash = last_hash;
res->id = last_id;
res->size = size;
res->used = false;
res->tautological = false;
int *literals = res->literals, *p = literals;
#ifndef CADICAL_NDEBUG
for (auto &b : checked_lits)
CADICAL_assert (!b); // = false;
#endif
for (const auto &lit : imported_clause) {
*p++ = lit;
checked_lit (-lit) = true;
if (checked_lit (lit)) {
LOG (imported_clause, "LRAT CHECKER clause tautological");
res->tautological = true;
}
}
for (const auto &lit : imported_clause)
checked_lit (-lit) = false;
num_clauses++;
return res;
}
void LratChecker::delete_clause (LratCheckerClause *c) {
CADICAL_assert (c);
if (!c->garbage) {
CADICAL_assert (num_clauses);
num_clauses--;
} else {
CADICAL_assert (num_garbage);
num_garbage--;
}
delete[] (char *) c;
}
void LratChecker::enlarge_clauses () {
CADICAL_assert (num_clauses == size_clauses);
const uint64_t new_size_clauses = size_clauses ? 2 * size_clauses : 1;
LOG ("LRAT CHECKER enlarging clauses of checker from %" PRIu64
" to %" PRIu64,
(uint64_t) size_clauses, (uint64_t) new_size_clauses);
LratCheckerClause **new_clauses;
new_clauses = new LratCheckerClause *[new_size_clauses];
clear_n (new_clauses, new_size_clauses);
for (uint64_t i = 0; i < size_clauses; i++) {
for (LratCheckerClause *c = clauses[i], *next; c; c = next) {
next = c->next;
const uint64_t h = reduce_hash (c->hash, new_size_clauses);
c->next = new_clauses[h];
new_clauses[h] = c;
}
}
delete[] clauses;
clauses = new_clauses;
size_clauses = new_size_clauses;
}
// Probably not necessary since we have no watches.
//
void LratChecker::collect_garbage_clauses () {
stats.collections++;
LOG ("LRAT CHECKER collecting %" PRIu64 " garbage clauses %.0f%%",
num_garbage, percent (num_garbage, num_clauses));
for (LratCheckerClause *c = garbage, *next; c; c = next)
next = c->next, delete_clause (c);
CADICAL_assert (!num_garbage);
garbage = 0;
}
/*------------------------------------------------------------------------*/
LratChecker::LratChecker (Internal *i)
: internal (i), size_vars (0), concluded (false), num_clauses (0),
num_finalized (0), num_garbage (0), size_clauses (0), clauses (0),
garbage (0), last_hash (0), last_id (0), current_id (0) {
// Initialize random number table for hash function.
//
Random random (42);
for (unsigned n = 0; n < num_nonces; n++) {
uint64_t nonce = random.next ();
if (!(nonce & 1))
nonce++;
CADICAL_assert (nonce), CADICAL_assert (nonce & 1);
nonces[n] = nonce;
}
memset (&stats, 0, sizeof (stats)); // Initialize statistics.
}
void LratChecker::connect_internal (Internal *i) {
internal = i;
LOG ("connected to internal");
}
LratChecker::~LratChecker () {
LOG ("LRAT CHECKER delete");
for (size_t i = 0; i < size_clauses; i++)
for (LratCheckerClause *c = clauses[i], *next; c; c = next)
next = c->next, delete_clause (c);
for (LratCheckerClause *c = garbage, *next; c; c = next)
next = c->next, delete_clause (c);
delete[] clauses;
}
/*------------------------------------------------------------------------*/
void LratChecker::enlarge_vars (int64_t idx) {
CADICAL_assert (0 < idx), CADICAL_assert (idx <= INT_MAX);
int64_t new_size_vars = size_vars ? 2 * size_vars : 2;
while (idx >= new_size_vars)
new_size_vars *= 2;
LOG ("LRAT CHECKER enlarging variables of checker from %" PRId64
" to %" PRId64 "",
size_vars, new_size_vars);
marks.resize (2 * new_size_vars);
checked_lits.resize (2 * new_size_vars);
CADICAL_assert (idx < new_size_vars);
size_vars = new_size_vars;
}
inline void LratChecker::import_literal (int lit) {
CADICAL_assert (lit);
CADICAL_assert (lit != INT_MIN);
int idx = abs (lit);
if (idx >= size_vars)
enlarge_vars (idx);
imported_clause.push_back (lit);
}
void LratChecker::import_clause (const vector<int> &c) {
for (const auto &lit : c)
import_literal (lit);
}
/*------------------------------------------------------------------------*/
uint64_t LratChecker::reduce_hash (uint64_t hash, uint64_t size) {
CADICAL_assert (size > 0);
unsigned shift = 32;
uint64_t res = hash;
while ((((uint64_t) 1) << shift) > size) {
res ^= res >> shift;
shift >>= 1;
}
res &= size - 1;
CADICAL_assert (res < size);
return res;
}
uint64_t LratChecker::compute_hash (const int64_t id) {
CADICAL_assert (id > 0);
unsigned j = id % num_nonces;
uint64_t tmp = nonces[j] * (uint64_t) id;
return last_hash = tmp;
}
LratCheckerClause **LratChecker::find (const int64_t id) {
stats.searches++;
LratCheckerClause **res, *c;
const uint64_t hash = compute_hash (id);
const uint64_t h = reduce_hash (hash, size_clauses);
for (res = clauses + h; (c = *res); res = &c->next) {
if (c->hash == hash && c->id == id) {
break;
}
stats.collisions++;
}
return res;
}
void LratChecker::insert () {
stats.insertions++;
if (num_clauses == size_clauses)
enlarge_clauses ();
const uint64_t h = reduce_hash (compute_hash (last_id), size_clauses);
LratCheckerClause *c = new_clause ();
c->next = clauses[h];
clauses[h] = c;
}
/*------------------------------------------------------------------------*/
// "strict" resolution check instead of rup check
bool LratChecker::check_resolution (vector<int64_t> proof_chain) {
if (proof_chain.empty ()) {
LOG ("LRAT CHECKER resolution check skipped clause is tautological");
return true;
}
// LOG (imported_clause, "LRAT CHECKER checking clause with resolution");
#ifndef CADICAL_NDEBUG
for (auto &b : checked_lits)
CADICAL_assert (!b); // = false;
#endif
if (!proof_chain.size () || proof_chain.back () < 0)
return false;
LratCheckerClause *c = *find (proof_chain.back ());
CADICAL_assert (c);
for (int *i = c->literals; i < c->literals + c->size; i++) {
int lit = *i;
checked_lit (lit) = true;
CADICAL_assert (!checked_lit (-lit));
}
for (auto p = proof_chain.end () - 2; p >= proof_chain.begin (); p--) {
auto &id = *p;
c = *find (id);
CADICAL_assert (c); // since this is checked in check already
for (int *i = c->literals; i < c->literals + c->size; i++) {
int lit = *i;
if (!checked_lit (-lit))
checked_lit (lit) = true;
else
checked_lit (-lit) = false;
}
}
for (const auto &lit : imported_clause) {
if (checked_lit (-lit)) {
LOG ("LRAT CHECKER resolution failed, resolved literal %d in learned "
"clause",
lit);
for (auto &b : checked_lits)
b = false; // clearing checking bits
return false;
}
if (!checked_lit (lit)) {
// learned clause is subsumed by resolvents
checked_lit (lit) = true;
}
checked_lit (-lit) = true;
}
bool failed = false;
for (int64_t lit = 1; lit < size_vars; lit++) {
bool ok = checked_lit (lit) && checked_lit (-lit);
ok = ok || (!checked_lit (lit) && !checked_lit (-lit));
checked_lit (lit) = checked_lit (-lit) = false;
if (!ok && !failed) {
LOG ("LRAT CHECKER resolution failed, learned clause does not match "
"on "
"variable %" PRId64,
lit);
failed = true;
}
}
return !failed;
}
/*------------------------------------------------------------------------*/
bool LratChecker::check (vector<int64_t> proof_chain) {
LOG (imported_clause, "LRAT CHECKER checking clause");
stats.checks++;
#ifndef CADICAL_NDEBUG
for (auto &b : checked_lits)
CADICAL_assert (!b); // = false;
#endif
bool taut = false;
for (const auto &lit : imported_clause) { // tautological clauses
checked_lit (-lit) = true;
if (checked_lit (lit)) {
LOG (imported_clause, "LRAT CHECKER clause tautological");
CADICAL_assert (!proof_chain.size ()); // would be unnecessary hence a bug
taut = true;
}
}
// we assume that we can have RUP and ER clauses. One side of the ER
// clauses are pure, i.e. without any chain, the long clause is blocked,
// so the chain consists only of negative ids. Therefore these checks are
// enough to distiguish between RUP and ER
if (taut || !proof_chain.size () || proof_chain.back () < 0) {
for (const auto &lit : imported_clause) { // tautological clauses
checked_lit (-lit) = false;
}
return taut;
}
vector<LratCheckerClause *> used_clauses;
bool checking = false;
for (auto &id : proof_chain) {
LratCheckerClause *c = *find (id);
if (!c) {
LOG ("LRAT CHECKER LRAT failed. Did not find clause with id %" PRIu64,
id);
break;
}
if (c->tautological) {
LOG ("LRAT CHECKER LRAT failed. Clause with id %" PRId64
" is tautological",
id);
break;
}
used_clauses.push_back (c);
if (c->used) {
LOG ("LRAT CHECKER LRAT failed. Id %" PRId64
" was used multiple times",
id);
break;
} else
c->used = true;
int unit = 0;
for (int *i = c->literals; i < c->literals + c->size; i++) {
int lit = *i;
if (checked_lit (-lit))
continue;
if (unit && unit != lit) {
unit = INT_MIN; // multiple unfalsified literals
break;
}
unit = lit; // potential unit
}
if (unit == INT_MIN) {
LOG ("LRAT CHECKER check failed, found non unit clause %" PRId64, id);
break;
}
if (!unit) {
LOG ("LRAT CHECKER check succeded, clause falsified %" PRId64, id);
checking = true;
break;
}
// LOG ("LRAT CHECKER found unit clause %" PRIu64 ", assign %d", id,
// unit);
checked_lit (unit) = true;
}
for (auto &lc : used_clauses) {
lc->used = false;
}
for (auto &b : checked_lits)
b = false;
if (!checking) {
LOG ("LRAT CHECKER failed, no conflict found");
return false; // check failed because no empty clause was found
}
return true;
}
bool LratChecker::check_blocked (vector<int64_t> proof_chain) {
for (const auto &lit : imported_clause) {
checked_lit (-lit) = true;
mark (-lit) = true;
}
for (size_t i = 0; i < size_clauses; i++) {
for (LratCheckerClause *c = clauses[i], *next; c; c = next) {
next = c->next;
if (c->garbage)
continue;
// if c is part of the proof chain its id occurs negatively there.
if (std::find (proof_chain.begin (), proof_chain.end (), -c->id) !=
proof_chain.end ()) {
// clause needs to be blocked
unsigned count = 0;
vector<int> candidates;
for (unsigned i = 0; i < c->size; i++) {
const int lit = c->literals[i];
if (checked_lit (lit)) {
count++;
}
if (mark (lit)) {
candidates.push_back (lit);
}
}
if (count < 2) {
// check failed
for (const auto &lit : imported_clause) {
checked_lit (-lit) = false;
mark (-lit) = false;
}
return false;
} else {
// all literals outside of candidates are not valid RAT candidates
for (auto &lit : imported_clause) {
if (mark (-lit) &&
std::find (candidates.begin (), candidates.end (), -lit) ==
candidates.end ()) {
mark (-lit) = false;
}
}
}
} else {
// any literal contained in the clause is not a valid RAT candidate
for (unsigned i = 0; i < c->size; i++) {
const int lit = c->literals[i];
if (checked_lit (lit)) {
mark (lit) = false;
}
}
}
}
}
bool success = false;
for (const auto &lit : imported_clause) {
if (mark (-lit))
success = true;
checked_lit (-lit) = mark (-lit) = false;
}
return success;
}
/*------------------------------------------------------------------------*/
void LratChecker::add_original_clause (int64_t id, bool,
const vector<int> &c, bool restore) {
START (checking);
LOG (c, "LRAT CHECKER addition of original clause[%" PRId64 "]", id);
if (restore)
restore_clause (id, c);
stats.added++;
stats.original++;
import_clause (c);
last_id = id;
if (!restore && id == 1 + current_id)
current_id = id;
if (size_clauses && !restore) {
LratCheckerClause **p = find (id), *d = *p;
if (d) {
fatal_message_start ();
fputs ("different clause with id ", stderr);
fprintf (stderr, "%" PRId64, id);
fputs (" already present\n", stderr);
fatal_message_end ();
}
}
CADICAL_assert (id);
insert ();
imported_clause.clear ();
STOP (checking);
}
void LratChecker::add_derived_clause (int64_t id, bool,
const vector<int> &c,
const vector<int64_t> &proof_chain) {
START (checking);
LOG (c, "LRAT CHECKER addition of derived clause[%" PRId64 "]", id);
stats.added++;
stats.derived++;
import_clause (c);
last_id = id;
CADICAL_assert (id == current_id + 1);
current_id = id;
if (size_clauses) {
LratCheckerClause **p = find (id), *d = *p;
if (d) {
fatal_message_start ();
fputs ("different clause with id ", stderr);
fprintf (stderr, "%" PRId64, id);
fputs (" already present\n", stderr);
fatal_message_end ();
}
}
CADICAL_assert (id);
bool failed = true;
if (check (proof_chain) && check_resolution (proof_chain)) {
failed = false;
} else if (check_blocked (proof_chain)) {
failed = false;
}
if (failed) {
LOG (proof_chain, "LRAT CHECKER check failed with chain");
#ifdef LOGGING
for (const auto &pid : proof_chain) {
const int64_t aid = abs (pid);
LratCheckerClause **p = find (aid), *d = *p;
LOG (d->literals, d->size, "clause[%" PRId64 "]", pid);
}
#endif
fatal_message_start ();
fputs ("failed to check derived clause:\n", stderr);
for (const auto &lit : imported_clause)
fprintf (stderr, "%d ", lit);
fputc ('0', stderr);
fatal_message_end ();
} else
insert ();
imported_clause.clear ();
STOP (checking);
}
void LratChecker::add_assumption_clause (int64_t id, const vector<int> &c,
const vector<int64_t> &chain) {
for (auto &lit : c) {
if (std::find (assumptions.begin (), assumptions.end (), -lit) !=
assumptions.end ())
continue;
if (std::find (constraint.begin (), constraint.end (), -lit) !=
constraint.end ())
continue;
fatal_message_start ();
fputs ("clause contains non assumptions or constraint literals\n",
stderr);
fatal_message_end ();
}
add_derived_clause (id, true, c, chain);
delete_clause (id, true, c);
assumption_clauses.push_back (id);
}
void LratChecker::add_assumption (int a) { assumptions.push_back (a); }
void LratChecker::add_constraint (const vector<int> &c) {
constraint.clear ();
for (auto &lit : c) {
CADICAL_assert (lit);
if (std::find (constraint.begin (), constraint.end (), lit) !=
constraint.end ())
continue;
constraint.push_back (lit);
}
}
void LratChecker::reset_assumptions () {
assumption_clauses.clear ();
assumptions.clear ();
concluded = false;
// constraint.clear ();
}
void LratChecker::conclude_unsat (ConclusionType conclusion,
const vector<int64_t> &ids) {
if (concluded) {
fatal_message_start ();
fputs ("already concluded\n", stderr);
fatal_message_end ();
}
concluded = true;
if (conclusion == CONFLICT) {
LratCheckerClause **p = find (ids.back ()), *d = *p;
if (!d || d->size) {
fatal_message_start ();
fputs ("empty clause not in proof\n", stderr);
fatal_message_end ();
}
return;
} else if (conclusion == ASSUMPTIONS) {
if (ids.size () != 1 || assumption_clauses.size () != 1) {
fatal_message_start ();
fputs ("expected exactly one assumption clause\n", stderr);
fatal_message_end ();
}
if (ids.back () != assumption_clauses.back ()) {
fatal_message_start ();
fputs ("conclusion is not an assumption clause\n", stderr);
fatal_message_end ();
}
return;
} else {
CADICAL_assert (conclusion == CONSTRAINT);
if (constraint.size () != ids.size ()) {
fatal_message_start ();
fputs ("not complete conclusion given for constraint\n", stderr);
fputs ("The constraint contains the literals: ", stderr);
for (auto c : constraint) {
fprintf (stderr, "%d ", c);
}
fputs ("\nThe ids are: ", stderr);
for (auto c : ids) {
fprintf (stderr, "%" PRId64 " ", c);
}
fatal_message_end ();
}
for (auto &id : ids) {
if (std::find (assumption_clauses.begin (), assumption_clauses.end (),
id) != assumption_clauses.end ())
continue;
fatal_message_start ();
fputs ("assumption clause for constraint missing\n", stderr);
fatal_message_end ();
}
}
}
/*------------------------------------------------------------------------*/
void LratChecker::delete_clause (int64_t id, bool, const vector<int> &c) {
START (checking);
LOG (c, "LRAT CHECKER checking deletion of clause[%" PRId64 "]", id);
stats.deleted++;
import_clause (c);
last_id = id;
LratCheckerClause **p = find (id), *d = *p;
if (d) {
for (const auto &lit : imported_clause)
mark (lit) = true;
const int *dp = d->literals;
for (unsigned i = 0; i < d->size; i++) {
int lit = *(dp + i);
if (!mark (lit)) { // should never happen since ids
fatal_message_start (); // are unique.
fputs ("deleted clause not in proof:\n", stderr);
for (const auto &lit : imported_clause)
fprintf (stderr, "%d ", lit);
fputc ('0', stderr);
fatal_message_end ();
}
}
for (const auto &lit : imported_clause)
mark (lit) = false;
// Remove from hash table, mark as garbage, connect to garbage list.
num_garbage++;
CADICAL_assert (num_clauses);
num_clauses--;
*p = d->next;
d->next = garbage;
garbage = d;
d->garbage = true;
// If there are enough garbage clauses collect them.
// TODO: probably can just delete clause directly without
// specific garbage collection phase.
if (num_garbage > 0.5 * max ((size_t) size_clauses, (size_t) size_vars))
collect_garbage_clauses ();
} else {
fatal_message_start ();
fputs ("deleted clause not in proof:\n", stderr);
for (const auto &lit : imported_clause)
fprintf (stderr, "%d ", lit);
fputc ('0', stderr);
fatal_message_end ();
}
imported_clause.clear ();
STOP (checking);
}
/*------------------------------------------------------------------------*/
void LratChecker::weaken_minus (int64_t id, const vector<int> &c) {
LOG (c, "LRAT CHECKER saving clause[%" PRId64 "] to restore later", id);
import_clause (c);
CADICAL_assert (id <= current_id);
last_id = id;
LratCheckerClause **p = find (id), *d = *p;
if (d) {
for (const auto &lit : imported_clause)
mark (lit) = true;
const int *dp = d->literals;
for (unsigned i = 0; i < d->size; i++) {
int lit = *(dp + i);
if (!mark (lit)) { // should never happen since ids
fatal_message_start (); // are unique.
fputs ("deleted clause not in proof:\n", stderr);
for (const auto &lit : imported_clause)
fprintf (stderr, "%d ", lit);
fputc ('0', stderr);
fatal_message_end ();
}
}
for (const auto &lit : imported_clause)
mark (lit) = false;
} else {
fatal_message_start ();
fputs ("weakened clause not in proof:\n", stderr);
for (const auto &lit : imported_clause)
fprintf (stderr, "%d ", lit);
fputc ('0', stderr);
fatal_message_end ();
}
imported_clause.clear ();
vector<int> e = c;
sort (begin (e), end (e));
clauses_to_reconstruct[id] = e;
}
void LratChecker::restore_clause (int64_t id, const vector<int> &c) {
LOG (c, "LRAT CHECKER check of restoration of clause[%" PRId64 "]", id);
if (clauses_to_reconstruct.find (id) == end (clauses_to_reconstruct)) {
fatal_message_start ();
fputs ("restoring clauses not deleted previously:\n", stderr);
for (const auto &lit : c)
fprintf (stderr, "%d ", lit);
fputc ('0', stderr);
fatal_message_end ();
}
vector<int> e = c;
sort (begin (e), end (e));
const vector<int> &d = clauses_to_reconstruct.find (id)->second;
bool eq = true;
if (c.size () != d.size ()) {
eq = false;
}
for (std::vector<int>::size_type i = 0; i < e.size () && eq; ++i) {
eq = (e[i] == d[i]);
}
if (!eq) {
fatal_message_start ();
fputs ("restoring clause that is different than the one imported:\n",
stderr);
for (const auto &lit : c)
fprintf (stderr, "%d ", lit);
fputc ('0', stderr);
fputs ("vs:\n", stderr);
for (const auto &lit : d)
fprintf (stderr, "%d ", lit);
fputc ('0', stderr);
fatal_message_end ();
}
clauses_to_reconstruct.erase (id);
}
void LratChecker::finalize_clause (int64_t id, const vector<int> &c) {
START (checking);
LOG (c, "LRAT CHECKER checking finalize of clause[%" PRId64 "]", id);
stats.finalized++;
num_finalized++;
import_clause (c);
CADICAL_assert (id <= current_id);
last_id = id;
LratCheckerClause **p = find (id), *d = *p;
if (d) {
for (const auto &lit : imported_clause)
mark (lit) = true;
const int *dp = d->literals;
for (unsigned i = 0; i < d->size; i++) {
int lit = *(dp + i);
if (!mark (lit)) { // should never happen since ids
fatal_message_start (); // are unique.
fputs ("deleted clause not in proof:\n", stderr);
for (const auto &lit : imported_clause)
fprintf (stderr, "%d ", lit);
fputc ('0', stderr);
fatal_message_end ();
}
}
for (const auto &lit : imported_clause)
mark (lit) = false;
} else {
fatal_message_start ();
fputs ("deleted clause not in proof:\n", stderr);
for (const auto &lit : imported_clause)
fprintf (stderr, "%d ", lit);
fputc ('0', stderr);
fatal_message_end ();
}
imported_clause.clear ();
STOP (checking);
}
// check if all clauses have been deleted
void LratChecker::report_status (int, int64_t) {
START (checking);
if (num_finalized == num_clauses) {
num_finalized = 0;
LOG ("LRAT CHECKER successful finalize check, all clauses have been "
"deleted");
} else {
fatal_message_start ();
fputs ("finalize check failed ", stderr);
fprintf (stderr, "%" PRIu64, num_clauses);
fputs (" are not finalized", stderr);
fatal_message_end ();
}
STOP (checking);
}
/*------------------------------------------------------------------------*/
void LratChecker::dump () {
int max_var = 0;
for (uint64_t i = 0; i < size_clauses; i++)
for (LratCheckerClause *c = clauses[i]; c; c = c->next)
for (unsigned i = 0; i < c->size; i++)
if (abs (c->literals[i]) > max_var)
max_var = abs (c->literals[i]);
printf ("p cnf %d %" PRIu64 "\n", max_var, num_clauses);
for (uint64_t i = 0; i < size_clauses; i++)
for (LratCheckerClause *c = clauses[i]; c; c = c->next) {
for (unsigned i = 0; i < c->size; i++)
printf ("%d ", c->literals[i]);
printf ("0\n");
}
}
void LratChecker::begin_proof (int64_t id) { current_id = id; }
} // namespace CaDiCaL
ABC_NAMESPACE_IMPL_END

View File

@ -1,206 +0,0 @@
#include "global.h"
#include "internal.hpp"
#include <limits.h>
ABC_NAMESPACE_IMPL_START
namespace CaDiCaL {
/*------------------------------------------------------------------------*/
LratTracer::LratTracer (Internal *i, File *f, bool b)
: internal (i), file (f), binary (b)
#ifndef CADICAL_QUIET
,
added (0), deleted (0)
#endif
,
latest_id (0) {
(void) internal;
}
void LratTracer::connect_internal (Internal *i) {
internal = i;
file->connect_internal (internal);
LOG ("LRAT TRACER connected to internal");
}
LratTracer::~LratTracer () {
LOG ("LRAT TRACER delete");
delete file;
}
/*------------------------------------------------------------------------*/
inline void LratTracer::put_binary_zero () {
CADICAL_assert (binary);
CADICAL_assert (file);
file->put ((unsigned char) 0);
}
inline void LratTracer::put_binary_lit (int lit) {
CADICAL_assert (binary);
CADICAL_assert (file);
CADICAL_assert (lit != INT_MIN);
unsigned idx = abs (lit);
CADICAL_assert (idx < (1u << 31));
unsigned x = 2 * idx + (lit < 0);
unsigned char ch;
while (x & ~0x7f) {
ch = (x & 0x7f) | 0x80;
file->put (ch);
x >>= 7;
}
ch = x;
file->put (ch);
}
inline void LratTracer::put_binary_id (int64_t id) {
CADICAL_assert (binary);
CADICAL_assert (file);
uint64_t x = abs (id);
x = 2 * x + (id < 0);
unsigned char ch;
while (x & ~0x7f) {
ch = (x & 0x7f) | 0x80;
file->put (ch);
x >>= 7;
}
ch = x;
file->put (ch);
}
/*------------------------------------------------------------------------*/
void LratTracer::lrat_add_clause (int64_t id, const vector<int> &clause,
const vector<int64_t> &chain) {
if (delete_ids.size ()) {
if (!binary)
file->put (latest_id), file->put (" ");
if (binary)
file->put ('d');
else
file->put ("d ");
for (auto &did : delete_ids) {
if (binary)
put_binary_id (did);
else
file->put (did), file->put (" ");
}
if (binary)
put_binary_zero ();
else
file->put ("0\n");
delete_ids.clear ();
}
latest_id = id;
if (binary)
file->put ('a'), put_binary_id (id);
else
file->put (id), file->put (" ");
for (const auto &external_lit : clause)
if (binary)
put_binary_lit (external_lit);
else
file->put (external_lit), file->put (' ');
if (binary)
put_binary_zero ();
else
file->put ("0 ");
for (const auto &c : chain)
if (binary)
put_binary_id (c);
else
file->put (c), file->put (' '); // in proof chain, so they get
if (binary)
put_binary_zero (); // since cadical has no rat-steps
else
file->put ("0\n"); // this is just 2c here
}
void LratTracer::lrat_delete_clause (int64_t id) {
delete_ids.push_back (id); // pushing off deletion for later
}
/*------------------------------------------------------------------------*/
void LratTracer::add_derived_clause (int64_t id, bool,
const vector<int> &clause,
const vector<int64_t> &chain) {
if (file->closed ())
return;
LOG ("LRAT TRACER tracing addition of derived clause");
lrat_add_clause (id, clause, chain);
#ifndef CADICAL_QUIET
added++;
#endif
}
void LratTracer::delete_clause (int64_t id, bool, const vector<int> &) {
if (file->closed ())
return;
LOG ("LRAT TRACER tracing deletion of clause");
lrat_delete_clause (id);
#ifndef CADICAL_QUIET
deleted++;
#endif
}
void LratTracer::begin_proof (int64_t id) {
if (file->closed ())
return;
LOG ("LRAT TRACER tracing begin of proof");
latest_id = id;
}
/*------------------------------------------------------------------------*/
bool LratTracer::closed () { return file->closed (); }
#ifndef CADICAL_QUIET
void LratTracer::print_statistics () {
uint64_t bytes = file->bytes ();
uint64_t total = added + deleted;
MSG ("LRAT %" PRId64 " added clauses %.2f%%", added,
percent (added, total));
MSG ("LRAT %" PRId64 " deleted clauses %.2f%%", deleted,
percent (deleted, total));
MSG ("LRAT %" PRId64 " bytes (%.2f MB)", bytes,
bytes / (double) (1 << 20));
}
#endif
void LratTracer::close (bool print) {
CADICAL_assert (!closed ());
file->close ();
#ifndef CADICAL_QUIET
if (print) {
MSG ("LRAT proof file '%s' closed", file->name ());
print_statistics ();
}
#else
(void) print;
#endif
}
void LratTracer::flush (bool print) {
CADICAL_assert (!closed ());
file->flush ();
#ifndef CADICAL_QUIET
if (print) {
MSG ("LRAT proof file '%s' flushed", file->name ());
print_statistics ();
}
#else
(void) print;
#endif
}
} // namespace CaDiCaL
ABC_NAMESPACE_IMPL_END

View File

@ -1,440 +0,0 @@
#include "global.h"
#include "internal.hpp"
ABC_NAMESPACE_IMPL_START
namespace CaDiCaL {
// It turns out that even in the competition there are formulas which are
// easy to satisfy by either setting all variables to the same truth value
// or by assigning variables to the same value and propagating it. In the
// latter situation this can be done either in the order of all variables
// (forward or backward) or in the order of all clauses. These lucky
// assignments can be tested initially in a kind of pre-solving step.
// This function factors out clean up code common among the 'lucky'
// functions for backtracking and resetting a potential conflict. One could
// also use exceptions here, but there are two different reasons for
// aborting early. The first kind of aborting is due to asynchronous
// termination and the second kind due to a situation in which it is clear
// that a particular function will not be successful (for instance a
// completely negative clause is found). The latter situation returns zero
// and will just abort the particular lucky function, while the former will
// abort all (by returning '-1').
int Internal::unlucky (int res) {
if (level > 0)
backtrack ();
if (conflict)
conflict = 0;
return res;
}
int Internal::trivially_false_satisfiable () {
LOG ("checking that all clauses contain a negative literal");
CADICAL_assert (!level);
CADICAL_assert (assumptions.empty ());
for (const auto &c : clauses) {
if (terminated_asynchronously (100))
return unlucky (-1);
if (c->garbage)
continue;
if (c->redundant)
continue;
bool satisfied = false, found_negative_literal = false;
for (const auto &lit : *c) {
const signed char tmp = val (lit);
if (tmp > 0) {
satisfied = true;
break;
}
if (tmp < 0)
continue;
if (lit > 0)
continue;
found_negative_literal = true;
break;
}
if (satisfied || found_negative_literal)
continue;
LOG (c, "found purely positively");
return unlucky (0);
}
VERBOSE (1, "all clauses contain a negative literal");
for (auto idx : vars) {
if (terminated_asynchronously (10))
return unlucky (-1);
if (val (idx))
continue;
search_assume_decision (-idx);
if (propagate ())
continue;
CADICAL_assert (level > 0);
LOG ("propagation failed including redundant clauses");
return unlucky (0);
}
stats.lucky.constant.zero++;
return 10;
}
int Internal::trivially_true_satisfiable () {
LOG ("checking that all clauses contain a positive literal");
CADICAL_assert (!level);
CADICAL_assert (assumptions.empty ());
for (const auto &c : clauses) {
if (terminated_asynchronously (100))
return unlucky (-1);
if (c->garbage)
continue;
if (c->redundant)
continue;
bool satisfied = false, found_positive_literal = false;
for (const auto &lit : *c) {
const signed char tmp = val (lit);
if (tmp > 0) {
satisfied = true;
break;
}
if (tmp < 0)
continue;
if (lit < 0)
continue;
found_positive_literal = true;
break;
}
if (satisfied || found_positive_literal)
continue;
LOG (c, "found purely negatively");
return unlucky (0);
}
VERBOSE (1, "all clauses contain a positive literal");
for (auto idx : vars) {
if (terminated_asynchronously (10))
return unlucky (-1);
if (val (idx))
continue;
search_assume_decision (idx);
if (propagate ())
continue;
CADICAL_assert (level > 0);
LOG ("propagation failed including redundant clauses");
return unlucky (0);
}
stats.lucky.constant.one++;
return 10;
}
/*------------------------------------------------------------------------*/
inline bool Internal::lucky_propagate_discrepency (int dec) {
search_assume_decision (dec);
bool no_conflict = propagate ();
if (no_conflict)
return false;
if (level > 1) {
backtrack (level - 1);
search_assume_decision (-dec);
no_conflict = propagate ();
if (no_conflict)
return false;
return true;
} else {
analyze ();
CADICAL_assert (!level);
no_conflict = propagate ();
if (!no_conflict) {
analyze ();
LOG ("lucky inconsistency backward assigning to true");
return true;
}
}
return false;
}
int Internal::forward_false_satisfiable () {
LOG ("checking increasing variable index false assignment");
CADICAL_assert (!unsat);
CADICAL_assert (!level);
CADICAL_assert (assumptions.empty ());
for (auto idx : vars) {
START:
if (terminated_asynchronously (100))
return unlucky (-1);
if (val (idx))
continue;
if (lucky_propagate_discrepency (-idx)) {
if (unsat)
return 20;
else
return unlucky (0);
} else
goto START;
}
VERBOSE (1, "forward assuming variables false satisfies formula");
CADICAL_assert (satisfied ());
stats.lucky.forward.zero++;
return 10;
}
int Internal::forward_true_satisfiable () {
LOG ("checking increasing variable index true assignment");
CADICAL_assert (!unsat);
CADICAL_assert (!level);
CADICAL_assert (assumptions.empty ());
for (auto idx : vars) {
START:
if (terminated_asynchronously (10))
return unlucky (-1);
if (val (idx))
continue;
if (lucky_propagate_discrepency (idx)) {
if (unsat)
return 20;
else
return unlucky (0);
} else
goto START;
}
VERBOSE (1, "forward assuming variables true satisfies formula");
CADICAL_assert (satisfied ());
stats.lucky.forward.one++;
return 10;
}
/*------------------------------------------------------------------------*/
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--) {
START:
if (terminated_asynchronously (10))
return unlucky (-1);
if (val (idx))
continue;
if (lucky_propagate_discrepency (-idx)) {
if (unsat)
return 20;
else
return unlucky (0);
} else
goto START;
}
VERBOSE (1, "backward assuming variables false satisfies formula");
CADICAL_assert (satisfied ());
stats.lucky.backward.zero++;
return 10;
}
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--) {
START:
if (terminated_asynchronously (10))
return unlucky (-1);
if (val (idx))
continue;
if (lucky_propagate_discrepency (idx)) {
if (unsat)
return 20;
else
return unlucky (0);
} else
goto START;
}
VERBOSE (1, "backward assuming variables true satisfies formula");
CADICAL_assert (satisfied ());
stats.lucky.backward.one++;
return 10;
}
/*------------------------------------------------------------------------*/
// The following two functions test if the formula is a satisfiable horn
// formula. Actually the test is slightly more general. It goes over all
// clauses and assigns the first positive literal to true and propagates.
// Already satisfied clauses are of course skipped. A reverse function
// is not implemented yet.
int Internal::positive_horn_satisfiable () {
LOG ("checking that all clauses are positive horn satisfiable");
CADICAL_assert (!level);
CADICAL_assert (assumptions.empty ());
for (const auto &c : clauses) {
if (terminated_asynchronously (10))
return unlucky (-1);
if (c->garbage)
continue;
if (c->redundant)
continue;
int positive_literal = 0;
bool satisfied = false;
for (const auto &lit : *c) {
const signed char tmp = val (lit);
if (tmp > 0) {
satisfied = true;
break;
}
if (tmp < 0)
continue;
if (lit < 0)
continue;
positive_literal = lit;
break;
}
if (satisfied)
continue;
if (!positive_literal) {
LOG (c, "no positive unassigned literal in");
return unlucky (0);
}
CADICAL_assert (positive_literal > 0);
LOG (c, "found positive literal %d in", positive_literal);
search_assume_decision (positive_literal);
if (propagate ())
continue;
LOG ("propagation of positive literal %d leads to conflict",
positive_literal);
return unlucky (0);
}
for (auto idx : vars) {
if (terminated_asynchronously (10))
return unlucky (-1);
if (val (idx))
continue;
search_assume_decision (-idx);
if (propagate ())
continue;
LOG ("propagation of remaining literal %d leads to conflict", -idx);
return unlucky (0);
}
VERBOSE (1, "clauses are positive horn satisfied");
CADICAL_assert (!conflict);
CADICAL_assert (satisfied ());
stats.lucky.horn.positive++;
return 10;
}
int Internal::negative_horn_satisfiable () {
LOG ("checking that all clauses are negative horn satisfiable");
CADICAL_assert (!level);
CADICAL_assert (assumptions.empty ());
for (const auto &c : clauses) {
if (terminated_asynchronously (10))
return unlucky (-1);
if (c->garbage)
continue;
if (c->redundant)
continue;
int negative_literal = 0;
bool satisfied = false;
for (const auto &lit : *c) {
const signed char tmp = val (lit);
if (tmp > 0) {
satisfied = true;
break;
}
if (tmp < 0)
continue;
if (lit > 0)
continue;
negative_literal = lit;
break;
}
if (satisfied)
continue;
if (!negative_literal) {
if (level > 0)
backtrack ();
LOG (c, "no negative unassigned literal in");
return unlucky (0);
}
CADICAL_assert (negative_literal < 0);
LOG (c, "found negative literal %d in", negative_literal);
search_assume_decision (negative_literal);
if (propagate ())
continue;
LOG ("propagation of negative literal %d leads to conflict",
negative_literal);
return unlucky (0);
}
for (auto idx : vars) {
if (terminated_asynchronously (10))
return unlucky (-1);
if (val (idx))
continue;
search_assume_decision (idx);
if (propagate ())
continue;
LOG ("propagation of remaining literal %d leads to conflict", idx);
return unlucky (0);
}
VERBOSE (1, "clauses are negative horn satisfied");
CADICAL_assert (!conflict);
CADICAL_assert (satisfied ());
stats.lucky.horn.negative++;
return 10;
}
/*------------------------------------------------------------------------*/
int Internal::lucky_phases () {
CADICAL_assert (!level);
require_mode (SEARCH);
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)
return 0;
START (search);
START (lucky);
CADICAL_assert (!searching_lucky_phases);
searching_lucky_phases = true;
stats.lucky.tried++;
const int64_t active_before = stats.active;
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 = backward_false_satisfiable ();
if (!res)
res = backward_true_satisfiable ();
if (!res)
res = positive_horn_satisfiable ();
if (!res)
res = negative_horn_satisfiable ();
if (res < 0)
CADICAL_assert (termination_forced), res = 0;
if (res == 10)
stats.lucky.succeeded++;
report ('l', !res);
CADICAL_assert (searching_lucky_phases);
const int64_t units = active_before - stats.active;
if (!res && units)
LOG ("lucky %zd units", units);
searching_lucky_phases = false;
STOP (lucky);
STOP (search);
return res;
}
} // namespace CaDiCaL
ABC_NAMESPACE_IMPL_END

View File

@ -1,218 +0,0 @@
#include "global.h"
#include "internal.hpp"
ABC_NAMESPACE_IMPL_START
namespace CaDiCaL {
/*------------------------------------------------------------------------*/
#ifndef CADICAL_QUIET
/*------------------------------------------------------------------------*/
void Internal::print_prefix () { fputs (prefix.c_str (), stdout); }
void Internal::vmessage (const char *fmt, va_list &ap) {
#ifdef LOGGING
if (!opts.log)
#endif
if (opts.quiet)
return;
print_prefix ();
vprintf (fmt, ap);
fputc ('\n', stdout);
fflush (stdout);
}
void Internal::message (const char *fmt, ...) {
va_list ap;
va_start (ap, fmt);
vmessage (fmt, ap);
va_end (ap);
}
void Internal::message () {
#ifdef LOGGING
if (!opts.log)
#endif
if (opts.quiet)
return;
print_prefix ();
fputc ('\n', stdout);
fflush (stdout);
}
/*------------------------------------------------------------------------*/
void Internal::vverbose (int level, const char *fmt, va_list &ap) {
#ifdef LOGGING
if (!opts.log)
#endif
if (opts.quiet || level > opts.verbose)
return;
print_prefix ();
vprintf (fmt, ap);
fputc ('\n', stdout);
fflush (stdout);
}
void Internal::verbose (int level, const char *fmt, ...) {
va_list ap;
va_start (ap, fmt);
vverbose (level, fmt, ap);
va_end (ap);
}
void Internal::verbose (int level) {
#ifdef LOGGING
if (!opts.log)
#endif
if (opts.quiet || level > opts.verbose)
return;
print_prefix ();
fputc ('\n', stdout);
fflush (stdout);
}
/*------------------------------------------------------------------------*/
void Internal::section (const char *title) {
#ifdef LOGGING
if (!opts.log)
#endif
if (opts.quiet)
return;
if (stats.sections++)
MSG ();
print_prefix ();
tout.blue ();
fputs ("--- [ ", stdout);
tout.blue (true);
fputs (title, stdout);
tout.blue ();
fputs (" ] ", stdout);
for (int i = strlen (title) + strlen (prefix.c_str ()) + 9; i < 78; i++)
fputc ('-', stdout);
tout.normal ();
fputc ('\n', stdout);
MSG ();
}
/*------------------------------------------------------------------------*/
void Internal::phase (const char *phase, const char *fmt, ...) {
#ifdef LOGGING
if (!opts.log)
#endif
if (opts.quiet || (!force_phase_messages && opts.verbose < 2))
return;
print_prefix ();
printf ("[%s] ", phase);
va_list ap;
va_start (ap, fmt);
vprintf (fmt, ap);
va_end (ap);
fputc ('\n', stdout);
fflush (stdout);
}
void Internal::phase (const char *phase, int64_t count, const char *fmt,
...) {
#ifdef LOGGING
if (!opts.log)
#endif
if (opts.quiet || (!force_phase_messages && opts.verbose < 2))
return;
print_prefix ();
printf ("[%s-%" PRId64 "] ", phase, count);
va_list ap;
va_start (ap, fmt);
vprintf (fmt, ap);
va_end (ap);
fputc ('\n', stdout);
fflush (stdout);
}
/*------------------------------------------------------------------------*/
#endif // ifndef CADICAL_QUIET
/*------------------------------------------------------------------------*/
void Internal::warning (const char *fmt, ...) {
fflush (stdout);
terr.bold ();
fputs ("cadical: ", stderr);
terr.red (1);
fputs ("warning:", stderr);
terr.normal ();
fputc (' ', stderr);
va_list ap;
va_start (ap, fmt);
vfprintf (stderr, fmt, ap);
va_end (ap);
fputc ('\n', stderr);
fflush (stderr);
}
/*------------------------------------------------------------------------*/
void Internal::error_message_start () {
fflush (stdout);
terr.bold ();
fputs ("cadical: ", stderr);
terr.red (1);
fputs ("error:", stderr);
terr.normal ();
fputc (' ', stderr);
}
void Internal::error_message_end () {
fputc ('\n', stderr);
fflush (stderr);
// TODO add possibility to use call back instead.
exit (1);
}
void Internal::verror (const char *fmt, va_list &ap) {
error_message_start ();
vfprintf (stderr, fmt, ap);
error_message_end ();
}
void Internal::error (const char *fmt, ...) {
va_list ap;
va_start (ap, fmt);
verror (fmt, ap);
va_end (ap); // unreachable
}
/*------------------------------------------------------------------------*/
void fatal_message_start () {
fflush (stdout);
terr.bold ();
fputs ("cadical: ", stderr);
terr.red (1);
fputs ("fatal error:", stderr);
terr.normal ();
fputc (' ', stderr);
}
void fatal_message_end () {
fputc ('\n', stderr);
fflush (stderr);
abort ();
}
void fatal (const char *fmt, ...) {
fatal_message_start ();
va_list ap;
va_start (ap, fmt);
vfprintf (stderr, fmt, ap);
va_end (ap);
fatal_message_end ();
abort ();
}
} // namespace CaDiCaL
ABC_NAMESPACE_IMPL_END

View File

@ -1,230 +0,0 @@
#include "global.h"
#include "internal.hpp"
ABC_NAMESPACE_IMPL_START
namespace CaDiCaL {
// Functions for learned clause minimization. We only have the recursive
// version, which actually really is implemented recursively. We also
// played with a derecursified version, which however was more complex and
// slower. The trick to keep potential stack exhausting recursion under
// guards is to explicitly limit the recursion depth.
// Instead of signatures as in the original implementation in MiniSAT and
// our corresponding paper, we use the 'poison' idea of Allen Van Gelder to
// mark unsuccessful removal attempts, then Donald Knuth's idea to abort
// minimization if only one literal was seen on the level and a new idea of
// also aborting if the earliest seen literal was assigned afterwards.
bool Internal::minimize_literal (int lit, int depth) {
LOG ("attempt to minimize lit %d at depth %d", lit, depth);
CADICAL_assert (val (lit) > 0);
Flags &f = flags (lit);
Var &v = var (lit);
if (!v.level || f.removable || f.keep)
return true;
if (!v.reason || f.poison || v.level == level)
return false;
const Level &l = control[v.level];
if (!depth && l.seen.count < 2)
return false; // Don Knuth's idea
if (v.trail <= l.seen.trail)
return false; // new early abort
if (depth > opts.minimizedepth)
return false;
bool res = true;
CADICAL_assert (v.reason);
if (opts.minimizeticks)
stats.ticks.search[stable]++;
if (v.reason == external_reason) {
CADICAL_assert (!opts.exteagerreasons);
v.reason = learn_external_reason_clause (lit, 0, true);
if (!v.reason) {
CADICAL_assert (!v.level);
return true;
}
}
CADICAL_assert (v.reason != external_reason);
const const_literal_iterator end = v.reason->end ();
const_literal_iterator i;
for (i = v.reason->begin (); res && i != end; i++) {
const int other = *i;
if (other == lit)
continue;
res = minimize_literal (-other, depth + 1);
}
if (res)
f.removable = true;
else
f.poison = true;
minimized.push_back (lit);
if (!depth) {
LOG ("minimizing %d %s", lit, res ? "succeeded" : "failed");
}
return res;
}
// Sorting the clause before minimization with respect to the trail order
// (literals with smaller trail height first) is necessary but natural and
// might help to minimize the required recursion depth too.
struct minimize_trail_positive_rank {
Internal *internal;
minimize_trail_positive_rank (Internal *s) : internal (s) {}
typedef unsigned Type;
Type operator() (const int &a) const {
CADICAL_assert (internal->val (a));
return (unsigned) internal->var (a).trail;
}
};
struct minimize_trail_smaller {
Internal *internal;
minimize_trail_smaller (Internal *s) : internal (s) {}
bool operator() (const int &a, const int &b) const {
return internal->var (a).trail < internal->var (b).trail;
}
};
struct minimize_trail_level_positive_rank {
Internal *internal;
minimize_trail_level_positive_rank (Internal *s) : internal (s) {}
typedef uint64_t Type;
Type operator() (const int &a) const {
CADICAL_assert (internal->val (a));
Var &v = internal->var (a);
uint64_t res = v.level;
res <<= 32;
res |= v.trail;
return res;
}
};
struct minimize_trail_level_smaller {
Internal *internal;
minimize_trail_level_smaller (Internal *s) : internal (s) {}
bool operator() (const int &a, const int &b) const {
return minimize_trail_level_positive_rank (internal) (a) <
minimize_trail_level_positive_rank (internal) (b);
}
};
void Internal::minimize_clause () {
START (minimize);
LOG (clause, "minimizing first UIP clause");
external->check_learned_clause (); // check 1st UIP learned clause first
minimize_sort_clause ();
CADICAL_assert (minimized.empty ());
CADICAL_assert (minimize_chain.empty ());
const auto end = clause.end ();
auto j = clause.begin (), i = j;
std::vector<int> stack;
for (; i != end; i++) {
if (minimize_literal (-*i)) {
if (lrat) {
CADICAL_assert (mini_chain.empty ());
calculate_minimize_chain (-*i, stack);
for (auto p : mini_chain) {
minimize_chain.push_back (p);
}
mini_chain.clear ();
}
stats.minimized++;
} else
flags (*j++ = *i).keep = true;
}
LOG ("minimized %zd literals", (size_t) (clause.end () - j));
if (j != end)
clause.resize (j - clause.begin ());
clear_minimized_literals ();
for (auto p = minimize_chain.rbegin (); p != minimize_chain.rend ();
p++) {
lrat_chain.push_back (*p);
}
minimize_chain.clear ();
STOP (minimize);
}
// go backwards in reason graph and add ids
// mini_chain is in correct order so we have to add it to minimize_chain
// and then reverse when we put it on lrat_chain
//
// We have to use the non-recursive as we cannot limit the depth like the
// minimize version. Unlike the minimize version, we have to keep literals
// on the stack in order to push its reason later.
void Internal::calculate_minimize_chain (int lit, std::vector<int> &stack) {
CADICAL_assert (stack.empty ());
stack.push_back (vidx (lit));
while (!stack.empty ()) {
const int idx = stack.back ();
CADICAL_assert (idx);
stack.pop_back ();
if (idx < 0) {
Var &v = var (idx);
mini_chain.push_back (v.reason->id);
continue;
}
CADICAL_assert (idx);
Flags &f = flags (idx);
Var &v = var (idx);
if (f.keep || f.added || f.poison) {
continue;
}
if (!v.level) {
if (f.seen)
continue;
f.seen = true;
unit_analyzed.push_back (idx);
const int lit = val (idx) > 0 ? idx : -idx;
int64_t id = unit_id (lit);
unit_chain.push_back (id);
continue;
}
f.added = true;
CADICAL_assert (v.reason && f.removable);
const const_literal_iterator end = v.reason->end ();
const_literal_iterator i;
LOG (v.reason, "LRAT chain for lit %d at depth %zd by going over", lit,
stack.size ());
stack.push_back (-idx);
for (i = v.reason->begin (); i != end; i++) {
const int other = *i;
if (other == idx)
continue;
stack.push_back (vidx (other));
}
}
CADICAL_assert (stack.empty ());
}
// Sort the literals in reverse assignment order (thus trail order) to
// establish the base case of the recursive minimization algorithm in the
// positive case (where a literal with 'keep' true is hit).
//
void Internal::minimize_sort_clause () {
MSORT (opts.radixsortlim, clause.begin (), clause.end (),
minimize_trail_positive_rank (this),
minimize_trail_smaller (this));
}
void Internal::clear_minimized_literals () {
LOG ("clearing %zd minimized literals", minimized.size ());
for (const auto &lit : minimized) {
Flags &f = flags (lit);
f.poison = f.removable = f.shrinkable = f.added = false;
}
for (const auto &lit : clause)
CADICAL_assert (!flags (lit).shrinkable), flags (lit).keep =
flags (lit).shrinkable =
flags (lit).added = false;
minimized.clear ();
}
} // namespace CaDiCaL
ABC_NAMESPACE_IMPL_END

View File

@ -1,58 +0,0 @@
#include "global.h"
#include "internal.hpp"
ABC_NAMESPACE_IMPL_START
namespace CaDiCaL {
/*------------------------------------------------------------------------*/
// Occurrence lists.
void Internal::init_occs () {
if (otab.size () < 2 * vsize)
otab.resize (2 * vsize, Occs ());
LOG ("initialized occurrence lists");
}
void Internal::reset_occs () {
CADICAL_assert (occurring ());
erase_vector (otab);
LOG ("reset occurrence lists");
}
void Internal::clear_occs () {
CADICAL_assert (occurring ());
for (auto &occ : otab)
occ.clear ();
LOG ("clear occurrence lists");
}
/*------------------------------------------------------------------------*/
// One-sided occurrence counter (each literal has its own counter).
void Internal::init_noccs () {
CADICAL_assert (ntab.empty ());
if (ntab.size () < 2 * vsize)
ntab.resize (2 * vsize, 0);
LOG ("initialized two-sided occurrence counters");
}
void Internal::clear_noccs () {
CADICAL_assert (!ntab.empty ());
for (auto &nt : ntab)
nt = 0;
LOG ("clear two-sided occurrence counters");
}
void Internal::reset_noccs () {
CADICAL_assert (!max_var || !ntab.empty ());
erase_vector (ntab);
LOG ("reset two-sided occurrence counters");
}
} // namespace CaDiCaL
ABC_NAMESPACE_IMPL_END

View File

@ -1,365 +0,0 @@
#include "global.h"
#include "internal.hpp"
ABC_NAMESPACE_IMPL_START
namespace CaDiCaL {
/*------------------------------------------------------------------------*/
// By default, e.g., for library usage, the 'opts.report' value is zero
// ('false') but can be set to '1' by the stand alone solver. Using here
// a static default value avoids that the stand alone solver reports that
// '--report=1' is different from the default in 'print ()' below.
//
int Options::reportdefault;
/*------------------------------------------------------------------------*/
// The order of initializations of static objects is undefined and thus we
// can not assume that this table is already initialized if a solver and
// thus the constructor of 'Options' is called. Therefore we just have to
// reinitialize this table in every call to 'Options::Options'. This does
// not produce a data race even for parallel initialization since the
// same values are written by all threads under the assumption that the
// 'reportdefault' is set before any solver is initialized. We do have to
// perform this static initialization though, since 'has' is static and does
// not require that the 'Options' constructor was called.
Option Options::table[] = {
#define OPTION(N, V, L, H, O, P, R, D) \
{#N, (int) V, (int) L, (int) H, (int) O, (bool) P, D},
OPTIONS
#undef OPTION
};
/*------------------------------------------------------------------------*/
// Binary search in 'table', which requires option names to be sorted, which
// in turned is checked at start-up in 'Options::Options'.
Option *Options::has (const char *name) {
size_t l = 0, r = number_of_options;
while (l < r) {
size_t m = l + (r - l) / 2;
Option *res = &table[m];
int tmp = strcmp (name, res->name);
if (!tmp)
return res;
if (tmp < 0)
r = m;
if (tmp > 0)
l = m + 1;
}
return 0;
}
/*------------------------------------------------------------------------*/
bool Options::parse_long_option (const char *arg, string &name, int &val) {
if (arg[0] != '-' || arg[1] != '-')
return false;
const bool has_no_prefix =
(arg[2] == 'n' && arg[3] == 'o' && arg[4] == '-');
const size_t offset = has_no_prefix ? 5 : 2;
name = arg + offset;
const size_t pos = name.find_first_of ('=');
if (pos != string::npos)
name[pos] = 0;
if (!Options::has (name.c_str ()))
return false;
if (pos == string::npos)
val = !has_no_prefix;
else {
const char *val_str = name.c_str () + pos + 1;
if (!parse_int_str (val_str, val))
return false;
}
return true;
}
/*------------------------------------------------------------------------*/
void Options::initialize_from_environment (int &val, const char *name,
const int L, const int H) {
char key[80], *q;
const char *p;
CADICAL_assert (strlen (name) + strlen ("CADICAL_") + 1 < sizeof (key));
for (p = "CADICAL_", q = key; *p; p++)
*q++ = *p;
for (p = name; *p; p++)
*q++ = toupper (*p);
CADICAL_assert (q < key + sizeof (key));
*q = 0;
const char *val_str = getenv (key);
if (!val_str)
return;
if (!parse_int_str (val_str, val))
return;
if (val < L)
val = L;
if (val > H)
val = H;
}
// Initialize all the options to their default value 'V'.
Options::Options (Internal *s) : internal (s) {
CADICAL_assert (number_of_options == sizeof Options::table / sizeof (Option));
// First initialize them according to defaults in 'options.hpp'.
//
const char *prev = "";
size_t i = 0;
#define OPTION(N, V, L, H, O, P, R, D) \
do { \
if ((L) > (V)) \
FATAL ("'" #N "' default '" #V "' " \
"lower minimum '" #L "' in 'options.hpp'"); \
if ((H) < (V)) \
FATAL ("'" #N "' default '" #V "' " \
"larger maximum '" #H "' in 'options.hpp'"); \
if (strcmp (prev, #N) > 0) \
FATAL ("'%s' ordered before '" #N "' in 'options.hpp'", prev); \
N = (int) (V); \
CADICAL_assert (&val (i) == &N); \
/* The order of initializing static data is undefined and thus */ \
/* it might be the case that the 'table' is not initialized yet. */ \
/* Thus this construction just reinitializes the table too even */ \
/* though it might not be necessary. */ \
CADICAL_assert (!table[i].name || !strcmp (table[i].name, #N)); \
table[i] = {#N, (int) (V), (int) (L), (int) (H), \
(int) (O), (bool) (P), D}; \
prev = #N; \
i++; \
} while (0);
OPTIONS
#undef OPTION
// Check consistency in debugging mode.
//
#ifndef CADICAL_NDEBUG
CADICAL_assert (i == number_of_options);
CADICAL_assert (!has ("aaaaa"));
CADICAL_assert (!has ("non-existing-option"));
CADICAL_assert (!has ("zzzzz"));
#endif
// Now overwrite default options with environment values.
//
#define OPTION(N, V, L, H, O, P, R, D) \
initialize_from_environment (N, #N, L, H);
OPTIONS
#undef OPTION
}
/*------------------------------------------------------------------------*/
void Options::set (Option *o, int new_val) {
CADICAL_assert (o);
int &val = o->val (this), old_val = val;
if (old_val == new_val) {
LOG ("keeping value '%d' of option '%s'", old_val, o->name);
return;
}
if (new_val < o->lo) {
LOG ("bounding '%d' to lower limit '%d' for option '%s'", new_val,
o->lo, o->name);
new_val = o->lo;
}
if (new_val > o->hi) {
LOG ("bounding '%d' to upper limit '%d' for option '%s'", new_val,
o->hi, o->name);
new_val = o->hi;
}
val = new_val;
LOG ("set option 'set (\"%s\", %d)' from '%d'", o->name, new_val,
old_val);
}
// Explicit option value setting.
bool Options::set (const char *name, int val) {
Option *o = has (name);
if (!o)
return false;
set (o, val);
return true;
}
int Options::get (const char *name) {
Option *o = has (name);
return o ? o->val (this) : 0;
}
/*------------------------------------------------------------------------*/
void Options::print () {
unsigned different = 0;
#ifdef CADICAL_QUIET
const bool verbose = false;
#endif
char buffer[256];
// We prefer the macro iteration here since '[VLH]' might be '1e9' etc.
#define OPTION(N, V, L, H, O, P, R, D) \
if (N != (V)) \
different++; \
if (verbose || N != (V)) { \
if ((L) == 0 && (H) == 1) { \
snprintf (buffer, sizeof buffer, "--" #N "=%s", \
(N ? "true" : "false")); \
MSG (" %s%-30s%s (%s default %s'%s'%s)", \
((N == (V)) ? "" : tout.bright_yellow_code ()), buffer, \
((N == (V)) ? "" : tout.normal_code ()), \
((N == (V)) ? "same as" : "different from"), \
((N == (V)) ? tout.green_code () : tout.yellow_code ()), \
(bool) (V) ? "true" : "false", tout.normal_code ()); \
} else { \
snprintf (buffer, sizeof buffer, "--" #N "=%d", N); \
MSG (" %s%-30s%s (%s default %s'" #V "'%s)", \
((N == (V)) ? "" : tout.bright_yellow_code ()), buffer, \
((N == (V)) ? "" : tout.normal_code ()), \
((N == (V)) ? "same as" : "different from"), \
((N == (V)) ? tout.green_code () : tout.yellow_code ()), \
tout.normal_code ()); \
} \
}
OPTIONS
#undef OPTION
if (!different)
MSG ("all options are set to their default value");
}
/*------------------------------------------------------------------------*/
void Options::usage () {
// We prefer the macro iteration here since '[VLH]' might be '1e9' etc.
#define OPTION(N, V, L, H, O, P, R, D) \
if ((L) == 0 && (H) == 1) \
printf (" %-26s " D " [%s]\n", "--" #N "=bool", \
(bool) (V) ? "true" : "false"); \
else \
printf (" %-26s " D " [" #V "]\n", "--" #N "=" #L ".." #H);
OPTIONS
#undef OPTION
}
/*------------------------------------------------------------------------*/
void Options::optimize (int val) {
if (val < 0) {
LOG ("ignoring negative optimization mode '%d'", val);
return;
}
const int max_val = 31;
if (val > max_val) {
LOG ("optimization argument '%d' reduced to '%d'", val, max_val);
val = max_val;
}
int64_t factor2 = 1;
for (int i = 0; i < val && factor2 <= INT_MAX; i++)
factor2 *= 2;
int64_t factor10 = 1;
for (int i = 0; i < val && factor10 <= INT_MAX; i++)
factor10 *= 10;
unsigned increased = 0;
#define OPTION(N, V, L, H, O, P, R, D) \
do { \
if (!(O)) \
break; \
const int64_t factor1 = ((O) == 1 ? factor2 : factor10); \
int64_t new_val = factor1 * (int64_t) (V); \
if (new_val > (H)) \
new_val = (H); \
if (new_val == (int) (V)) \
break; \
LOG ("optimization mode '%d' for '%s' " \
"gives '%" PRId64 "' instead of '%d", \
val, #N, new_val, (int) (V)); \
CADICAL_assert (new_val <= INT_MAX); \
N = (int) new_val; \
increased++; \
} while (0);
OPTIONS
#undef OPTION
if (increased)
MSG ("optimization mode '-O%d' increased %u limits", val, increased);
}
/*------------------------------------------------------------------------*/
void Options::disable_preprocessing () {
size_t count = 0;
#define OPTION(N, V, L, H, O, P, R, D) \
do { \
if (!(P)) \
break; \
if (!(N)) \
break; \
LOG ("plain mode disables '%s'", #N); \
CADICAL_assert ((L) == 0); \
CADICAL_assert ((H) == 1); \
count++; \
N = 0; \
} while (0);
OPTIONS
#undef OPTION
LOG ("forced plain mode disabled %zd preprocessing options", count);
#ifndef LOGGING
(void) count;
#endif
}
bool Options::is_preprocessing_option (const char *name) {
Option *o = has (name);
return o ? o->preprocessing : false;
}
/*------------------------------------------------------------------------*/
void Options::reset_default_values () {
size_t count = 0;
#define OPTION(N, V, L, H, O, P, R, D) \
do { \
if (!(R)) \
break; \
if (N == (V)) \
break; \
LOG ("resetting option '%s' to default %s", #N, #V); \
count++; \
N = (int) (V); \
} while (0);
OPTIONS
#undef OPTION
LOG ("reset %zd options to their default values", count);
#ifndef LOGGING
(void) count;
#endif
}
/*------------------------------------------------------------------------*/
void Options::copy (Options &other) const {
#ifdef LOGGING
Internal *internal = other.internal;
#endif
#define OPTION(N, V, L, H, O, P, R, D) \
if ((N) == (int) (V)) \
LOG ("keeping non default option '--%s=%s'", #N, #V); \
else if ((N) != (int) (V)) { \
LOG ("overwriting default option by '--%s=%d'", #N, N); \
other.N = N; \
}
OPTIONS
#undef OPTION
}
} // namespace CaDiCaL
ABC_NAMESPACE_IMPL_END

View File

@ -1,442 +0,0 @@
#include "global.h"
#include "internal.hpp"
ABC_NAMESPACE_IMPL_START
/*------------------------------------------------------------------------*/
namespace CaDiCaL {
/*------------------------------------------------------------------------*/
// Parse error.
#define PER(...) \
do { \
internal->error_message.init ( \
"%s:%" PRIu64 ": parse error: ", file->name (), \
(uint64_t) file->lineno ()); \
return internal->error_message.append (__VA_ARGS__); \
} while (0)
/*------------------------------------------------------------------------*/
// Parsing utilities.
inline int Parser::parse_char () { return file->get (); }
// Return an non zero error string if a parse error occurred.
inline const char *Parser::parse_string (const char *str, char prev) {
for (const char *p = str; *p; p++)
if (parse_char () == *p)
prev = *p;
else if (*p == ' ')
PER ("expected space after '%c'", prev);
else
PER ("expected '%c' after '%c'", *p, prev);
return 0;
}
inline const char *Parser::parse_positive_int (int &ch, int &res,
const char *name) {
CADICAL_assert (isdigit (ch));
res = ch - '0';
while (isdigit (ch = parse_char ())) {
int digit = ch - '0';
if (INT_MAX / 10 < res || INT_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,
int strict) {
if (ch == 'a')
return cube_token;
int sign = 0;
if (ch == '-') {
if (!isdigit (ch = parse_char ()))
PER ("expected digit after '-'");
sign = -1;
} else if (!isdigit (ch))
PER ("expected digit or '-'");
else
sign = 1;
lit = ch - '0';
while (isdigit (ch = parse_char ())) {
int digit = ch - '0';
if (INT_MAX / 10 < lit || INT_MAX - digit < 10 * lit)
PER ("literal too large");
lit = 10 * lit + digit;
}
if (ch == '\r')
ch = parse_char ();
if (ch != 'c' && ch != ' ' && ch != '\t' && ch != '\n' && ch != EOF)
PER ("expected white space after '%d'", sign * lit);
if (lit > vars) {
if (strict != FORCED)
PER ("literal %d exceeds maximum variable %d", sign * lit, vars);
else
vars = lit;
}
lit *= sign;
return 0;
}
/*------------------------------------------------------------------------*/
// Parsing CNF in DIMACS format.
const char *Parser::parse_dimacs_non_profiled (int &vars, int strict) {
#ifndef CADICAL_QUIET
double start = internal->time ();
#endif
bool found_inccnf_header = false;
int ch, clauses = 0;
vars = 0;
// First read comments before header with possibly embedded options.
//
for (;;) {
ch = parse_char ();
if (strict != STRICT)
if (ch == ' ' || ch == '\n' || ch == '\t' || ch == '\r')
continue;
if (ch != 'c')
break;
string buf;
while ((ch = parse_char ()) != '\n')
if (ch == EOF)
PER ("unexpected end-of-file in header comment");
else if (ch != '\r')
buf.push_back (ch);
const char *o;
for (o = buf.c_str (); *o && *o != '-'; o++)
;
if (!*o)
continue;
PHASE ("parse-dimacs", "found option '%s'", o);
if (*o)
solver->set_long_option (o);
}
if (ch != 'p')
PER ("expected 'c' or 'p'");
ch = parse_char ();
if (strict == STRICT) {
if (ch != ' ')
PER ("expected space after 'p'");
ch = parse_char ();
} else if (ch != ' ' && ch != '\t')
PER ("expected white space after 'p'");
else {
do
ch = parse_char ();
while (ch == ' ' || ch == '\t');
}
// Now read 'p cnf <var> <clauses>' header of DIMACS file
// or 'p inccnf' of incremental 'INCCNF' file.
//
if (ch == 'c') {
CADICAL_assert (!found_inccnf_header);
if (strict == STRICT) {
const char *err = parse_string ("nf ", 'c');
if (err)
return err;
ch = parse_char ();
if (!isdigit (ch))
PER ("expected digit after 'p cnf '");
err = parse_positive_int (ch, vars, "<max-var>");
if (err)
return err;
if (ch != ' ')
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>");
if (err)
return err;
if (ch != '\n')
PER ("expected new-line after 'p cnf %d %d'", vars, clauses);
} else {
if (parse_char () != 'n')
PER ("expected 'n' after 'p c'");
if (parse_char () != 'f')
PER ("expected 'f' after 'p cn'");
ch = parse_char ();
if (!isspace (ch))
PER ("expected space after 'p cnf'");
do
ch = parse_char ();
while (isspace (ch));
if (!isdigit (ch))
PER ("expected digit after 'p cnf '");
const char *err = parse_positive_int (ch, vars, "<max-var>");
if (err)
return err;
if (!isspace (ch))
PER ("expected space after 'p cnf %d'", vars);
do
ch = parse_char ();
while (isspace (ch));
if (!isdigit (ch))
PER ("expected digit after 'p cnf %d '", vars);
err = parse_positive_int (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);
ch = parse_char ();
}
}
MSG ("found %s'p cnf %d %d'%s header", tout.green_code (), vars,
clauses, tout.normal_code ());
if (strict != FORCED)
solver->reserve (vars);
internal->reserve_ids (clauses);
} else if (!parse_inccnf_too)
PER ("expected 'c' after 'p '");
else if (ch == 'i') {
found_inccnf_header = true;
const char *err = parse_string ("nccnf", 'i');
if (err)
return err;
ch = parse_char ();
if (strict == STRICT) {
if (ch != '\n')
PER ("expected new-line after 'p inccnf'");
} else {
while (ch != '\n') {
if (ch != '\r' && !isspace (ch))
PER ("expected new-line after 'p inccnf'");
ch = parse_char ();
}
}
MSG ("found %s'p inccnf'%s header", tout.green_code (),
tout.normal_code ());
strict = FORCED;
} else
PER ("expected 'c' or 'i' after 'p '");
if (parse_inccnf_too)
*parse_inccnf_too = false;
// Now read body of DIMACS part.
//
int lit = 0, parsed = 0;
while ((ch = parse_char ()) != EOF) {
if (ch == ' ' || ch == '\n' || ch == '\t' || ch == '\r')
continue;
if (ch == 'c') {
while ((ch = parse_char ()) != '\n' && ch != EOF)
;
if (ch == EOF)
break;
continue;
}
if (ch == 'a' && found_inccnf_header)
break;
const char *err = parse_lit (ch, lit, vars, strict);
if (err)
return err;
if (ch == 'c') {
while ((ch = parse_char ()) != '\n')
if (ch == EOF)
PER ("unexpected end-of-file in comment");
}
solver->add (lit);
if (!found_inccnf_header && !lit && parsed++ >= clauses &&
strict != FORCED)
PER ("too many clauses");
}
if (lit)
PER ("last clause without terminating '0'");
if (!found_inccnf_header && parsed < clauses && strict != FORCED)
PER ("clause missing");
#ifndef CADICAL_QUIET
double end = internal->time ();
MSG ("parsed %d clauses in %.2f seconds %s time", parsed, end - start,
internal->opts.realtime ? "real" : "process");
#endif
#ifndef CADICAL_QUIET
start = end;
size_t num_cubes = 0;
#endif
if (ch == 'a') {
CADICAL_assert (parse_inccnf_too);
CADICAL_assert (found_inccnf_header);
if (!*parse_inccnf_too)
*parse_inccnf_too = true;
for (;;) {
ch = parse_char ();
if (ch == ' ' || ch == '\n' || ch == '\t' || ch == '\r')
continue;
if (ch == 'c') {
while ((ch = parse_char ()) != '\n' && ch != EOF)
;
if (ch == EOF)
break;
continue;
}
const char *err = parse_lit (ch, lit, vars, strict);
if (err == cube_token)
PER ("two 'a' in a row");
else if (err)
return err;
if (ch == 'c') {
while ((ch = parse_char ()) != '\n')
if (ch == EOF)
PER ("unexpected end-of-file in comment");
}
if (cubes)
cubes->push_back (lit);
if (!lit) {
#ifndef CADICAL_QUIET
num_cubes++;
#endif
for (;;) {
ch = parse_char ();
if (ch == ' ' || ch == '\n' || ch == '\t' || ch == '\r')
continue;
if (ch == 'c') {
while ((ch = parse_char ()) != '\n' && ch != EOF)
;
if (ch == EOF)
break;
}
if (ch == EOF)
break;
if (ch != 'a')
PER ("expected 'a' or end-of-file after zero");
lit = INT_MIN;
break;
}
if (ch == EOF)
break;
}
}
if (lit)
PER ("last cube without terminating '0'");
}
#ifndef CADICAL_QUIET
if (found_inccnf_header) {
double end = internal->time ();
MSG ("parsed %zd cubes in %.2f seconds %s time", num_cubes, end - start,
internal->opts.realtime ? "real" : "process");
}
#endif
return 0;
}
/*------------------------------------------------------------------------*/
// Parsing solution in competition output format.
const char *Parser::parse_solution_non_profiled () {
external->solution = new signed char[external->max_var + 1u];
external->solution_size = external->max_var;
clear_n (external->solution, external->max_var + 1u);
int ch;
for (;;) {
ch = parse_char ();
if (ch == EOF)
PER ("missing 's' line");
else if (ch == 'c') {
while ((ch = parse_char ()) != '\n')
if (ch == EOF)
PER ("unexpected end-of-file in comment");
} else if (ch == 's')
break;
else
PER ("expected 'c' or 's'");
}
const char *err = parse_string (" SATISFIABLE", 's');
if (err)
return err;
if ((ch = parse_char ()) == '\r')
ch = parse_char ();
if (ch != '\n')
PER ("expected new-line after 's SATISFIABLE'");
#ifndef CADICAL_QUIET
int count = 0;
#endif
for (;;) {
ch = parse_char ();
if (ch != 'v')
PER ("expected 'v' at start-of-line");
if ((ch = parse_char ()) != ' ')
PER ("expected ' ' after 'v'");
int lit = 0;
ch = parse_char ();
do {
if (ch == ' ' || ch == '\t') {
ch = parse_char ();
continue;
}
err = parse_lit (ch, lit, external->max_var, false);
if (err)
return err;
if (ch == 'c')
PER ("unexpected comment");
if (!lit)
break;
if (external->solution[abs (lit)])
PER ("variable %d occurs twice", abs (lit));
LOG ("solution %d", lit);
external->solution[abs (lit)] = sign (lit);
#ifndef CADICAL_QUIET
count++;
#endif
if (ch == '\r')
ch = parse_char ();
} while (ch != '\n');
if (!lit)
break;
}
MSG ("parsed %d values %.2f%%", count,
percent (count, external->max_var));
return 0;
}
/*------------------------------------------------------------------------*/
// Wrappers to profile parsing and at the same time use the convenient
// implicit 'return' in PER in the non-profiled versions.
const char *Parser::parse_dimacs (int &vars, int strict) {
CADICAL_assert (strict == FORCED || strict == RELAXED || strict == STRICT);
START (parse);
const char *err = parse_dimacs_non_profiled (vars, strict);
STOP (parse);
return err;
}
const char *Parser::parse_solution () {
START (parse);
const char *err = parse_solution_non_profiled ();
STOP (parse);
return err;
}
} // namespace CaDiCaL
ABC_NAMESPACE_IMPL_END

View File

@ -1,50 +0,0 @@
#include "global.h"
#include "internal.hpp"
ABC_NAMESPACE_IMPL_START
namespace CaDiCaL {
void Internal::copy_phases (vector<signed char> &dst) {
START (copy);
for (auto i : vars)
dst[i] = phases.saved[i];
STOP (copy);
}
void Internal::clear_phases (vector<signed char> &dst) {
START (copy);
for (auto i : vars)
dst[i] = 0;
STOP (copy);
}
void Internal::phase (int lit) {
const int idx = vidx (lit);
signed char old_forced_phase = phases.forced[idx];
signed char new_forced_phase = sign (lit);
if (old_forced_phase == new_forced_phase) {
LOG ("forced phase remains at %d", old_forced_phase * idx);
return;
}
if (old_forced_phase)
LOG ("overwriting old forced phase %d", old_forced_phase * idx);
LOG ("new forced phase %d", new_forced_phase * idx);
phases.forced[idx] = new_forced_phase;
}
void Internal::unphase (int lit) {
const int idx = vidx (lit);
signed char old_forced_phase = phases.forced[idx];
if (!old_forced_phase) {
LOG ("forced phase of %d already reset", lit);
return;
}
LOG ("clearing old forced phase %d", old_forced_phase * idx);
phases.forced[idx] = 0;
}
} // namespace CaDiCaL
ABC_NAMESPACE_IMPL_END

View File

@ -1,993 +0,0 @@
#include "global.h"
#include "internal.hpp"
ABC_NAMESPACE_IMPL_START
namespace CaDiCaL {
/*------------------------------------------------------------------------*/
// Failed literal probing uses its own propagation and assignment
// functions. It further provides on-the-fly generation of hyper binary
// resolvents but only probes on roots of the binary implication graph. The
// search for failed literals is limited, but untried roots are kept until
// the next time 'probe' is called. Left over probes from the last attempt
// and new probes are tried until the limit is hit or all are tried.
/*------------------------------------------------------------------------*/
bool Internal::inprobing () {
if (!opts.inprobing)
return false;
if (!preprocessing && !opts.inprocessing)
return false;
if (preprocessing)
CADICAL_assert (lim.preprocessing);
if (stats.inprobingphases && last.inprobe.reductions == stats.reductions)
return false;
return lim.inprobe <= stats.conflicts;
}
/*------------------------------------------------------------------------*/
inline int Internal::get_parent_reason_literal (int lit) {
const int idx = vidx (lit);
int res = parents[idx];
if (lit < 0)
res = -res;
return res;
}
inline void Internal::set_parent_reason_literal (int lit, int reason) {
const int idx = vidx (lit);
if (lit < 0)
reason = -reason;
parents[idx] = reason;
}
/*-----------------------------------------------------------------------*/
// for opts.probehbr=false we need to do a lot of extra work to remember the
// correct lrat_chains... This solution is also memory intensive I think
// all corresponding functions are guarded to only work with the right
// options so they can be called without checking for options
//
// call locally after failed_literal or backtracking
//
void Internal::clean_probehbr_lrat () {
if (!lrat || opts.probehbr)
return;
for (auto &field : probehbr_chains) {
for (auto &chain : field) {
chain.clear ();
}
}
}
// call globally before a probe round (or a lookahead round)
//
void Internal::init_probehbr_lrat () {
if (!lrat || opts.probehbr)
return;
const size_t size = 2 * (1 + (size_t) max_var);
probehbr_chains.resize (size);
for (size_t i = 0; i < size; i++) {
probehbr_chains[i].resize (size);
// commented because not needed... should be empty already
/*
for (size_t j = 0; j < size; j++) {
vector<int64_t> empty;
probehbr_chains[i][j] = empty;
}
*/
}
}
// sets lrat_chain to the stored chain in probehbr_chains.
// this leads to conflict with unit reason uip
//
void Internal::get_probehbr_lrat (int lit, int uip) {
if (!lrat || opts.probehbr)
return;
CADICAL_assert (lit);
CADICAL_assert (lrat_chain.empty ());
CADICAL_assert (val (uip) < 0);
lrat_chain = probehbr_chains[vlit (lit)][vlit (uip)];
int64_t id = unit_id (-uip);
lrat_chain.push_back (id);
}
// sets the corresponding probehbr_chain to what is currently stored in
// lrat_chain. also clears lrat_chain.
//
void Internal::set_probehbr_lrat (int lit, int uip) {
if (!lrat || opts.probehbr)
return;
CADICAL_assert (lit);
CADICAL_assert (lrat_chain.size ());
CADICAL_assert (probehbr_chains[vlit (lit)][vlit (uip)].empty ());
probehbr_chains[vlit (lit)][vlit (uip)] = lrat_chain;
lrat_chain.clear ();
}
// compute lrat_chain for the part of the tree from lit to dom
// use mini_chain because it needs to be reversed
//
void Internal::probe_dominator_lrat (int dom, Clause *reason) {
if (!lrat || !dom)
return;
LOG (reason, "probe dominator LRAT for %d from", dom);
for (const auto lit : *reason) {
if (val (lit) >= 0)
continue;
const auto other = -lit;
if (other == dom)
continue;
Flags &f = flags (other);
if (f.seen)
continue;
f.seen = true;
analyzed.push_back (other);
Var u = var (other);
if (u.level) {
if (!u.reason) {
LOG ("this may be a problem %d", other);
continue;
}
probe_dominator_lrat (dom, u.reason);
continue;
}
int64_t id = unit_id (other);
lrat_chain.push_back (id);
}
lrat_chain.push_back (reason->id);
}
/*------------------------------------------------------------------------*/
// On-the-fly (dynamic) hyper binary resolution on decision level one can
// make use of the fact that the implication graph is actually a tree.
// Compute a dominator of two literals in the binary implication tree.
int Internal::probe_dominator (int a, int b) {
require_mode (PROBE);
int l = a, k = b;
Var *u = &var (l), *v = &var (k);
CADICAL_assert (val (l) > 0), CADICAL_assert (val (k) > 0);
CADICAL_assert (u->level == 1), CADICAL_assert (v->level == 1);
while (l != k) {
if (u->trail > v->trail)
swap (l, k), swap (u, v);
if (!get_parent_reason_literal (l))
return l;
int parent = get_parent_reason_literal (k);
CADICAL_assert (parent), CADICAL_assert (val (parent) > 0);
v = &var (k = parent);
CADICAL_assert (v->level == 1);
}
LOG ("dominator %d of %d and %d", l, a, b);
CADICAL_assert (val (l) > 0);
return l;
}
// The idea of dynamic on-the-fly hyper-binary resolution came up in the
// PrecoSAT solver, where it originally was used on all decision levels.
// It turned out, that most of the hyper-binary resolvents were generated
// during probing on decision level one anyhow. Thus this version is
// specialized to decision level one, where actually all long (non-binary)
// forcing clauses can be resolved to become binary. So if we find a clause
// which would force a new assignment at decision level one during probing
// we resolve it (the 'reason' argument) to obtain a hyper binary resolvent.
// It consists of the still unassigned literal (the new unit) and the
// negation of the unique closest dominator of the negation of all (false)
// literals in the clause (which has to exist on decision level one).
// There are two special cases which should be mentioned:
//
// (A) The reason is already a binary clause in a certain sense, since all
// its unwatched literals are root level fixed to false. In this
// situation it would be better to shrink the clause immediately instead
// of adding a new clause consisting only of the watched literals.
// However, this would happen during the next garbage collection anyhow.
//
// (B) The resolvent subsumes the original reason clause. This is
// equivalent to the property that the negated dominator is contained in
// the original reason. Again one could in principle shrink the clause.
//
// Note that (A) is actually subsumed by (B). The possible optimization to
// shrink the clause on-the-fly is difficult (need to update 'blit' and
// 'binary' of the other watch at least) and also not really that important.
// For (B) we simply add the new binary resolvent and mark the old subsumed
// clause as garbage instead. And since in the situation of (A) the
// shrinking will be performed at the next garbage collection anyhow, we
// do not change clauses in (A).
// The hyper binary resolvent clause is redundant unless it subsumes the
// original reason and that one is irredundant.
// If the option 'opts.probehbr' is 'false', we actually do not add the new
// hyper binary resolvent, but simply pretend we would have added it and
// still return the dominator as new reason / parent for the new unit.
// Finally note that adding clauses changes the watches of the propagated
// literal and thus we can not use standard iterators during probing but
// need to fall back to indices. One watch for the hyper binary resolvent
// clause is added at the end of the currently propagated watches, but its
// watch is a binary watch and will be skipped during propagating long
// clauses anyhow.
inline int Internal::hyper_binary_resolve (Clause *reason) {
require_mode (PROBE);
CADICAL_assert (level == 1);
CADICAL_assert (reason->size > 2);
const const_literal_iterator end = reason->end ();
const int *lits = reason->literals;
const_literal_iterator k;
#ifndef CADICAL_NDEBUG
// First literal unassigned, all others false.
CADICAL_assert (!val (lits[0]));
for (k = lits + 1; k != end; k++)
CADICAL_assert (val (*k) < 0);
CADICAL_assert (var (lits[1]).level == 1);
#endif
LOG (reason, "hyper binary resolving");
stats.hbrs++;
stats.hbrsizes += reason->size;
const int lit = lits[1];
int dom = -lit, non_root_level_literals = 0;
for (k = lits + 2; k != end; k++) {
const int other = -*k;
CADICAL_assert (val (other) > 0);
if (!var (other).level)
continue;
dom = probe_dominator (dom, other);
non_root_level_literals++;
}
probe_reason = reason;
if (non_root_level_literals && opts.probehbr) { // !(A)
bool contained = false;
for (k = lits + 1; !contained && k != end; k++)
contained = (*k == -dom);
const bool red = !contained || reason->redundant;
if (red)
stats.hbreds++;
LOG ("new %s hyper binary resolvent %d %d",
(red ? "redundant" : "irredundant"), -dom, lits[0]);
CADICAL_assert (clause.empty ());
clause.push_back (-dom);
clause.push_back (lits[0]);
probe_dominator_lrat (dom, reason);
if (lrat)
clear_analyzed_literals ();
Clause *c = new_hyper_binary_resolved_clause (red, 2);
probe_reason = c;
if (red)
c->hyper = true;
clause.clear ();
lrat_chain.clear ();
if (contained) {
stats.hbrsubs++;
LOG (reason, "subsumed original");
mark_garbage (reason);
}
} else if (non_root_level_literals && lrat) {
// still calculate LRAT and remember for later
CADICAL_assert (!opts.probehbr);
probe_dominator_lrat (dom, reason);
clear_analyzed_literals ();
set_probehbr_lrat (dom, lits[0]);
}
return dom;
}
/*------------------------------------------------------------------------*/
// The following functions 'probe_assign' and 'probe_propagate' are used for
// propagating during failed literal probing in simplification mode, as
// replacement of the generic propagation routine 'propagate' and
// 'search_assign'.
// The code is mostly copied from 'propagate.cpp' and specialized. We only
// comment on the differences. More explanations are in 'propagate.cpp'.
inline void Internal::probe_assign (int lit, int parent) {
require_mode (PROBE);
int idx = vidx (lit);
CADICAL_assert (!val (idx));
CADICAL_assert (!flags (idx).eliminated () || !parent);
CADICAL_assert (!parent || val (parent) > 0);
Var &v = var (idx);
v.level = level;
v.trail = (int) trail.size ();
CADICAL_assert ((int) num_assigned < max_var);
num_assigned++;
v.reason = level ? probe_reason : 0;
probe_reason = 0;
set_parent_reason_literal (lit, parent);
if (!level)
learn_unit_clause (lit);
else
CADICAL_assert (level == 1);
const signed char tmp = sign (lit);
set_val (idx, tmp);
CADICAL_assert (val (lit) > 0);
CADICAL_assert (val (-lit) < 0);
trail.push_back (lit);
// Do not save the current phase during inprocessing but remember the
// number of units on the trail of the last time this literal was
// assigned. This allows us to avoid some redundant failed literal
// probing attempts. Search for 'propfixed' in 'probe.cpp' for details.
//
if (level)
propfixed (lit) = stats.all.fixed;
if (parent)
LOG ("probe assign %d parent %d", lit, parent);
else if (level)
LOG ("probe assign %d probe", lit);
else
LOG ("probe assign %d negated failed literal UIP", lit);
}
void Internal::probe_assign_decision (int lit) {
require_mode (PROBE);
CADICAL_assert (!level);
CADICAL_assert (propagated == trail.size ());
level++;
control.push_back (Level (lit, trail.size ()));
probe_assign (lit, 0);
}
void Internal::probe_assign_unit (int lit) {
require_mode (PROBE);
CADICAL_assert (!level);
CADICAL_assert (active (lit));
probe_assign (lit, 0);
}
/*------------------------------------------------------------------------*/
// same as in propagate but inlined here
//
inline void Internal::probe_lrat_for_units (int lit) {
if (!lrat)
return;
if (level)
return; // not decision level 0
LOG ("building chain for units");
CADICAL_assert (lrat_chain.empty ());
CADICAL_assert (probe_reason);
for (auto &reason_lit : *probe_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 (probe_reason->id);
}
/*------------------------------------------------------------------------*/
// This is essentially the same as 'propagate' except that we prioritize and
// always propagate binary clauses first (see our CPAIOR'13 paper on tree
// based look ahead), then immediately stop at a conflict and of course use
// 'probe_assign' instead of 'search_assign'. The binary propagation part
// is factored out too. If a new unit on decision level one is found we
// perform hyper binary resolution and thus actually build an implication
// tree instead of a DAG. Statistics counters are also different.
inline void Internal::probe_propagate2 () {
require_mode (PROBE);
int64_t &ticks = stats.ticks.probe;
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 ())
continue;
const signed char b = val (w.blit);
if (b > 0)
continue;
ticks++;
if (b < 0)
conflict = w.clause; // but continue
else {
CADICAL_assert (lrat_chain.empty ());
CADICAL_assert (!probe_reason);
probe_reason = w.clause;
probe_lrat_for_units (w.blit);
probe_assign (w.blit, -lit);
lrat_chain.clear ();
}
}
}
}
bool Internal::probe_propagate () {
require_mode (PROBE);
CADICAL_assert (!unsat);
START (propagate);
int64_t before = propagated2 = propagated;
int64_t &ticks = stats.ticks.probe;
while (!conflict) {
if (propagated2 != trail.size ())
probe_propagate2 ();
else if (propagated != trail.size ()) {
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 *)));
size_t i = 0, j = 0;
while (i != ws.size ()) {
const Watch w = ws[j++] = ws[i++];
if (w.binary ())
continue;
const signed char b = val (w.blit);
if (b > 0)
continue;
ticks++;
if (w.clause->garbage)
continue;
const literal_iterator lits = w.clause->begin ();
const int other = lits[0] ^ lits[1] ^ lit;
// lits[0] = other, lits[1] = lit;
const signed char u = val (other);
if (u > 0)
ws[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;
int r = 0;
signed char v = -1;
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)
ws[j - 1].blit = r;
else if (!v) {
ticks++;
LOG (w.clause, "unwatch %d in", r);
*k = lit;
lits[0] = other;
lits[1] = r;
watch_literal (r, lit, w.clause);
j--;
} else if (!u) {
ticks++;
if (level == 1) {
lits[0] = other, lits[1] = lit;
CADICAL_assert (lrat_chain.empty ());
CADICAL_assert (!probe_reason);
int dom = hyper_binary_resolve (w.clause);
probe_assign (other, dom);
} else {
ticks++;
CADICAL_assert (lrat_chain.empty ());
CADICAL_assert (!probe_reason);
probe_reason = w.clause;
probe_lrat_for_units (other);
probe_assign_unit (other);
lrat_chain.clear ();
}
probe_propagate2 ();
} else
conflict = w.clause;
}
}
if (j != i) {
while (i != ws.size ())
ws[j++] = ws[i++];
ws.resize (j);
}
} else
break;
}
int64_t delta = propagated2 - before;
stats.propagations.probe += delta;
if (conflict)
LOG (conflict, "conflict");
STOP (propagate);
return !conflict;
}
/*------------------------------------------------------------------------*/
// This a specialized instance of 'analyze'.
void Internal::failed_literal (int failed) {
LOG ("analyzing failed literal probe %d", failed);
stats.failed++;
stats.probefailed++;
CADICAL_assert (!unsat);
CADICAL_assert (conflict);
CADICAL_assert (level == 1);
CADICAL_assert (analyzed.empty ());
CADICAL_assert (lrat_chain.empty ());
START (analyze);
LOG (conflict, "analyzing failed literal conflict");
int uip = 0;
for (const auto &lit : *conflict) {
const int other = -lit;
if (!var (other).level) {
CADICAL_assert (val (other) > 0);
continue;
}
uip = uip ? probe_dominator (uip, other) : other;
}
probe_dominator_lrat (uip, conflict);
if (lrat)
clear_analyzed_literals ();
LOG ("found probing UIP %d", uip);
CADICAL_assert (uip);
vector<int> work;
int parent = uip;
while (parent != failed) {
const int next = get_parent_reason_literal (parent);
parent = next;
CADICAL_assert (parent);
work.push_back (parent);
}
backtrack ();
conflict = 0;
CADICAL_assert (!val (uip));
probe_assign_unit (-uip);
lrat_chain.clear ();
if (!probe_propagate ())
learn_empty_clause ();
size_t j = 0;
while (!unsat && j < work.size ()) {
// CADICAL_assert (!opts.probehbr); CADICAL_assertion fails ...
const int parent = work[j++];
const signed char tmp = val (parent);
if (tmp > 0) {
CADICAL_assert (!opts.probehbr); // ... CADICAL_assertion should hold here
get_probehbr_lrat (parent, uip);
LOG ("clashing failed parent %d", parent);
learn_empty_clause ();
} else if (tmp == 0) {
CADICAL_assert (!opts.probehbr); // ... and here
LOG ("found unassigned failed parent %d", parent);
get_probehbr_lrat (parent, uip); // this is computed during
probe_assign_unit (-parent); // propagation and can include
lrat_chain.clear (); // multiple chains where only one
if (!probe_propagate ())
learn_empty_clause (); // is needed!
}
uip = parent;
}
work.clear ();
erase_vector (work);
STOP (analyze);
CADICAL_assert (unsat || val (failed) < 0);
}
/*------------------------------------------------------------------------*/
bool Internal::is_binary_clause (Clause *c, int &a, int &b) {
CADICAL_assert (!level);
if (c->garbage)
return false;
int first = 0, second = 0;
for (const auto &lit : *c) {
const signed char tmp = val (lit);
if (tmp > 0)
return false;
if (tmp < 0)
continue;
if (second)
return false;
if (first)
second = lit;
else
first = lit;
}
if (!second)
return false;
a = first, b = second;
return true;
}
// We probe on literals first, which occur more often negated and thus we
// sort the 'probes' stack in such a way that literals which occur negated
// less frequently come first. Probes are taken from the back of the stack.
struct probe_negated_noccs_rank {
Internal *internal;
probe_negated_noccs_rank (Internal *i) : internal (i) {}
typedef size_t Type;
Type operator() (int a) const { return internal->noccs (-a); }
};
// Fill the 'probes' schedule.
void Internal::generate_probes () {
CADICAL_assert (probes.empty ());
int64_t &ticks = stats.ticks.probe;
// First determine all the literals which occur in binary clauses. It is
// way faster to go over the clauses once, instead of walking the watch
// lists for each literal.
//
init_noccs ();
ticks += 1 + cache_lines (clauses.size (), sizeof (Clause *));
for (const auto &c : clauses) {
int a, b;
ticks++;
if (!is_binary_clause (c, a, b))
continue;
noccs (a)++;
noccs (b)++;
}
for (auto idx : vars) {
// Then focus on roots of the binary implication graph, which are
// literals occurring negatively in a binary clause, but not positively.
// If neither 'idx' nor '-idx' is a root it makes less sense to probe
// this variable.
// This argument requires that equivalent literal substitution through
// 'decompose' is performed, because otherwise there might be 'cyclic
// roots' which are not tried, i.e., -1 2 0, 1 -2 0, 1 2 3 0, 1 2 -3 0.
ticks += 2;
const bool have_pos_bin_occs = noccs (idx) > 0;
const bool have_neg_bin_occs = noccs (-idx) > 0;
if (have_pos_bin_occs == have_neg_bin_occs)
continue;
int probe = have_neg_bin_occs ? idx : -idx;
// See the discussion where 'propfixed' is used below.
//
if (propfixed (probe) >= stats.all.fixed)
continue;
LOG ("scheduling probe %d negated occs %" PRId64 "", probe,
noccs (-probe));
probes.push_back (probe);
}
rsort (probes.begin (), probes.end (), probe_negated_noccs_rank (this));
reset_noccs ();
shrink_vector (probes);
PHASE ("probe-round", stats.probingrounds,
"scheduled %zd literals %.0f%%", probes.size (),
percent (probes.size (), 2u * max_var));
}
// Follow the ideas in 'generate_probes' but flush non root probes and
// reorder remaining probes.
void Internal::flush_probes () {
CADICAL_assert (!probes.empty ());
int64_t &ticks = stats.ticks.probe;
init_noccs ();
ticks += 1 + cache_lines (clauses.size (), sizeof (Clause *));
for (const auto &c : clauses) {
int a, b;
ticks++;
if (!is_binary_clause (c, a, b))
continue;
noccs (a)++;
noccs (b)++;
}
const auto eop = probes.end ();
auto j = probes.begin ();
for (auto i = j; i != eop; i++) {
int lit = *i;
if (!active (lit))
continue;
ticks += 2;
const bool have_pos_bin_occs = noccs (lit) > 0;
const bool have_neg_bin_occs = noccs (-lit) > 0;
if (have_pos_bin_occs == have_neg_bin_occs)
continue;
if (have_pos_bin_occs)
lit = -lit;
CADICAL_assert (!noccs (lit)), CADICAL_assert (noccs (-lit) > 0);
if (propfixed (lit) >= stats.all.fixed)
continue;
LOG ("keeping probe %d negated occs %" PRId64 "", lit, noccs (-lit));
*j++ = lit;
}
size_t remain = j - probes.begin ();
#ifndef CADICAL_QUIET
size_t flushed = probes.size () - remain;
#endif
probes.resize (remain);
rsort (probes.begin (), probes.end (), probe_negated_noccs_rank (this));
reset_noccs ();
shrink_vector (probes);
PHASE ("probe-round", stats.probingrounds,
"flushed %zd literals %.0f%% remaining %zd", flushed,
percent (flushed, remain + flushed), remain);
}
int Internal::next_probe () {
int generated = 0;
for (;;) {
if (probes.empty ()) {
if (generated++)
return 0;
generate_probes ();
}
while (!probes.empty ()) {
int probe = probes.back ();
probes.pop_back ();
// Eliminated or assigned.
//
if (!active (probe))
continue;
// There is now new unit since the last time we propagated this probe,
// thus we propagated it before without obtaining a conflict and
// nothing changed since then. Thus there is no need to propagate it
// again. This observation was independently made by Partik Simons
// et.al. in the context of implementing 'smodels' (see for instance
// Alg. 4 in his JAIR article from 2002) and it has also been
// contributed to the thesis work of Yacine Boufkhad.
//
if (propfixed (probe) >= stats.all.fixed)
continue;
return probe;
}
}
}
bool Internal::probe () {
if (!opts.probe)
return false;
if (unsat)
return false;
if (terminated_asynchronously ())
return false;
SET_EFFORT_LIMIT (limit, probe, true);
START_SIMPLIFIER (probe, PROBE);
stats.probingrounds++;
// Probing is limited in terms of non-probing propagations
// 'stats.propagations'. We allow a certain percentage 'opts.probeeffort'
// (say %5) of probing propagations in each probing with a lower bound of
// 'opts.probmineff'.
//
PHASE ("probe-round", stats.probingrounds,
"probing limit of %" PRId64 " propagations ", limit);
int old_failed = stats.failed;
#ifndef CADICAL_QUIET
int64_t old_probed = stats.probed;
#endif
int64_t old_hbrs = stats.hbrs;
if (!probes.empty ())
flush_probes ();
// We reset 'propfixed' since there was at least another conflict thus
// a new learned clause, which might produce new propagations (and hyper
// binary resolvents). During 'generate_probes' we keep the old value.
//
for (auto idx : vars)
propfixed (idx) = propfixed (-idx) = -1;
CADICAL_assert (unsat || propagated == trail.size ());
propagated = propagated2 = trail.size ();
int probe;
init_probehbr_lrat ();
while (!unsat && !terminated_asynchronously () &&
stats.ticks.probe < limit && (probe = next_probe ())) {
stats.probed++;
LOG ("probing %d", probe);
probe_assign_decision (probe);
if (probe_propagate ())
backtrack ();
else
failed_literal (probe);
clean_probehbr_lrat ();
}
if (unsat)
LOG ("probing derived empty clause");
else if (propagated < trail.size ()) {
LOG ("probing produced %zd units",
(size_t) (trail.size () - propagated));
if (!propagate ()) {
LOG ("propagating units after probing results in empty clause");
learn_empty_clause ();
} else
sort_watches ();
}
int failed = stats.failed - old_failed;
#ifndef CADICAL_QUIET
int64_t probed = stats.probed - old_probed;
#endif
int64_t hbrs = stats.hbrs - old_hbrs;
PHASE ("probe-round", stats.probingrounds,
"probed %" PRId64 " and found %d failed literals", probed, failed);
if (hbrs)
PHASE ("probe-round", stats.probingrounds,
"found %" PRId64 " hyper binary resolvents", hbrs);
STOP_SIMPLIFIER (probe, PROBE);
report ('p', !opts.reportall && !(unsat + failed + hbrs));
return !unsat && failed;
}
/*------------------------------------------------------------------------*/
// This schedules a number of inprocessing techniques.
// These range from very cheap and beneficial (decompose) to
// more expensive and sometimes less beneficial. We want to limit
// expensive techniques to some fraction of total time or search time.
// this is done using 'ticks'.
// Generally, there are options for each of the techniques to set the
// efficiency, i.e., the fraction of ticks they are allowed as budget.
// Whenever e.g. vivify is called, the budget is calculated from the
// search ticks that have passed since the last vivify round and this
// efficiency.
// We want to be able to run inprocessing frequently, without it dominating
// runtimes. This entire inprocessing scheme is scheduled after a certain
// amount of conflicts were found, the gap between two inprocessing rounds
// increasing by a constant number each time. In effect, the number of
// inprocessing rounds is allways the square root of the number of conflicts
// with some constant factor.
// This factor can also be with the option 'inprobeint'
// Some of the techniques are not run always, for different reasons.
// 'factor' or BVA depends on certain structures of the irredundant clauses
// and as such will only be run when new irredundant clauses are derived or
// it was not able to finish with the entire search space.
// 'sweeping' is especially usefull on certain classes of formulas, and uses
// a increasing or decreasing delay that depends on how usefull it was.
// In cases where it is less usefull, we obviously want to reset the budged,
// even if the routine was delayed.
// Additionally 'vivify', 'sweep' and 'factor' can also have a big initial
// overhead in setting up the datastructures. This has to be accounted for
// with the 'ticks', however, since inprocessing is done frequently, this
// overhead is too expensive to pay. So instead, we accumulate the budget
// of 'ticks' and delay the technique until it passes a certain threshhold,
// which depends on the the cost of initialization. Note that in the case of
// sweeping, we have two different delays, one which resets the budged, and
// one which passes it to the next round. In this case the former takes
// precendent, until we would run sweeping once, at which point the focus
// switches to the latter delay until the budget is big enough, such that
// sweeping can be run. Then we switch back to the other delay.
void CaDiCaL::Internal::inprobe (bool update_limits) {
if (unsat)
return;
if (level)
backtrack ();
if (!propagate ()) {
learn_empty_clause ();
return;
}
stats.inprobingphases++;
if (external_prop) {
CADICAL_assert (!level);
private_steps = true;
}
const int before = active ();
const int before_extended = stats.variables_extension;
// schedule of inprobing techniques.
//
{
mark_duplicated_binary_clauses_as_garbage ();
decompose ();
if (ternary ())
decompose (); // If we derived a binary clause
if (probe ())
decompose ();
if (extract_gates ())
decompose ();
if (sweep ()) // full occurrence list
decompose (); // ... and (ELS) afterwards.
(void) vivify (); // resets watches
transred (); // builds big.
factor (); // resets watches, partial occurrence list
}
if (external_prop) {
CADICAL_assert (!level);
private_steps = false;
}
if (!update_limits)
return;
const int after = active ();
const int after_extended = stats.variables_extension;
const int diff_extended = after_extended - before_extended;
CADICAL_assert (diff_extended >= 0);
const int removed = before - after + diff_extended;
CADICAL_assert (removed >= 0);
if (removed) {
stats.inprobesuccess++;
PHASE ("probe-phase", stats.inprobingphases,
"successfully removed %d active variables %.0f%%", removed,
percent (removed, before));
} else
PHASE ("probe-phase", stats.inprobingphases,
"could not remove any active variable");
const int64_t delta =
25 * opts.inprobeint * log10 (stats.inprobingphases + 9);
lim.inprobe = stats.conflicts + delta;
PHASE ("probe-phase", stats.inprobingphases,
"new limit at %" PRId64 " conflicts after %" PRId64 " conflicts",
lim.inprobe, delta);
last.inprobe.reductions = stats.reductions;
}
} // namespace CaDiCaL
ABC_NAMESPACE_IMPL_END

View File

@ -1,113 +0,0 @@
#include "global.h"
#ifndef CADICAL_QUIET
#include "internal.hpp"
ABC_NAMESPACE_IMPL_START
namespace CaDiCaL {
// Initialize all profile counters with constant name and profiling level.
Profiles::Profiles (Internal *s)
: internal (s)
#define PROFILE(NAME, LEVEL) , NAME (#NAME, LEVEL)
PROFILES
#undef PROFILE
{
}
void Internal::start_profiling (Profile &profile, double s) {
CADICAL_assert (profile.level <= opts.profile);
CADICAL_assert (!profile.active);
profile.started = s;
profile.active = true;
}
void Internal::stop_profiling (Profile &profile, double s) {
CADICAL_assert (profile.level <= opts.profile);
CADICAL_assert (profile.active);
profile.value += s - profile.started;
profile.active = false;
}
double Internal::update_profiles () {
double now = time ();
#define PROFILE(NAME, LEVEL) \
do { \
Profile &profile = profiles.NAME; \
if (profile.active) { \
CADICAL_assert (profile.level <= opts.profile); \
profile.value += now - profile.started; \
profile.started = now; \
} \
} while (0);
PROFILES
#undef PROFILE
return now;
}
double Internal::solve_time () {
(void) update_profiles ();
return profiles.solve.value;
}
#define PRT(S, T) \
MSG ("%s" S "%s", tout.magenta_code (), T, tout.normal_code ())
void Internal::print_profile () {
double now = update_profiles ();
const char *time_type = opts.realtime ? "real" : "process";
SECTION ("run-time profiling");
PRT ("%s time taken by individual solving procedures", time_type);
PRT ("(percentage relative to %s time for solving)", time_type);
LINE ();
const size_t size = sizeof profiles / sizeof (Profile);
struct Profile *profs[size];
size_t n = 0;
#define PROFILE(NAME, LEVEL) \
do { \
if (LEVEL > opts.profile) \
break; \
Profile *p = &profiles.NAME; \
if (p == &profiles.solve) \
break; \
if (!profiles.NAME.value && p != &profiles.parse && \
p != &profiles.search && p != &profiles.simplify) \
break; \
profs[n++] = p; \
} while (0);
PROFILES
#undef PROFILE
CADICAL_assert (n <= size);
// Explicit bubble sort to avoid heap allocation since 'print_profile'
// is also called during catching a signal after out of heap memory.
// This only makes sense if 'profs' is allocated on the stack, and
// not the heap, which should be the case.
double solve = profiles.solve.value;
for (size_t i = 0; i < n; i++) {
for (size_t j = i + 1; j < n; j++)
if (profs[j]->value > profs[i]->value)
swap (profs[i], profs[j]);
MSG ("%12.2f %7.2f%% %s", profs[i]->value,
percent (profs[i]->value, solve), profs[i]->name);
}
MSG (" =================================");
MSG ("%12.2f %7.2f%% solve", solve, percent (solve, now));
LINE ();
PRT ("last line shows %s time for solving", time_type);
PRT ("(percentage relative to total %s time)", time_type);
}
} // namespace CaDiCaL
ABC_NAMESPACE_IMPL_END
#endif // ifndef CADICAL_QUIET

View File

@ -1,663 +0,0 @@
#include "global.h"
#include "internal.hpp"
ABC_NAMESPACE_IMPL_START
namespace CaDiCaL {
using namespace std;
/*------------------------------------------------------------------------*/
// Enable proof logging and checking by allocating a 'Proof' object.
void Internal::new_proof_on_demand () {
if (!proof) {
LOG ("connecting proof to internal solver");
proof = new Proof (this);
}
}
void Internal::resize_unit_clauses_idx () {
size_t new_vsize = vsize ? 2 * vsize : 1 + (size_t) max_var;
unit_clauses_idx.resize (2 * new_vsize, 0);
}
void Internal::force_lrat () {
if (lrat)
return;
CADICAL_assert (proof);
lrat = true;
}
void Internal::connect_proof_tracer (Tracer *tracer, bool antecedents,
bool finalize_clauses) {
new_proof_on_demand ();
if (antecedents)
force_lrat ();
if (finalize_clauses)
frat = true;
resize_unit_clauses_idx ();
proof->connect (tracer);
tracers.push_back (tracer);
}
void Internal::connect_proof_tracer (InternalTracer *tracer,
bool antecedents,
bool finalize_clauses) {
new_proof_on_demand ();
if (antecedents)
force_lrat ();
if (finalize_clauses)
frat = true;
resize_unit_clauses_idx ();
tracer->connect_internal (this);
proof->connect (tracer);
tracers.push_back (tracer);
}
void Internal::connect_proof_tracer (StatTracer *tracer, bool antecedents,
bool finalize_clauses) {
new_proof_on_demand ();
if (antecedents)
force_lrat ();
if (finalize_clauses)
frat = true;
resize_unit_clauses_idx ();
tracer->connect_internal (this);
proof->connect (tracer);
stat_tracers.push_back (tracer);
}
void Internal::connect_proof_tracer (FileTracer *tracer, bool antecedents,
bool finalize_clauses) {
new_proof_on_demand ();
if (antecedents)
force_lrat ();
if (finalize_clauses)
frat = true;
resize_unit_clauses_idx ();
tracer->connect_internal (this);
proof->connect (tracer);
file_tracers.push_back (tracer);
}
bool Internal::disconnect_proof_tracer (Tracer *tracer) {
auto it = std::find (tracers.begin (), tracers.end (), tracer);
if (it != tracers.end ()) {
tracers.erase (it);
CADICAL_assert (proof);
proof->disconnect (tracer);
return true;
}
return false;
}
bool Internal::disconnect_proof_tracer (StatTracer *tracer) {
auto it = std::find (stat_tracers.begin (), stat_tracers.end (), tracer);
if (it != stat_tracers.end ()) {
stat_tracers.erase (it);
CADICAL_assert (proof);
proof->disconnect (tracer);
return true;
}
return false;
}
bool Internal::disconnect_proof_tracer (FileTracer *tracer) {
auto it = std::find (file_tracers.begin (), file_tracers.end (), tracer);
if (it != file_tracers.end ()) {
file_tracers.erase (it);
CADICAL_assert (proof);
proof->disconnect (tracer);
return true;
}
return false;
}
void Proof::disconnect (Tracer *t) {
tracers.erase (std::remove (tracers.begin (), tracers.end (), t),
tracers.end ());
}
// Enable proof tracing.
void Internal::trace (File *file) {
if (opts.veripb) {
LOG ("PROOF connecting VeriPB tracer");
bool antecedents = opts.veripb == 1 || opts.veripb == 2;
bool deletions = opts.veripb == 2 || opts.veripb == 4;
FileTracer *ft =
new VeripbTracer (this, file, opts.binary, antecedents, deletions);
connect_proof_tracer (ft, antecedents);
} else if (opts.frat) {
LOG ("PROOF connecting FRAT tracer");
bool antecedents = opts.frat == 1;
resize_unit_clauses_idx ();
FileTracer *ft =
new FratTracer (this, file, opts.binary, opts.frat == 1);
connect_proof_tracer (ft, antecedents, true);
} else if (opts.lrat) {
LOG ("PROOF connecting LRAT tracer");
FileTracer *ft = new LratTracer (this, file, opts.binary);
connect_proof_tracer (ft, true);
} else if (opts.idrup) {
LOG ("PROOF connecting IDRUP tracer");
FileTracer *ft = new IdrupTracer (this, file, opts.binary);
connect_proof_tracer (ft, true);
} else if (opts.lidrup) {
LOG ("PROOF connecting LIDRUP tracer");
FileTracer *ft = new LidrupTracer (this, file, opts.binary);
connect_proof_tracer (ft, true);
} else {
LOG ("PROOF connecting DRAT tracer");
FileTracer *ft = new DratTracer (this, file, opts.binary);
connect_proof_tracer (ft, false);
}
}
// Enable proof checking.
void Internal::check () {
new_proof_on_demand ();
if (opts.checkproof > 1) {
StatTracer *lratchecker = new LratChecker (this);
DeferDeletePtr<LratChecker> delete_lratchecker (
(LratChecker *) lratchecker);
LOG ("PROOF connecting LRAT proof checker");
force_lrat ();
frat = true;
resize_unit_clauses_idx ();
proof->connect (lratchecker);
stat_tracers.push_back (lratchecker);
delete_lratchecker.release ();
}
if (opts.checkproof == 1 || opts.checkproof == 3) {
StatTracer *checker = new Checker (this);
DeferDeletePtr<Checker> delete_checker ((Checker *) checker);
LOG ("PROOF connecting proof checker");
proof->connect (checker);
stat_tracers.push_back (checker);
delete_checker.release ();
}
}
// We want to close a proof trace and stop checking as soon we are done.
void Internal::close_trace (bool print) {
for (auto &tracer : file_tracers)
tracer->close (print);
}
// We can flush a proof trace file before actually closing it.
void Internal::flush_trace (bool print) {
for (auto &tracer : file_tracers)
tracer->flush (print);
}
/*------------------------------------------------------------------------*/
Proof::Proof (Internal *s) : internal (s) { LOG ("PROOF new"); }
Proof::~Proof () { LOG ("PROOF delete"); }
/*------------------------------------------------------------------------*/
inline void Proof::add_literal (int internal_lit) {
const int external_lit = internal->externalize (internal_lit);
clause.push_back (external_lit);
}
inline void Proof::add_literals (Clause *c) {
for (auto const &lit : *c)
add_literal (lit);
}
inline void Proof::add_literals (const vector<int> &c) {
for (auto const &lit : c)
add_literal (lit);
}
/*------------------------------------------------------------------------*/
void Proof::add_original_clause (int64_t id, bool r, const vector<int> &c) {
LOG (c, "PROOF adding original internal clause");
add_literals (c);
clause_id = id;
redundant = r;
add_original_clause ();
}
void Proof::add_external_original_clause (int64_t id, bool r,
const vector<int> &c,
bool restore) {
// literals of c are already external
CADICAL_assert (clause.empty ());
for (auto const &lit : c)
clause.push_back (lit);
clause_id = id;
redundant = r;
add_original_clause (restore);
}
void Proof::delete_external_original_clause (int64_t id, bool r,
const vector<int> &c) {
// literals of c are already external
CADICAL_assert (clause.empty ());
for (auto const &lit : c)
clause.push_back (lit);
clause_id = id;
redundant = r;
delete_clause ();
}
void Proof::add_derived_empty_clause (int64_t id,
const vector<int64_t> &chain) {
LOG ("PROOF adding empty clause");
CADICAL_assert (clause.empty ());
CADICAL_assert (proof_chain.empty ());
for (const auto &cid : chain)
proof_chain.push_back (cid);
clause_id = id;
redundant = false;
add_derived_clause ();
}
void Proof::add_derived_unit_clause (int64_t id, int internal_unit,
const vector<int64_t> &chain) {
LOG ("PROOF adding unit clause %d", internal_unit);
CADICAL_assert (proof_chain.empty ());
CADICAL_assert (clause.empty ());
add_literal (internal_unit);
for (const auto &cid : chain)
proof_chain.push_back (cid);
clause_id = id;
redundant = false;
add_derived_clause ();
}
/*------------------------------------------------------------------------*/
void Proof::add_derived_clause (Clause *c, const vector<int64_t> &chain) {
LOG (c, "PROOF adding to proof derived");
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;
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");
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;
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
CADICAL_assert (clause.empty ());
CADICAL_assert (proof_chain.empty ());
for (const auto &lit : c)
clause.push_back (lit);
for (const auto &cid : chain)
proof_chain.push_back (cid);
clause_id = id;
add_assumption_clause ();
}
void Proof::add_assumption (int a) {
// a is already external
CADICAL_assert (clause.empty ());
CADICAL_assert (proof_chain.empty ());
clause.push_back (a);
add_assumption ();
}
void Proof::add_constraint (const vector<int> &c) {
// literals of c are already external
CADICAL_assert (clause.empty ());
CADICAL_assert (proof_chain.empty ());
for (const auto &lit : c)
clause.push_back (lit);
add_constraint ();
}
void Proof::add_assumption_clause (int64_t id, int lit,
const vector<int64_t> &chain) {
CADICAL_assert (clause.empty ());
CADICAL_assert (proof_chain.empty ());
clause.push_back (lit);
for (const auto &cid : chain)
proof_chain.push_back (cid);
clause_id = id;
add_assumption_clause ();
}
void Proof::delete_clause (Clause *c) {
LOG (c, "PROOF deleting from proof");
clause.clear (); // Can be non-empty if an allocation fails during adding.
add_literals (c);
clause_id = c->id;
redundant = c->redundant;
delete_clause (); // Increments 'statistics.deleted'.
}
void Proof::delete_clause (int64_t id, bool r, const vector<int> &c) {
LOG (c, "PROOF deleting from proof");
CADICAL_assert (clause.empty ());
add_literals (c);
clause_id = id;
redundant = r;
delete_clause (); // Increments 'statistics.deleted'.
}
void Proof::weaken_minus (Clause *c) {
LOG (c, "PROOF weaken minus of");
CADICAL_assert (clause.empty ());
add_literals (c);
clause_id = c->id;
weaken_minus ();
}
void Proof::weaken_minus (int64_t id, const vector<int> &c) {
LOG (c, "PROOF deleting from proof");
CADICAL_assert (clause.empty ());
add_literals (c);
clause_id = id;
weaken_minus ();
}
void Proof::weaken_plus (Clause *c) {
weaken_minus (c);
delete_clause (c); // Increments 'statistics.deleted'.
}
void Proof::weaken_plus (int64_t id, const vector<int> &c) {
weaken_minus (id, c);
delete_clause (id, false, c); // Increments 'statistics.deleted'.
}
void Proof::delete_unit_clause (int64_t id, const int lit) {
LOG ("PROOF deleting unit from proof %d", lit);
CADICAL_assert (clause.empty ());
add_literal (lit);
clause_id = id;
redundant = false;
delete_clause ();
}
void Proof::finalize_clause (Clause *c) {
LOG (c, "PROOF finalizing clause");
CADICAL_assert (clause.empty ());
add_literals (c);
clause_id = c->id;
finalize_clause ();
}
void Proof::finalize_clause (int64_t id, const vector<int> &c) {
LOG (c, "PROOF finalizing clause");
CADICAL_assert (clause.empty ());
for (const auto &lit : c)
add_literal (lit);
clause_id = id;
finalize_clause ();
}
void Proof::finalize_unit (int64_t id, int lit) {
LOG ("PROOF finalizing clause %d", lit);
CADICAL_assert (clause.empty ());
add_literal (lit);
clause_id = id;
finalize_clause ();
}
void Proof::finalize_external_unit (int64_t id, int lit) {
LOG ("PROOF finalizing clause %d", lit);
CADICAL_assert (clause.empty ());
clause.push_back (lit);
clause_id = id;
finalize_clause ();
}
/*------------------------------------------------------------------------*/
// During garbage collection clauses are shrunken by removing falsified
// literals. To avoid copying the clause, we provide a specialized tracing
// function here, which traces the required 'add' and 'remove' operations.
void Proof::flush_clause (Clause *c) {
LOG (c, "PROOF flushing falsified literals in");
CADICAL_assert (clause.empty ());
const bool antecedents = (internal->lrat || internal->frat);
for (int i = 0; i < c->size; i++) {
int internal_lit = c->literals[i];
if (internal->fixed (internal_lit) < 0) {
if (antecedents) {
int64_t id = internal->unit_id (-internal_lit);
proof_chain.push_back (id);
}
continue;
}
add_literal (internal_lit);
}
proof_chain.push_back (c->id);
redundant = c->redundant;
int64_t id = ++internal->clause_id;
clause_id = id;
add_derived_clause ();
delete_clause (c);
c->id = id;
}
// While strengthening clauses, e.g., through self-subsuming resolutions,
// during subsumption checking, we have a similar situation, except that we
// have to remove exactly one literal. Again the following function allows
// to avoid copying the clause and instead provides tracing of the required
// 'add' and 'remove' operations.
void Proof::strengthen_clause (Clause *c, int remove,
const vector<int64_t> &chain) {
LOG (c, "PROOF strengthen by removing %d in", remove);
CADICAL_assert (clause.empty ());
for (int i = 0; i < c->size; i++) {
int internal_lit = c->literals[i];
if (internal_lit == remove)
continue;
add_literal (internal_lit);
}
int64_t id = ++internal->clause_id;
clause_id = id;
redundant = c->redundant;
for (const auto &cid : chain)
proof_chain.push_back (cid);
add_derived_clause ();
delete_clause (c);
c->id = id;
}
void Proof::otfs_strengthen_clause (Clause *c, const std::vector<int> &old,
const vector<int64_t> &chain) {
LOG (c, "PROOF otfs strengthen");
CADICAL_assert (clause.empty ());
for (int i = 0; i < c->size; i++) {
int internal_lit = c->literals[i];
add_literal (internal_lit);
}
int64_t id = ++internal->clause_id;
clause_id = id;
redundant = c->redundant;
for (const auto &cid : chain)
proof_chain.push_back (cid);
add_derived_clause ();
delete_clause (c->id, c->redundant, old);
c->id = id;
}
void Proof::strengthen (int64_t id) {
clause_id = id;
strengthen ();
}
/*------------------------------------------------------------------------*/
void Proof::add_original_clause (bool restore) {
LOG (clause, "PROOF adding original external clause");
CADICAL_assert (clause_id);
for (auto &tracer : tracers) {
tracer->add_original_clause (clause_id, false, clause, restore);
}
clause.clear ();
clause_id = 0;
}
void Proof::add_derived_clause () {
LOG (clause, "PROOF adding derived external clause (redundant: %d)",
redundant);
CADICAL_assert (clause_id);
for (auto &tracer : tracers) {
tracer->add_derived_clause (clause_id, redundant, clause, proof_chain);
}
proof_chain.clear ();
clause.clear ();
clause_id = 0;
}
void Proof::delete_clause () {
LOG (clause, "PROOF deleting external clause");
for (auto &tracer : tracers) {
tracer->delete_clause (clause_id, redundant, clause);
}
clause.clear ();
clause_id = 0;
}
void Proof::demote_clause () {
LOG (clause, "PROOF demoting external clause");
CADICAL_assert (!redundant);
for (auto &tracer : tracers) {
tracer->demote_clause (clause_id, clause);
}
clause.clear ();
clause_id = 0;
}
void Proof::weaken_minus () {
LOG (clause, "PROOF marking as clause to restore");
for (auto &tracer : tracers) {
tracer->weaken_minus (clause_id, clause);
}
clause.clear ();
clause_id = 0;
}
void Proof::strengthen () {
LOG ("PROOF strengthen clause with id %" PRId64, clause_id);
for (auto &tracer : tracers) {
tracer->strengthen (clause_id);
}
clause_id = 0;
}
void Proof::finalize_clause () {
for (auto &tracer : tracers) {
tracer->finalize_clause (clause_id, clause);
}
clause.clear ();
clause_id = 0;
}
void Proof::add_assumption_clause () {
LOG (clause, "PROOF adding assumption clause");
for (auto &tracer : tracers) {
tracer->add_assumption_clause (clause_id, clause, proof_chain);
}
proof_chain.clear ();
clause.clear ();
clause_id = 0;
}
void Proof::add_assumption () {
LOG (clause, "PROOF adding assumption");
CADICAL_assert (clause.size () == 1);
for (auto &tracer : tracers) {
tracer->add_assumption (clause.back ());
}
clause.clear ();
}
void Proof::add_constraint () {
LOG (clause, "PROOF adding constraint");
for (auto &tracer : tracers) {
tracer->add_constraint (clause);
}
clause.clear ();
}
void Proof::reset_assumptions () {
LOG ("PROOF reset assumptions");
for (auto &tracer : tracers) {
tracer->reset_assumptions ();
}
}
void Proof::report_status (int status, int64_t id) {
LOG ("PROOF reporting status %d", status);
for (auto &tracer : tracers) {
tracer->report_status (status, id);
}
}
void Proof::begin_proof (int64_t id) {
LOG (clause, "PROOF begin proof");
for (auto &tracer : tracers) {
tracer->begin_proof (id);
}
}
void Proof::solve_query () {
LOG (clause, "PROOF solve query");
for (auto &tracer : tracers) {
tracer->solve_query ();
}
}
void Proof::conclude_unsat (ConclusionType con,
const vector<int64_t> &conclusion) {
LOG (clause, "PROOF conclude unsat");
for (auto &tracer : tracers) {
tracer->conclude_unsat (con, conclusion);
}
}
void Proof::conclude_sat (const vector<int> &model) {
LOG (clause, "PROOF conclude sat");
for (auto &tracer : tracers) {
tracer->conclude_sat (model);
}
}
void Proof::conclude_unknown (const vector<int> &trail) {
LOG (clause, "PROOF conclude unknown");
for (auto &tracer : tracers) {
tracer->conclude_unknown (trail);
}
}
} // namespace CaDiCaL
ABC_NAMESPACE_IMPL_END

View File

@ -1,586 +0,0 @@
#include "global.h"
#include "internal.hpp"
ABC_NAMESPACE_IMPL_START
namespace CaDiCaL {
/*------------------------------------------------------------------------*/
// We are using the address of 'decision_reason' as pseudo reason for
// decisions to distinguish assignment decisions from other assignments.
// Before we added chronological backtracking all learned units were
// assigned at decision level zero ('Solver.level == 0') and we just used a
// zero pointer as reason. After allowing chronological backtracking units
// were also assigned at higher decision level (but with assignment level
// zero), and it was not possible anymore to just distinguish the case
// 'unit' versus 'decision' by just looking at the current level. Both had
// a zero pointer as reason. Now only units have a zero reason and
// decisions need to use the pseudo reason 'decision_reason'.
// External propagation steps use the pseudo reason 'external_reason'.
// The corresponding actual reason clauses are learned only when they are
// relevant in conflict analysis or in root-level fixing steps.
static Clause decision_reason_clause;
static Clause *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
// level is defined as the maximum level of the literals in the reason
// clause except the literal for which the clause is a reason. This
// function determines this assignment level. For non-chronological
// backtracking as in classical CDCL this function always returns the
// current decision level, the concept of assignment level does not make
// sense, and accordingly this function can be skipped.
// In case of external propagation, it is implicitly assumed that the
// assignment level is the level of the literal (since the reason clause,
// i.e., the set of other literals, is unknown).
inline int Internal::assignment_level (int lit, Clause *reason) {
CADICAL_assert (opts.chrono || external_prop);
if (!reason || reason == external_reason)
return level;
int res = 0;
for (const auto &other : *reason) {
if (other == lit)
continue;
CADICAL_assert (val (other));
int tmp = var (other).level;
if (tmp > res)
res = tmp;
}
return res;
}
// calculate lrat_chain
//
void Internal::build_chain_for_units (int lit, Clause *reason,
bool forced) {
if (!lrat)
return;
if (opts.chrono && assignment_level (lit, reason) && !forced)
return;
else if (!opts.chrono && level && !forced)
return; // not decision level 0
CADICAL_assert (lrat_chain.empty ());
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);
}
// same code as above but reason is assumed to be conflict and lit is not
// needed
//
void Internal::build_chain_for_empty () {
if (!lrat || !lrat_chain.empty ())
return;
CADICAL_assert (!level);
CADICAL_assert (lrat_chain.empty ());
CADICAL_assert (conflict);
LOG (conflict, "lrat for global empty clause with conflict");
for (auto &lit : *conflict) {
CADICAL_assert (val (lit) < 0);
int64_t id = unit_id (-lit);
lrat_chain.push_back (id);
}
lrat_chain.push_back (conflict->id);
}
/*------------------------------------------------------------------------*/
inline void Internal::search_assign (int lit, Clause *reason) {
if (level)
require_mode (SEARCH);
const int idx = vidx (lit);
const bool from_external = reason == external_reason;
CADICAL_assert (!val (idx));
CADICAL_assert (!flags (idx).eliminated () || reason == decision_reason ||
reason == external_reason);
Var &v = var (idx);
int lit_level;
CADICAL_assert (!lrat || level || reason == external_reason ||
reason == decision_reason || !lrat_chain.empty ());
// The following cases are explained in the two comments above before
// 'decision_reason' and 'assignment_level'.
//
// External decision reason means that the propagation was done by
// an external propagation and the reason clause not known (yet).
// In that case it is assumed that the propagation is NOT out of
// order (i.e. lit_level = level), because due to lazy explanation,
// we can not calculate the real assignment level.
// The function assignment_level () will also assign the current level
// to literals with external reason.
if (!reason)
lit_level = 0; // unit
else if (reason == decision_reason)
lit_level = level, reason = 0;
else if (opts.chrono)
lit_level = assignment_level (lit, reason);
else
lit_level = level;
if (!lit_level)
reason = 0;
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++;
if (!lit_level && !from_external)
learn_unit_clause (lit); // increases 'stats.fixed'
CADICAL_assert (lit_level || !from_external);
const signed char tmp = sign (lit);
set_val (idx, tmp);
CADICAL_assert (val (lit) > 0); // Just a bit paranoid but useful.
CADICAL_assert (val (-lit) < 0); // Ditto.
if (!searching_lucky_phases)
phases.saved[idx] = tmp; // phase saving during search
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
if (watching ()) {
const Watches &ws = watches (-lit);
if (!ws.empty ()) {
const Watch &w = ws[0];
#ifndef WIN32
__builtin_prefetch (&w, 0, 1);
#endif
}
}
lrat_chain.clear ();
}
/*------------------------------------------------------------------------*/
// External versions of 'search_assign' which are not inlined. They either
// are used to assign unit clauses on the root-level, in 'decide' to assign
// a decision or in 'analyze' to assign the literal 'driven' by a learned
// clause. This happens far less frequently than the 'search_assign' above,
// which is called directly in 'propagate' below and thus is inlined.
void Internal::assign_unit (int lit) {
CADICAL_assert (!level);
search_assign (lit, 0);
}
// Just assume the given literal as decision (increase decision level and
// assign it). This is used below in 'decide'.
void Internal::search_assume_decision (int lit) {
require_mode (SEARCH);
CADICAL_assert (propagated == trail.size ());
new_trail_level (lit);
notify_decision ();
LOG ("search decide %d", lit);
search_assign (lit, decision_reason);
}
void Internal::search_assign_driving (int lit, Clause *c) {
require_mode (SEARCH);
search_assign (lit, c);
notify_assignments ();
}
void Internal::search_assign_external (int lit) {
require_mode (SEARCH);
search_assign (lit, external_reason);
notify_assignments ();
}
/*------------------------------------------------------------------------*/
// The 'propagate' function is usually the hot-spot of a CDCL SAT solver.
// The 'trail' stack saves assigned variables and is used here as BFS queue
// for checking clauses with the negation of assigned variables for being in
// conflict or whether they produce additional assignments.
// This version of 'propagate' uses lazy watches and keeps two watched
// literals at the beginning of the clause. We also use 'blocking literals'
// to reduce the number of times clauses have to be visited (2008 JSAT paper
// by Chu, Harwood and Stuckey). The watches know if a watched clause is
// binary, in which case it never has to be visited. If a binary clause is
// falsified we continue propagating.
// Finally, for long clauses we save the position of the last watch
// replacement in 'pos', which in turn reduces certain quadratic accumulated
// propagation costs (2013 JAIR article by Ian Gent) at the expense of four
// more bytes for each clause.
bool Internal::propagate () {
if (level)
require_mode (SEARCH);
CADICAL_assert (!unsat);
LOG ("starting propagate");
START (propagate);
// Updating statistics counter in the propagation loops is costly so we
// delay until propagation ran to completion.
//
int64_t before = propagated;
int64_t ticks = 0;
while (!conflict && 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;
ticks += 1 + cache_lines (ws.size (), sizeof *i);
while (i != eow) {
const Watch w = *j++ = *i++;
const signed char b = val (w.blit);
LOG (w.clause, "checking");
if (b > 0)
continue; // blocking literal satisfied
if (w.binary ()) {
// CADICAL_assert (w.clause->redundant || !w.clause->garbage);
// 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)
conflict = w.clause; // but continue ...
else {
build_chain_for_units (w.blit, w.clause, 0);
search_assign (w.blit, w.clause);
// lrat_chain.clear (); done in search_assign
ticks++;
}
} else {
CADICAL_assert (w.clause->size > 2);
if (conflict)
break; // Stop if there was a binary conflict already.
// 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.
ticks++;
if (w.clause->garbage) {
j--;
continue;
}
literal_iterator lits = w.clause->begin ();
// Simplify code by forcing 'lit' to be the second literal in the
// clause. This goes back to MiniSAT. We use a branch-less version
// for conditionally swapping the first two literals, since it
// turned out to be substantially faster than this one
//
// if (lits[0] == lit) swap (lits[0], lits[1]);
//
// which achieves the same effect, but needs a branch.
//
const int other = lits[0] ^ lits[1] ^ lit;
const signed char u = val (other); // value of the other watch
if (u > 0)
j[-1].blit = other; // satisfied, just replace blit
else {
// This follows Ian Gent's (JAIR'13) idea of saving the position
// of the last watch replacement. In essence it needs two copies
// of the default search for a watch replacement (in essence the
// code in the 'if (v < 0) { ... }' block below), one starting at
// the saved position until the end of the clause and then if that
// one failed to find a replacement another one starting at the
// first non-watched literal until the saved position.
const int size = w.clause->size;
const literal_iterator middle = lits + w.clause->pos;
const const_literal_iterator end = lits + size;
literal_iterator k = middle;
// Find replacement watch 'r' at position 'k' with value 'v'.
int r = 0;
signed char v = -1;
while (k != end && (v = val (r = *k)) < 0)
k++;
if (v < 0) { // need second search starting at the head?
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'.
ticks++;
} 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);
search_assign (other, w.clause);
// lrat_chain.clear (); done in search_assign
ticks++;
// Similar code is in the implementation of the SAT'18 paper on
// chronological backtracking but in our experience, this code
// first does not really seem to be necessary for correctness,
// and further does not improve running time either.
//
if (opts.chrono > 1) {
const int other_level = var (other).level;
if (other_level > var (lit).level) {
// The assignment level of the new unit 'other' is larger
// than the assignment level of 'lit'. Thus we should find
// another literal in the clause at that higher assignment
// level and watch that instead of 'lit'.
CADICAL_assert (size > 2);
int pos, s = 0;
for (pos = 2; pos < size; pos++)
if (var (s = lits[pos]).level == other_level)
break;
CADICAL_assert (s);
CADICAL_assert (pos < size);
LOG (w.clause, "unwatch %d in", lit);
lits[pos] = lit;
lits[0] = other;
lits[1] = s;
watch_literal (s, lit, w.clause);
j--; // Drop this watch from the watch list of 'lit'.
}
}
} else {
CADICAL_assert (u < 0);
CADICAL_assert (v < 0);
// The other watch is assigned false ('u < 0') and all other
// literals as well (still 'v < 0'), thus we found a conflict.
conflict = w.clause;
break;
}
}
}
}
if (j != i) {
while (i != eow)
*j++ = *i++;
ws.resize (j - ws.begin ());
}
}
if (searching_lucky_phases) {
if (conflict)
LOG (conflict, "ignoring lucky conflict");
} else {
// Avoid updating stats eagerly in the hot-spot of the solver.
//
stats.propagations.search += propagated - before;
stats.ticks.search[stable] += ticks;
if (!conflict)
no_conflict_until = propagated;
else {
if (stable)
stats.stabconflicts++;
stats.conflicts++;
LOG (conflict, "conflict");
// The trail before the current decision level was conflict free.
//
no_conflict_until = control[level].trail;
}
}
STOP (propagate);
return !conflict;
}
/*------------------------------------------------------------------------*/
void Internal::propergate () {
CADICAL_assert (!conflict);
CADICAL_assert (propagated == trail.size ());
while (propergated != trail.size ()) {
const int lit = -trail[propergated++];
LOG ("propergating %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++;
if (w.binary ()) {
CADICAL_assert (val (w.blit) > 0);
continue;
}
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);
// TODO: check if u == 0 can happen.
if (u > 0)
continue;
CADICAL_assert (u < 0);
const int size = w.clause->size;
const literal_iterator middle = lits + w.clause->pos;
const const_literal_iterator end = lits + size;
literal_iterator k = middle;
int r = 0;
signed char v = -1;
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++;
}
CADICAL_assert (lits + 2 <= k), CADICAL_assert (k <= w.clause->end ());
w.clause->pos = k - lits;
CADICAL_assert (v > 0);
LOG (w.clause, "unwatch %d in", lit);
lits[0] = other;
lits[1] = r;
*k = lit;
watch_literal (r, lit, w.clause);
j--;
}
if (j != i) {
while (i != eow)
*j++ = *i++;
ws.resize (j - ws.begin ());
}
}
}
} // namespace CaDiCaL
ABC_NAMESPACE_IMPL_END

View File

@ -1,96 +0,0 @@
#include "global.h"
#include "internal.hpp"
ABC_NAMESPACE_IMPL_START
namespace CaDiCaL {
// Slightly different than 'bump_variable' since the variable is not
// enqueued at all.
inline void Internal::init_enqueue (int idx) {
Link &l = links[idx];
if (opts.reverse) {
l.prev = 0;
if (queue.first) {
CADICAL_assert (!links[queue.first].prev);
links[queue.first].prev = idx;
btab[idx] = btab[queue.first] - 1;
} else {
CADICAL_assert (!queue.last);
queue.last = idx;
btab[idx] = 0;
}
CADICAL_assert (btab[idx] <= stats.bumped);
l.next = queue.first;
queue.first = idx;
if (!queue.unassigned)
update_queue_unassigned (queue.last);
} else {
l.next = 0;
if (queue.last) {
CADICAL_assert (!links[queue.last].next);
links[queue.last].next = idx;
} else {
CADICAL_assert (!queue.first);
queue.first = idx;
}
btab[idx] = ++stats.bumped;
l.prev = queue.last;
queue.last = idx;
update_queue_unassigned (queue.last);
}
}
// Initialize VMTF queue from current 'old_max_var + 1' to 'new_max_var'.
// This incorporates an initial variable order. We currently simply assume
// that variables with smaller index are more important. This is the same
// as in MiniSAT (implicitly) and also matches the 'scores' initialization.
//
void Internal::init_queue (int old_max_var, int new_max_var) {
LOG ("initializing VMTF queue from %d to %d", old_max_var + 1,
new_max_var);
CADICAL_assert (old_max_var < new_max_var);
// New variables can be created that can invoke enlarge anytime (eg via
// calls during ipasir-up call-backs), thus assuming (!level) is not
// correct
for (int idx = old_max_var; idx < new_max_var; idx++)
init_enqueue (idx + 1);
}
// Shuffle the VMTF queue.
void Internal::shuffle_queue () {
if (!opts.shuffle)
return;
if (!opts.shufflequeue)
return;
stats.shuffled++;
LOG ("shuffling queue");
vector<int> shuffle;
if (opts.shufflerandom) {
for (int idx = max_var; idx; idx--)
shuffle.push_back (idx);
Random random (opts.seed); // global seed
random += stats.shuffled; // different every time
for (int i = 0; i <= max_var - 2; i++) {
const int j = random.pick_int (i, max_var - 1);
swap (shuffle[i], shuffle[j]);
}
} else {
for (int idx = queue.last; idx; idx = links[idx].prev)
shuffle.push_back (idx);
}
queue.first = queue.last = 0;
for (const int idx : shuffle)
queue.enqueue (links, idx);
int64_t bumped = queue.bumped;
for (int idx = queue.last; idx; idx = links[idx].prev)
btab[idx] = bumped--;
queue.unassigned = queue.last;
}
} // namespace CaDiCaL
ABC_NAMESPACE_IMPL_END

View File

@ -1,233 +0,0 @@
#include "global.h"
#include "internal.hpp"
/*------------------------------------------------------------------------*/
// Our random number generator is seeded by default (i.e., in the default
// constructor) with random seeds, which should be unique across machines,
// processes and time. This makes this code below rather operating system
// dependent. We also use in essence defensive programming, overlaying
// several methods to get randomness since in the past we were bitten a
// couple of times (and got the same seeds). Having several methods makes
// it also simpler to port randomly initializing seeds to different
// operating systems (even though currently it is only tested on Linux).
// This functionality is only used in the 'Mobical' model based tester at
// this point, since the main solver explicitly sets a random seed ('0' by
// default in 'options.hpp') and also currently only uses this seed in the
// local search procedure explicitly without using the default constructor.
// It is crucial for 'Mobical' to make sure that concurrent runs are really
// independent.
/*------------------------------------------------------------------------*/
// Uncomment the following definition to force printing the computed hash
// values for individual machine and process properties. This is only needed
// for testing, porting and debugging different ports of this seeding and
// hashing functions (uncomment and run 'mobical' for instance).
/*
#define DO_PRINT_HASH
*/
#ifdef DO_PRINT_HASH
#define PRINT_HASH(H) \
do { \
printf ("c PRINT_HASH %32s () = %020" PRIu64 "\n", __func__, H); \
fflush (stdout); \
} while (0)
#else
#define PRINT_HASH(...) \
do { \
} while (0)
#endif
/*------------------------------------------------------------------------*/
// This is Linux specific but if '/var/lib/dbus/machine-id' does not exist
// does not have any effect. TODO: add a similar machine identity hashing
// function for other operating systems (Windows and macOS).
ABC_NAMESPACE_IMPL_START
namespace CaDiCaL {
static uint64_t hash_machine_identifier () {
FILE *file = fopen ("/var/lib/dbus/machine-id", "r");
uint64_t res = 0;
if (file) {
char buffer[128];
memset (buffer, 0, sizeof buffer);
size_t bytes = fread (buffer, 1, sizeof buffer - 1, file);
CADICAL_assert (bytes);
fclose (file);
if (bytes && bytes < sizeof buffer) {
buffer[bytes] = 0;
res = hash_string (buffer);
}
}
PRINT_HASH (res);
return res;
}
} // namespace CaDiCaL
ABC_NAMESPACE_IMPL_END
/*------------------------------------------------------------------------*/
// On our Linux cluster where we used an NFS mounted root disk the
// 'machine-id' above (even on a locally mounted '/var' disk on each node)
// was copied from '/etc/machine-id' which was shared among all nodes
// (before figuring this out and fixing it). Thus the main idea of getting
// different hash values through this machine identifier machines did not
// work. As an additional measure to increase the possibility to get
// different seeds we are now also using network addresses (explicitly).
#ifndef WIN32
extern "C" {
#include <ifaddrs.h>
#include <netdb.h>
#include <netinet/in.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/socket.h>
}
#endif
ABC_NAMESPACE_IMPL_START
namespace CaDiCaL {
static uint64_t hash_network_addresses () {
uint64_t res = 0;
// We still need to properly port this to Windows, but since accessing the
// IP address is only required for better randomization during testing
// (running 'mobical' on a cluster for instance) it is not crucial unless
// you really need to run 'mobical' on a Windows cluster where each node
// has identical IP addresses.
#ifndef WIN32
struct ifaddrs *addrs;
if (!getifaddrs (&addrs)) {
for (struct ifaddrs *addr = addrs; addr; addr = addr->ifa_next) {
if (!addr->ifa_addr)
continue;
const int family = addr->ifa_addr->sa_family;
if (family == AF_INET || family == AF_INET6) {
const int size = (family == AF_INET) ? sizeof (struct sockaddr_in)
: sizeof (struct sockaddr_in6);
char buffer[128];
if (!getnameinfo (addr->ifa_addr, size, buffer, sizeof buffer, 0, 0,
NI_NUMERICHOST)) {
uint64_t tmp = hash_string (buffer);
#ifdef DO_PRINT_HASH
printf ("c PRINT_HASH %35s = %020" PRIu64 "\n", buffer, tmp);
fflush (stdout);
#endif
res ^= tmp;
res *= 10000000000000000051ul;
}
}
}
freeifaddrs (addrs);
}
#endif
PRINT_HASH (res);
return res;
}
} // namespace CaDiCaL
ABC_NAMESPACE_IMPL_END
/*------------------------------------------------------------------------*/
// Hash the current wall-clock time in seconds.
extern "C" {
#include <time.h>
}
ABC_NAMESPACE_IMPL_START
namespace CaDiCaL {
static uint64_t hash_time () {
uint64_t res = ::time (0);
PRINT_HASH (res);
return res;
}
} // namespace CaDiCaL
ABC_NAMESPACE_IMPL_END
/*------------------------------------------------------------------------*/
// Hash the process identified.
extern "C" {
#include <sys/types.h>
#ifdef WIN32
#include <process.h>
#else
#include <unistd.h>
#endif
}
ABC_NAMESPACE_IMPL_START
namespace CaDiCaL {
static uint64_t hash_process () {
uint64_t res = getpid ();
PRINT_HASH (res);
return res;
}
} // namespace CaDiCaL
ABC_NAMESPACE_IMPL_END
/*------------------------------------------------------------------------*/
// Hash the current number of clock cycles.
#include <ctime>
ABC_NAMESPACE_IMPL_START
namespace CaDiCaL {
static uint64_t hash_clock_cycles () {
uint64_t res = std::clock ();
PRINT_HASH (res);
return res;
}
} // namespace CaDiCaL
/*------------------------------------------------------------------------*/
namespace CaDiCaL {
Random::Random () : state (1) {
add (hash_machine_identifier ());
add (hash_network_addresses ());
add (hash_clock_cycles ());
add (hash_process ());
add (hash_time ());
#ifdef DO_PRINT_HASH
printf ("c PRINT_HASH %32s = %020" PRIu64 "\n", "combined", state);
fflush (stdout);
#endif
}
} // namespace CaDiCaL
ABC_NAMESPACE_IMPL_END

View File

@ -1,142 +0,0 @@
#include "global.h"
#include "reap.hpp"
#include <cassert>
#include <climits>
#include <cstring>
#ifdef _MSC_VER
#include <intrin.h>
static inline int __builtin_clz(unsigned x) {
unsigned long r;
_BitScanReverse(&r, x);
return (int)(r ^ 31);
}
#endif
ABC_NAMESPACE_IMPL_START
void Reap::init () {
for (auto &bucket : buckets)
bucket = {0};
CADICAL_assert (!num_elements);
CADICAL_assert (!last_deleted);
min_bucket = 32;
CADICAL_assert (!max_bucket);
}
void Reap::release () {
num_elements = 0;
last_deleted = 0;
min_bucket = 32;
max_bucket = 0;
}
Reap::Reap () {
num_elements = 0;
last_deleted = 0;
min_bucket = 32;
max_bucket = 0;
}
static inline unsigned leading_zeroes_of_unsigned (unsigned x) {
return x ? __builtin_clz (x) : sizeof (unsigned) * 8;
}
void Reap::push (unsigned e) {
CADICAL_assert (last_deleted <= e);
const unsigned diff = e ^ last_deleted;
const unsigned bucket = 32 - leading_zeroes_of_unsigned (diff);
buckets[bucket].push_back (e);
if (min_bucket > bucket)
min_bucket = bucket;
if (max_bucket < bucket)
max_bucket = bucket;
CADICAL_assert (num_elements != UINT_MAX);
num_elements++;
}
unsigned Reap::pop () {
CADICAL_assert (num_elements > 0);
unsigned i = min_bucket;
for (;;) {
CADICAL_assert (i < 33);
CADICAL_assert (i <= max_bucket);
std::vector<unsigned> &s = buckets[i];
if (s.empty ()) {
min_bucket = ++i;
continue;
}
unsigned res;
if (i) {
res = UINT_MAX;
const auto begin = std::begin (s);
const auto end = std::end (s);
auto q = std::begin (s);
CADICAL_assert (begin < end);
for (auto p = begin; p != end; ++p) {
const unsigned tmp = *p;
if (tmp >= res)
continue;
res = tmp;
q = p;
}
for (auto p = begin; p != end; ++p) {
if (p == q)
continue;
const unsigned other = *p;
const unsigned diff = other ^ res;
CADICAL_assert (sizeof (unsigned) == 4);
const unsigned j = 32 - leading_zeroes_of_unsigned (diff);
CADICAL_assert (j < i);
buckets[j].push_back (other);
if (min_bucket > j)
min_bucket = j;
}
s.clear ();
if (i && max_bucket == i) {
#ifndef CADICAL_NDEBUG
for (unsigned j = i + 1; j < 33; j++)
CADICAL_assert (buckets[j].empty ());
#endif
if (s.empty ())
max_bucket = i - 1;
}
} else {
res = last_deleted;
CADICAL_assert (!buckets[0].empty ());
CADICAL_assert (buckets[0].at (0) == res);
buckets[0].pop_back ();
}
if (min_bucket == i) {
#ifndef CADICAL_NDEBUG
for (unsigned j = 0; j < i; j++)
CADICAL_assert (buckets[j].empty ());
#endif
if (s.empty ())
min_bucket = std::min ((int) (i + 1), 32);
}
--num_elements;
CADICAL_assert (last_deleted <= res);
last_deleted = res;
return res;
}
}
void Reap::clear () {
CADICAL_assert (max_bucket <= 32);
for (unsigned i = 0; i < 33; i++)
buckets[i].clear ();
num_elements = 0;
last_deleted = 0;
min_bucket = 32;
max_bucket = 0;
}
ABC_NAMESPACE_IMPL_END

View File

@ -1,284 +0,0 @@
#include "global.h"
#include "internal.hpp"
ABC_NAMESPACE_IMPL_START
namespace CaDiCaL {
/*------------------------------------------------------------------------*/
// Once in a while we reduce, e.g., we remove learned clauses which are
// supposed to be less useful in the future. This is done in increasing
// intervals, which has the effect of allowing more and more learned clause
// to be kept for a longer period. The number of learned clauses kept
// in memory corresponds to an upper bound on the 'space' of a resolution
// proof needed to refute a formula in proof complexity sense.
bool Internal::reducing () {
if (!opts.reduce)
return false;
if (!stats.current.redundant)
return false;
return stats.conflicts >= lim.reduce;
}
/*------------------------------------------------------------------------*/
// Even less regularly we are flushing all redundant clauses.
bool Internal::flushing () {
if (!opts.flush)
return false;
return stats.conflicts >= lim.flush;
}
/*------------------------------------------------------------------------*/
void Internal::mark_clauses_to_be_flushed () {
const int tier1limit = tier1[false];
const int tier2limit = max (tier1limit, tier2[false]);
for (const auto &c : clauses) {
if (!c->redundant)
continue; // keep irredundant
if (c->garbage)
continue; // already marked as garbage
if (c->reason)
continue; // need to keep reasons
const unsigned used = c->used;
if (used)
c->used--;
if (c->glue < tier1limit && used)
continue;
if (c->glue < tier2limit && used >= max_used - 1)
continue;
mark_garbage (c); // flush unused clauses
if (c->hyper)
stats.flush.hyper++;
else
stats.flush.learned++;
}
// No change to 'lim.kept{size,glue}'.
}
/*------------------------------------------------------------------------*/
// Clauses of larger glue or larger size are considered less useful.
//
// We also follow the observations made by the Glucose team in their
// IJCAI'09 paper and keep all low glue clauses limited by
// 'options.keepglue' (typically '2').
//
// In earlier versions we pre-computed a 64-bit sort key per clause and
// wrapped a pointer to the clause and the 64-bit sort key into a separate
// data structure for sorting. This was probably faster but awkward and
// so we moved back to a simpler scheme which also uses 'stable_sort'
// instead of 'rsort' below. Sorting here is not a hot-spot anyhow.
struct reduce_less_useful {
bool operator() (const Clause *c, const Clause *d) const {
if (c->glue > d->glue)
return true;
if (c->glue < d->glue)
return false;
return c->size > d->size;
}
};
// This function implements the important reduction policy. It determines
// which redundant clauses are considered not useful and thus will be
// collected in a subsequent garbage collection phase.
void Internal::mark_useless_redundant_clauses_as_garbage () {
// We use a separate stack for sorting candidates for removal. This uses
// (slightly) more memory but has the advantage to keep the relative order
// in 'clauses' intact, which actually due to using stable sorting goes
// into the candidate selection (more recently learned clauses are kept if
// they otherwise have the same glue and size).
vector<Clause *> stack;
const int tier1limit = tier1[false];
const int tier2limit = max (tier1limit, tier2[false]);
stack.reserve (stats.current.redundant);
for (const auto &c : clauses) {
if (!c->redundant)
continue; // Keep irredundant.
if (c->garbage)
continue; // Skip already marked.
if (c->reason)
continue; // Need to keep reasons.
const unsigned used = c->used;
if (used)
c->used--;
if (c->glue <= tier1limit && used)
continue;
if (c->glue <= tier2limit && used >= max_used - 1)
continue;
if (c->hyper) { // Hyper binary and ternary resolvents
CADICAL_assert (c->size <= 3); // are only kept for one reduce round
if (!used)
mark_garbage (c); // unless
continue; // used recently.
}
stack.push_back (c);
}
stable_sort (stack.begin (), stack.end (), reduce_less_useful ());
size_t target = 1e-2 * opts.reducetarget * stack.size ();
// This is defensive code, which I usually consider a bug, but here I am
// just not sure that using floating points in the line above is precise
// in all situations and instead of figuring that out, I just use this.
//
if (target > stack.size ())
target = stack.size ();
PHASE ("reduce", stats.reductions, "reducing %zd clauses %.0f%%", target,
percent (target, stats.current.redundant));
auto i = stack.begin ();
const auto t = i + target;
while (i != t) {
Clause *c = *i++;
LOG (c, "marking useless to be collected");
mark_garbage (c);
stats.reduced++;
}
lim.keptsize = lim.keptglue = 0;
const auto end = stack.end ();
for (i = t; i != end; i++) {
Clause *c = *i;
LOG (c, "keeping");
if (c->size > lim.keptsize)
lim.keptsize = c->size;
if (c->glue > lim.keptglue)
lim.keptglue = c->glue;
}
erase_vector (stack);
PHASE ("reduce", stats.reductions, "maximum kept size %d glue %d",
lim.keptsize, lim.keptglue);
}
/*------------------------------------------------------------------------*/
// If chronological backtracking produces out-of-order assigned units, then
// it is necessary to completely propagate them at the root level in order
// to derive all implied units. Otherwise the blocking literals in
// 'flush_watches' are messed up and CADICAL_assertion 'FW1' fails.
bool Internal::propagate_out_of_order_units () {
if (!level)
return true;
int oou = 0;
for (size_t i = control[1].trail; !oou && i < trail.size (); i++) {
const int lit = trail[i];
CADICAL_assert (val (lit) > 0);
if (var (lit).level)
continue;
LOG ("found out-of-order assigned unit %d", oou);
oou = lit;
}
if (!oou)
return true;
CADICAL_assert (opts.chrono || external_prop);
backtrack (0);
if (propagate ())
return true;
learn_empty_clause ();
return false;
}
/*------------------------------------------------------------------------*/
// reduction is scheduled with reduceint, reducetarget and reduceopt.
// with reduceopt=1 the number of learnt clauses scale with
// sqrt of conflicts times reduceint
// the scaling is the same as with reduceopt=0 (the classical default)
// however, the constants are different. To avoid this (and get roughly the
// same behaviour with reduceopt=0 and reduceopt=1) we need to scale the
// interval, namely (reduceint^2/2)
// Lastly, reduceopt=2 just replaces sqrt conflicts with log conflicts.
// The learnt clauses should not be bigger than
// 1/reducetarget * reduceint * function (conflicts)
// for function being log if reduceint=2 an sqrt otherwise.
// This is however only the theoretical target and second chance for
// tier2 clauses and very long lifespan of tier1 clauses (through used flag)
// make this behave differently.
// reduceinit shifts the curve to the right, increasing the number of
// clauses in the solver. This impact will decrease over time.
void Internal::reduce () {
START (reduce);
stats.reductions++;
report ('.', 1);
bool flush = flushing ();
if (flush)
stats.flush.count++;
if (!propagate_out_of_order_units ())
goto DONE;
mark_satisfied_clauses_as_garbage ();
protect_reasons ();
if (flush)
mark_clauses_to_be_flushed ();
else
mark_useless_redundant_clauses_as_garbage ();
garbage_collection ();
{
int64_t delta = opts.reduceint;
double factor = stats.reductions + 1;
if (opts.reduceopt ==
0) // adjust delta such this is the same as reduceopt=1
delta = delta * delta / 2;
else if (opts.reduceopt == 1) {
// this is the same as reduceopt=0 if reduceint = sqrt (reduceint) =
// 17
factor = sqrt ((double) stats.conflicts);
} else if (opts.reduceopt == 2)
// log scaling instead
factor = log ((double) stats.conflicts);
if (factor < 1)
factor = 1;
delta = delta * factor;
if (irredundant () > 1e5) {
delta *= log (irredundant () / 1e4) / log (10);
}
if (delta < 1)
delta = 1;
lim.reduce = stats.conflicts + delta;
PHASE ("reduce", stats.reductions,
"new reduce limit %" PRId64 " after %" PRId64 " conflicts",
lim.reduce, delta);
}
if (flush) {
PHASE ("flush", stats.flush.count, "new flush increment %" PRId64 "",
inc.flush);
inc.flush *= opts.flushfactor;
lim.flush = stats.conflicts + inc.flush;
PHASE ("flush", stats.flush.count, "new flush limit %" PRId64 "",
lim.flush);
}
last.reduce.conflicts = stats.conflicts;
DONE:
report (flush ? 'f' : '-');
STOP (reduce);
}
} // namespace CaDiCaL
ABC_NAMESPACE_IMPL_END

View File

@ -1,342 +0,0 @@
#include "global.h"
#include "internal.hpp"
ABC_NAMESPACE_IMPL_START
namespace CaDiCaL {
/*------------------------------------------------------------------------*/
// We experimented with resetting and reinitializing the saved phase with
// many solvers. Actually RSAT had already such a scheme. Our newest
// version seems to be quite beneficial for satisfiable instances. In an
// arithmetic increasing interval in the number of conflicts we either use
// the original phase (set by the option 'phase'), its inverted value, flip
// the current phase, pick random phases, then pick the best since the last
// time a best phase was picked and finally also use local search to find
// phases which minimize the number of falsified clauses. During
// stabilization (see 'stabilizing' in 'restart.cpp' when 'stable' is true)
// we execute a different rephasing schedule. The same applies if local
// search is disabled.
/*------------------------------------------------------------------------*/
bool Internal::rephasing () {
if (!opts.rephase)
return false;
if (opts.forcephase)
return false;
return stats.conflicts > lim.rephase;
}
/*------------------------------------------------------------------------*/
// Pick the original default phase.
char Internal::rephase_original () {
stats.rephased.original++;
signed char val = opts.phase ? 1 : -1; // original = initial
PHASE ("rephase", stats.rephased.total, "switching to original phase %d",
val);
for (auto idx : vars)
phases.saved[idx] = val;
return 'O';
}
// Pick the inverted original default phase.
char Internal::rephase_inverted () {
stats.rephased.inverted++;
signed char val = opts.phase ? -1 : 1; // original = -initial
PHASE ("rephase", stats.rephased.total,
"switching to inverted original phase %d", val);
for (auto idx : vars)
phases.saved[idx] = val;
return 'I';
}
// Flip the current phase.
char Internal::rephase_flipping () {
stats.rephased.flipped++;
PHASE ("rephase", stats.rephased.total,
"flipping all phases individually");
for (auto idx : vars)
phases.saved[idx] *= -1;
return 'F';
}
// Complete random picking of phases.
char Internal::rephase_random () {
stats.rephased.random++;
PHASE ("rephase", stats.rephased.total, "resetting all phases randomly");
Random random (opts.seed); // global seed
random += stats.rephased.random; // different every time
for (auto idx : vars)
phases.saved[idx] = random.generate_bool () ? -1 : 1;
return '#';
}
// Best phases are those saved at the largest trail height without conflict.
// See code and comments in 'update_target_and_best' in 'backtrack.cpp'
char Internal::rephase_best () {
stats.rephased.best++;
PHASE ("rephase", stats.rephased.total,
"overwriting saved phases by best phases");
signed char val;
for (auto idx : vars)
if ((val = phases.best[idx]))
phases.saved[idx] = val;
return 'B';
}
// Trigger local search 'walk' in 'walk.cpp'.
char Internal::rephase_walk () {
stats.rephased.walk++;
PHASE ("rephase", stats.rephased.total,
"starting local search to improve current phase");
walk ();
return 'W';
}
/*------------------------------------------------------------------------*/
void Internal::rephase () {
stats.rephased.total++;
PHASE ("rephase", stats.rephased.total,
"reached rephase limit %" PRId64 " after %" PRId64 " conflicts",
lim.rephase, 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
// the next 'update_target_and_best' called from the next 'backtrack'.
//
report ('~', 1);
backtrack ();
clear_phases (phases.target);
target_assigned = 0;
size_t count = lim.rephased[stable]++;
bool single;
char type;
if (opts.stabilize && opts.stabilizeonly)
single = true;
else
single = !opts.stabilize;
if (single && !opts.walk) {
// (inverted,best,flipping,best,random,best,original,best)^\omega
switch (count % 8) {
case 0:
type = rephase_inverted ();
break;
case 1:
type = rephase_best ();
break;
case 2:
type = rephase_flipping ();
break;
case 3:
type = rephase_best ();
break;
case 4:
type = rephase_random ();
break;
case 5:
type = rephase_best ();
break;
case 6:
type = rephase_original ();
break;
case 7:
type = rephase_best ();
break;
default:
type = 0;
break;
}
} else if (single && 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)
type = rephase_original ();
else if (count == 1)
type = rephase_inverted ();
else
switch ((count - 2) % 4) {
case 0:
type = rephase_best ();
break;
case 1:
type = rephase_original ();
break;
case 2:
type = rephase_best ();
break;
case 3:
type = rephase_inverted ();
break;
default:
type = 0;
break;
}
} else if (stable && opts.walk) {
// original,inverted,(best,walk,original,best,walk,inverted)^\omega
if (!count)
type = rephase_original ();
else if (count == 1)
type = rephase_inverted ();
else
switch ((count - 2) % 6) {
case 0:
type = rephase_best ();
break;
case 1:
type = rephase_walk ();
break;
case 2:
type = rephase_original ();
break;
case 3:
type = rephase_best ();
break;
case 4:
type = rephase_walk ();
break;
case 5:
type = rephase_inverted ();
break;
default:
type = 0;
break;
}
} else if (!stable && (!opts.walk || !opts.walknonstable)) {
// flipping,(random,best,flipping,best)^\omega
if (!count)
type = rephase_flipping ();
else
switch ((count - 1) % 4) {
case 0:
type = rephase_random ();
break;
case 1:
type = rephase_best ();
break;
case 2:
type = rephase_flipping ();
break;
case 3:
type = rephase_best ();
break;
default:
type = 0;
break;
}
} else {
CADICAL_assert (!stable && opts.walk && opts.walknonstable);
// flipping,(random,best,walk,flipping,best,walk)^\omega
if (!count)
type = rephase_flipping ();
else
switch ((count - 1) % 6) {
case 0:
type = rephase_random ();
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;
default:
type = 0;
break;
}
}
CADICAL_assert (type);
int64_t delta = opts.rephaseint * (stats.rephased.total + 1);
lim.rephase = stats.conflicts + delta;
PHASE ("rephase", stats.rephased.total,
"new rephase limit %" PRId64 " after %" PRId64 " conflicts",
lim.rephase, delta);
// This will trigger to report the effect of this new set of phases at the
// 'backtrack' (actually 'update_target_and_best') after the next
// conflict, as well as resetting 'best_assigned' then to allow to compute
// a new "best" assignment at that point.
//
last.rephase.conflicts = stats.conflicts;
rephased = type;
if (stable)
shuffle_scores ();
else
shuffle_queue ();
}
} // namespace CaDiCaL
ABC_NAMESPACE_IMPL_END

View File

@ -1,302 +0,0 @@
#include "global.h"
#include "internal.hpp"
ABC_NAMESPACE_IMPL_START
namespace CaDiCaL {
#ifndef CADICAL_QUIET
/*------------------------------------------------------------------------*/
// Provide nicely formatted progress report messages while running through
// the 'report' function below. The code is so complex, because it should
// be easy to add and remove reporting of certain statistics, while still
// providing a nicely looking format with automatically aligned headers.
/*------------------------------------------------------------------------*\
The 'reports' are shown as 'c <char> ...' with '<char>' as follows:
i propagated learned unit clause
O backtracked after phases reset to original phase
F backtracked after flipping phases
# backtracked after randomly setting phases
B backtracked after resetting to best phases
W backtracked after local search improved phases
b blocked clause elimination
G before garbage collection
C after garbage collection
/ compacted internal literals and remapped external to internal
c covered clause elimination
d decomposed binary implication graph and substituted equivalent literals
2 removed duplicated binary clauses
e bounded variable elimination round
^ variable elimination bound increased
I variable instantiation
[ start of stable search phase
] end of stable search phase
{ start of unstable search phase
} end of unstable search phase
P preprocessing round (capital 'P')
L local search round
* start of solving without the need to restore clauses
+ start of solving before restoring clauses
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
l lucky phase solving
p failed literal probing round (lower case 'p')
. before reducing redundant clauses
f flushed redundant clauses
- reduced redundant clauses
~ start of resetting phases
R restart
s subsumed clause removal round
3 ternary resolution round
t transition reduction of binary implication graph
u vivified tier1 clauses
v vivified tier2 clauses
x vivified tier3 clauses
w vivified irredundant clauses
The order of the list follows the occurrences of 'report' in the source
files, i.e., obtained from "grep 'report (' *.cpp". Note that some of the
reports are only printed for higher verbosity level (for instance 'R').
\*------------------------------------------------------------------------*/
struct Report {
const char *header;
char buffer[32];
int pos;
Report (const char *h, int precision, int min, double value);
Report () {}
void print_header (char *line);
};
/*------------------------------------------------------------------------*/
void Report::print_header (char *line) {
int len = strlen (header);
for (int i = -1, j = pos - (len + 1) / 2 - 3; i < len; i++, j++)
line[j] = i < 0 ? ' ' : header[i];
}
Report::Report (const char *h, int precision, int min, double value)
: header (h) {
char fmt[32];
if (precision < 0)
snprintf (fmt, sizeof fmt, "%%.%df", -precision - 1);
else
snprintf (fmt, sizeof fmt, "%%.%df", precision);
snprintf (buffer, sizeof buffer, fmt, value);
const int width = strlen (buffer);
if (precision < 0)
strcat (buffer, "%");
if (width >= min)
return;
if (precision < 0)
snprintf (fmt, sizeof fmt, "%%%d.%df%%%%", min, -precision - 1);
else
snprintf (fmt, sizeof fmt, "%%%d.%df", min, precision);
snprintf (buffer, sizeof buffer, fmt, value);
}
/*------------------------------------------------------------------------*/
// The following statistics are printed in columns, whenever 'report' is
// called. For instance 'reduce' with prefix '-' will call it. The other
// more interesting report is due to learning a unit, called iteration, with
// prefix 'i'. To add another statistics column, add a corresponding line
// here. If you want to report something else add 'report (..)' functions.
#define TIME opts.reportsolve ? solve_time () : time ()
#define MB (current_resident_set_size () / (double) (1l << 20))
#define REMAINING (percent (active (), stats.variables_original))
#define TRAIL (percent (averages.current.trail.slow, max_var))
#define TARGET (percent (target_assigned, max_var))
#define BEST (percent (best_assigned, max_var))
#define REPORTS \
/* HEADER, PRECISION, MIN, VALUE */ \
REPORT ("seconds", 2, 5, TIME) \
REPORT ("MB", 0, 2, MB) \
REPORT ("level", 0, 2, averages.current.level) \
REPORT ("reductions", 0, 1, stats.reductions) \
REPORT ("restarts", 0, 3, stats.restarts) \
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 ("glue", 0, 1, averages.current.glue.slow) \
REPORT ("irredundant", 0, 4, stats.current.irredundant) \
REPORT ("variables", 0, 3, active ()) \
REPORT ("remaining", -1, 2, REMAINING)
// Note, keep an empty line before this line (because of '\')!
#if 0 // ADDITIONAL STATISTICS TO REPORT
REPORT("best", -1, 2, BEST) \
REPORT("target", -1, 2, TARGET) \
REPORT("maxvar", 0, 2, external->max_var)
#endif
static const int num_reports = // as compile time constant
#define REPORT(HEAD, PREC, MIN, EXPR) 1 +
REPORTS
#undef REPORT
0;
/*------------------------------------------------------------------------*/
void Internal::report (char type, int verbose) {
if (!opts.report)
return;
#ifdef LOGGING
if (!opts.log)
#endif
if (opts.quiet || (verbose > opts.verbose))
return;
if (!reported) {
CADICAL_assert (!lim.report);
reported = true;
MSG ("%stime measured in %s time %s%s", tout.magenta_code (),
internal->opts.realtime ? "real" : "process",
internal->opts.reportsolve ? "in solving" : "since initialization",
tout.normal_code ());
}
Report reports[num_reports];
int n = 0;
#define REPORT(HEAD, PREC, MIN, EXPR) \
CADICAL_assert (n < num_reports); \
reports[n++] = Report (HEAD, PREC, MIN, (double) (EXPR));
REPORTS
#undef REPORT
if (!lim.report) {
print_prefix ();
fputc ('\n', stdout);
int pos = 4;
for (int i = 0; i < n; i++) {
int len = strlen (reports[i].buffer);
reports[i].pos = pos + (len + 1) / 2;
pos += len + 1;
}
const int max_line = pos + 20, nrows = 3;
char *line = new char[max_line];
for (int start = 0; start < nrows; start++) {
int i;
for (i = 0; i < max_line; i++)
line[i] = ' ';
for (i = start; i < n; i += nrows)
reports[i].print_header (line);
for (i = max_line - 1; line[i - 1] == ' '; i--)
;
line[i] = 0;
print_prefix ();
tout.yellow ();
fputs (line, stdout);
tout.normal ();
fputc ('\n', stdout);
}
print_prefix ();
fputc ('\n', stdout);
delete[] line;
lim.report = 19;
} else
lim.report--;
print_prefix ();
switch (type) {
case '[':
case ']':
tout.magenta (true);
break;
case 's':
case 'b':
case 'c':
tout.green (false);
break;
case 'e':
tout.green (true);
break;
case 'p':
case '2':
case '3':
case 'u':
case 'v':
case 'w':
case 'x':
case 'f':
case '=':
tout.blue (false);
break;
case 't':
tout.cyan (false);
break;
case 'd':
tout.blue (true);
break;
case 'z':
case '!':
tout.cyan (true);
break;
case '-':
tout.normal ();
break;
case '/':
tout.yellow (true);
break;
case 'a':
case 'n':
tout.red (false);
break;
case '0':
case '1':
case '?':
case 'i':
tout.bold ();
break;
case 'L':
case 'P':
tout.bold ();
tout.underline ();
break;
default:
break;
}
fputc (type, stdout);
if (stable || type == ']')
tout.magenta ();
else if (type != 'L' && type != 'P')
tout.normal ();
for (int i = 0; i < n; i++) {
fputc (' ', stdout);
fputs (reports[i].buffer, stdout);
}
if (stable || type == 'L' || type == 'P' || type == ']')
tout.normal ();
fputc ('\n', stdout);
fflush (stdout);
}
#else // ifndef CADICAL_QUIET
void Internal::report (char, int) {}
#endif
} // namespace CaDiCaL
ABC_NAMESPACE_IMPL_END

View File

@ -1,166 +0,0 @@
#include "global.h"
#include "internal.hpp"
/*------------------------------------------------------------------------*/
// This is operating system specific code. We mostly develop on Linux and
// there it should be fine and mostly works out-of-the-box on MacOS too but
// Windows needs special treatment (as probably other operating systems
// too).
extern "C" {
#ifdef WIN32
#ifndef __WIN32_WINNT
#define __WIN32_WINNT 0x0600
#endif
// Clang-format would reorder the includes which breaks the Windows code
// as it expects 'windows.h' to be included first. So disable it here.
// clang-format off
#include <windows.h>
#include <psapi.h>
// clang-format on
#else
#include <sys/resource.h>
#include <sys/time.h>
#include <sys/types.h>
#include <unistd.h>
#endif
#include <string.h>
}
ABC_NAMESPACE_IMPL_START
namespace CaDiCaL {
/*------------------------------------------------------------------------*/
#ifdef WIN32
double absolute_real_time () {
FILETIME f;
GetSystemTimeAsFileTime (&f);
ULARGE_INTEGER t;
t.LowPart = f.dwLowDateTime;
t.HighPart = f.dwHighDateTime;
double res = (__int64) t.QuadPart;
res *= 1e-7;
return res;
}
double absolute_process_time () {
double res = 0;
FILETIME fc, fe, fu, fs;
if (GetProcessTimes (GetCurrentProcess (), &fc, &fe, &fu, &fs)) {
ULARGE_INTEGER u, s;
u.LowPart = fu.dwLowDateTime;
u.HighPart = fu.dwHighDateTime;
s.LowPart = fs.dwLowDateTime;
s.HighPart = fs.dwHighDateTime;
res = (__int64) u.QuadPart + (__int64) s.QuadPart;
res *= 1e-7;
}
return res;
}
#else
double absolute_real_time () {
struct timeval tv;
if (gettimeofday (&tv, 0))
return 0;
return 1e-6 * tv.tv_usec + tv.tv_sec;
}
// We use 'getrusage' for 'process_time' and 'maximum_resident_set_size'
// which is pretty standard on Unix but probably not available on Windows
// etc. For different variants of Unix not all fields are meaningful.
double absolute_process_time () {
double res;
struct rusage u;
if (getrusage (RUSAGE_SELF, &u))
return 0;
res = u.ru_utime.tv_sec + 1e-6 * u.ru_utime.tv_usec; // user time
res += u.ru_stime.tv_sec + 1e-6 * u.ru_stime.tv_usec; // + system time
return res;
}
#endif
double Internal::real_time () const {
return absolute_real_time () - stats.time.real;
}
double Internal::process_time () const {
return absolute_process_time () - stats.time.process;
}
/*------------------------------------------------------------------------*/
#ifdef WIN32
uint64_t current_resident_set_size () {
PROCESS_MEMORY_COUNTERS pmc;
if (GetProcessMemoryInfo (GetCurrentProcess (), &pmc, sizeof (pmc))) {
return pmc.WorkingSetSize;
} else
return 0;
}
uint64_t maximum_resident_set_size () {
PROCESS_MEMORY_COUNTERS pmc;
if (GetProcessMemoryInfo (GetCurrentProcess (), &pmc, sizeof (pmc))) {
return pmc.PeakWorkingSetSize;
} else
return 0;
}
#else
// This seems to work on Linux (man page says since Linux 2.6.32).
uint64_t maximum_resident_set_size () {
struct rusage u;
if (getrusage (RUSAGE_SELF, &u))
return 0;
return ((uint64_t) u.ru_maxrss) << 10;
}
// Unfortunately 'getrusage' on Linux does not support current resident set
// size (the field 'ru_ixrss' is there but according to the man page
// 'unused'). Thus we fall back to use the '/proc' file system instead. So
// this is not portable at all and needs to be replaced on other systems
// The code would still compile though (assuming 'sysconf' and
// '_SC_PAGESIZE' are available).
uint64_t current_resident_set_size () {
char path[64];
snprintf (path, sizeof path, "/proc/%" PRId64 "/statm",
(int64_t) getpid ());
FILE *file = fopen (path, "r");
if (!file)
return 0;
uint64_t dummy, rss;
int scanned = fscanf (file, "%" PRIu64 " %" PRIu64 "", &dummy, &rss);
fclose (file);
return scanned == 2 ? rss * sysconf (_SC_PAGESIZE) : 0;
}
#endif
/*------------------------------------------------------------------------*/
} // namespace CaDiCaL
ABC_NAMESPACE_IMPL_END

View File

@ -1,165 +0,0 @@
#include "global.h"
#include "internal.hpp"
ABC_NAMESPACE_IMPL_START
namespace CaDiCaL {
// As observed by Chanseok Oh and implemented in MapleSAT solvers too,
// various mostly satisfiable instances benefit from long quiet phases
// with less or almost no restarts. We implement this idea by prohibiting
// the Glucose style restart scheme in a geometric fashion, which is very
// similar to how originally restarts were scheduled in MiniSAT and earlier
// solvers. We start with say 1e3 = 1000 (opts.stabilizeinit) conflicts of
// Glucose restarts. Then in a "stabilizing" phase we disable these
// until 1e4 = 2000 conflicts (if 'opts.stabilizefactor' is '200' percent)
// have passed. After that we switch back to regular Glucose style restarts
// until again 2 times more conflicts than the previous limit are reached.
// Actually, in the latest version we still restarts during stabilization
// but only in a reluctant doubling scheme with a rather high interval.
bool Internal::stabilizing () {
if (!opts.stabilize)
return false;
if (stable && opts.stabilizeonly)
return true;
if (!inc.stabilize) {
CADICAL_assert (!stable);
if (stats.conflicts <= lim.stabilize)
return false;
} else if (stats.ticks.search[stable] <= lim.stabilize)
return stable;
report (stable ? ']' : '}');
if (stable)
STOP (stable);
else
STOP (unstable);
const int64_t delta_conflicts =
stats.conflicts - last.stabilize.conflicts;
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";
PHASE ("stabilizing", stats.stabphases,
"reached %s stabilization limit %" PRId64 " after %" PRId64
" conflicts and %" PRId64 " ticks at %" PRId64
" conflicts and %" PRId64 " ticks",
current_mode, lim.stabilize, delta_conflicts, delta_ticks,
stats.conflicts, stats.ticks.search[stable]);
if (!inc.stabilize)
inc.stabilize = delta_ticks;
if (!inc.stabilize) // rare occurence in incremental calls requiring no
// 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;
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);
else
START (unstable);
return stable;
}
// Restarts are scheduled by a variant of the Glucose scheme as presented
// in our POS'15 paper using exponential moving averages. There is a slow
// moving average of the average recent glucose level of learned clauses
// as well as a fast moving average of those glues. If the end of a base
// restart conflict interval has passed and the fast moving average is
// above a certain margin over the slow moving average then we restart.
bool Internal::restarting () {
if (!opts.restart)
return false;
if ((size_t) level < assumptions.size () + 2)
return false;
if (stabilizing ())
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);
return l <= f;
}
// This is Marijn's reuse trail idea. Instead of always backtracking to
// the top we figure out which decisions will be made again anyhow and
// only backtrack to the level of the last such decision or to the top if
// no such decision exists top (in which case we do not reuse any level).
int Internal::reuse_trail () {
const int trivial_decisions =
assumptions.size ()
// Plus 1 if the constraint is satisfied via implications of
// assumptions and a pseudo-decision level was introduced.
+ !control[assumptions.size () + 1].decision;
if (!opts.restartreusetrail)
return trivial_decisions;
int next_decision = next_decision_variable ();
CADICAL_assert (1 <= next_decision);
int res = trivial_decisions;
if (use_scores ()) {
while (res < level) {
int decision = control[res + 1].decision;
if (decision && score_smaller (this) (abs (decision), next_decision))
break;
res++;
}
} else {
int64_t limit = bumped (next_decision);
while (res < level) {
int decision = control[res + 1].decision;
if (decision && bumped (decision) < limit)
break;
res++;
}
}
int reused = res - trivial_decisions;
if (reused > 0) {
stats.reused++;
stats.reusedlevels += reused;
if (stable)
stats.reusedstable++;
}
return res;
}
void Internal::restart () {
START (restart);
stats.restarts++;
stats.restartlevels += level;
if (stable)
stats.restartstable++;
LOG ("restart %" PRId64 "", stats.restarts);
backtrack (reuse_trail ());
lim.restart = stats.conflicts + opts.restartint;
LOG ("new restart limit at %" PRId64 " conflicts", lim.restart);
report ('R', 2);
STOP (restart);
}
} // namespace CaDiCaL
ABC_NAMESPACE_IMPL_END

View File

@ -1,273 +0,0 @@
#include "global.h"
#include "internal.hpp"
ABC_NAMESPACE_IMPL_START
namespace CaDiCaL {
/*------------------------------------------------------------------------*/
// In incremental solving after a first call to 'solve' has finished and
// before calling the internal 'solve' again incrementally we have to
// restore clauses which have the negation of a literal as a witness literal
// on the extension stack, which was added as original literal in a new
// clause or in an assumption. This procedure has to be applied
// recursively, i.e., the literals of restored clauses are treated in the
// same way as literals of a new original clause.
//
// To figure out whether literals are such witnesses we have a 'witness'
// bit for each external literal, which is set in 'block', 'elim', and
// 'decompose' if a clause is pushed on the extension stack. The witness
// bits are recomputed after restoring clauses.
//
// We further mark in the external solver newly internalized external
// literals in 'add' and 'assume' since the last call to 'solve' as tainted
// if they occur negated as a witness literal on the extension stack. Then
// we go through the extension stack and restore all clauses which have a
// tainted literal (and its negation a marked as witness).
//
// Since the API contract disallows to call 'val' and 'failed' in an
// 'UNKNOWN' state. We do not have to internalize literals there.
//
// In order to have tainted literals accepted by the internal solver they
// have to be active and thus we might need to 'reactivate' them before
// restoring clauses if they are inactive. In case they have completely
// been eliminated and removed from the internal solver in 'compact', then
// we just use a new internal variable. This is performed in 'internalize'
// during marking external literals as tainted.
//
// To check that this approach is correct the external solver can maintain a
// stack of original clauses and current assumptions both in terms of
// external literals. Whenever 'solve' determines that the current
// incremental call is satisfiable we check that the (extended) witness does
// satisfy the saved original clauses, as well as all the assumptions. To
// enable these checks set 'opts.check' as well as 'opts.checkwitness' and
// 'opts.checkassumptions' all to 'true'. The model based tester actually
// prefers to enable the 'opts.check' option and the other two are 'true' by
// default anyhow.
//
// See our SAT'19 paper [FazekasBiereScholl-SAT'19] for more details.
/*------------------------------------------------------------------------*/
void External::restore_clause (const vector<int>::const_iterator &begin,
const vector<int>::const_iterator &end,
const int64_t id) {
LOG (begin, end, "restoring external clause[%" PRId64 "]", id);
CADICAL_assert (eclause.empty ());
CADICAL_assert (id);
for (auto p = begin; p != end; p++) {
eclause.push_back (*p);
if (internal->proof && internal->lrat) {
const auto &elit = *p;
unsigned eidx = (elit > 0) + 2u * (unsigned) abs (elit);
CADICAL_assert ((size_t) eidx < ext_units.size ());
const int64_t id = ext_units[eidx];
bool added = ext_flags[abs (elit)];
if (id && !added) {
ext_flags[abs (elit)] = true;
internal->lrat_chain.push_back (id);
}
}
int ilit = internalize (*p);
internal->add_original_lit (ilit), internal->stats.restoredlits++;
}
if (internal->proof && internal->lrat) {
for (const auto &elit : eclause) {
ext_flags[abs (elit)] = false;
}
}
internal->finish_added_clause_with_id (id, true);
eclause.clear ();
internal->stats.restored++;
}
/*------------------------------------------------------------------------*/
void External::restore_clauses () {
CADICAL_assert (internal->opts.restoreall == 2 || !tainted.empty ());
START (restore);
internal->stats.restorations++;
struct {
int64_t weakened, satisfied, restored, removed;
} clauses;
memset (&clauses, 0, sizeof clauses);
if (internal->opts.restoreall && tainted.empty ())
PHASE ("restore", internal->stats.restorations,
"forced to restore all clauses");
#ifndef CADICAL_QUIET
{
unsigned numtainted = 0;
for (const auto b : tainted)
if (b)
numtainted++;
PHASE ("restore", internal->stats.restorations,
"starting with %u tainted literals %.0f%%", numtainted,
percent (numtainted, 2u * max_var));
}
#endif
auto end_of_extension = extension.end ();
auto p = extension.begin (), q = p;
// Go over all witness labelled clauses on the extension stack, restore
// those necessary, remove restored and flush satisfied clauses.
//
while (p != end_of_extension) {
clauses.weakened++;
CADICAL_assert (!*p);
const auto saved = q; // Save old start.
*q++ = *p++; // Copy zero '0'.
// Copy witness part and try to find a tainted witness literal in it.
//
int tlit = 0; // Negation tainted.
int elit;
//
CADICAL_assert (p != end_of_extension);
//
while ((elit = *q++ = *p++)) {
if (marked (tainted, -elit)) {
tlit = elit;
LOG ("negation of witness literal %d tainted", tlit);
}
CADICAL_assert (p != end_of_extension);
}
// now copy the id of the clause
const int64_t id = ((int64_t) (*p) << 32) + (int64_t) * (p + 1);
LOG ("id is %" PRId64, id);
*q++ = *p++;
*q++ = *p++;
CADICAL_assert (id);
CADICAL_assert (!*p);
*q++ = *p++;
// Now find 'end_of_clause' (clause starts at 'p') and at the same time
// figure out whether the clause is actually root level satisfied.
//
int satisfied = 0;
auto end_of_clause = p;
while (end_of_clause != end_of_extension && (elit = *end_of_clause)) {
if (!satisfied && fixed (elit) > 0)
satisfied = elit;
end_of_clause++;
}
CADICAL_assert (id);
// Do not apply our 'FLUSH' rule to remove satisfied (implied) clauses
// if the corresponding option is set simply by resetting 'satisfied'.
//
if (satisfied && !internal->opts.restoreflush) {
LOG (p, end_of_clause, "forced to not remove %d satisfied",
satisfied);
satisfied = 0;
}
if (satisfied || tlit || internal->opts.restoreall) {
if (satisfied) {
LOG (p, end_of_clause,
"flushing implied clause satisfied by %d from extension stack",
satisfied);
clauses.satisfied++;
} else {
restore_clause (p, end_of_clause, id); // Might taint literals.
clauses.restored++;
}
clauses.removed++;
p = end_of_clause;
q = saved;
} else {
LOG (p, end_of_clause, "keeping clause on extension stack");
while (p != end_of_clause) // Copy clause too.
*q++ = *p++;
}
}
extension.resize (q - extension.begin ());
shrink_vector (extension);
#ifndef CADICAL_QUIET
if (clauses.satisfied)
PHASE ("restore", internal->stats.restorations,
"removed %" PRId64 " satisfied %.0f%% of %" PRId64
" weakened clauses",
clauses.satisfied, percent (clauses.satisfied, clauses.weakened),
clauses.weakened);
else
PHASE ("restore", internal->stats.restorations,
"no satisfied clause removed out of %" PRId64
" weakened clauses",
clauses.weakened);
if (clauses.restored)
PHASE ("restore", internal->stats.restorations,
"restored %" PRId64 " clauses %.0f%% out of %" PRId64
" weakened clauses",
clauses.restored, percent (clauses.restored, clauses.weakened),
clauses.weakened);
else
PHASE ("restore", internal->stats.restorations,
"no clause restored out of %" PRId64 " weakened clauses",
clauses.weakened);
{
unsigned numtainted = 0;
for (const auto &b : tainted)
if (b)
numtainted++;
PHASE ("restore", internal->stats.restorations,
"finishing with %u tainted literals %.0f%%", numtainted,
percent (numtainted, 2u * max_var));
}
#endif
LOG ("extension stack clean");
tainted.clear ();
// Finally recompute the witness bits.
//
witness.clear ();
const auto begin_of_extension = extension.begin ();
p = extension.end ();
while (p != begin_of_extension) {
while (*--p)
CADICAL_assert (p != begin_of_extension);
int elit;
CADICAL_assert (p != begin_of_extension);
--p;
CADICAL_assert (p != begin_of_extension);
CADICAL_assert (*p || *(p - 1));
--p;
CADICAL_assert (p != begin_of_extension);
CADICAL_assert (!*p);
--p;
CADICAL_assert (p != begin_of_extension);
while ((elit = *--p)) {
mark (witness, elit);
CADICAL_assert (p != begin_of_extension);
}
}
STOP (restore);
}
} // namespace CaDiCaL
ABC_NAMESPACE_IMPL_END

View File

@ -1,57 +0,0 @@
#include "global.h"
#include "internal.hpp"
ABC_NAMESPACE_IMPL_START
namespace CaDiCaL {
// This initializes variables on the binary 'scores' heap also with
// smallest variable index first (thus picked first) and larger indices at
// the end.
//
void Internal::init_scores (int old_max_var, int new_max_var) {
LOG ("initializing EVSIDS scores from %d to %d", old_max_var + 1,
new_max_var);
for (int i = old_max_var; i < new_max_var; i++)
scores.push_back (i + 1);
}
// Shuffle the EVSIDS heap.
void Internal::shuffle_scores () {
if (!opts.shuffle)
return;
if (!opts.shufflescores)
return;
CADICAL_assert (!level);
stats.shuffled++;
LOG ("shuffling scores");
vector<int> shuffle;
if (opts.shufflerandom) {
scores.erase ();
for (int idx = max_var; idx; idx--)
shuffle.push_back (idx);
Random random (opts.seed); // global seed
random += stats.shuffled; // different every time
for (int i = 0; i <= max_var - 2; i++) {
const int j = random.pick_int (i, max_var - 1);
swap (shuffle[i], shuffle[j]);
}
} else {
while (!scores.empty ()) {
int idx = scores.front ();
(void) scores.pop_front ();
shuffle.push_back (idx);
}
}
score_inc = 0;
for (const auto &idx : shuffle) {
stab[idx] = score_inc++;
scores.push_back (idx);
}
}
} // namespace CaDiCaL
ABC_NAMESPACE_IMPL_END

View File

@ -1,513 +0,0 @@
#include "global.h"
#include "internal.hpp"
#include "reap.hpp"
ABC_NAMESPACE_IMPL_START
namespace CaDiCaL {
void Internal::reset_shrinkable () {
#ifdef LOGGING
size_t reset = 0;
#endif
for (const auto &lit : shrinkable) {
LOG ("resetting lit %i", lit);
Flags &f = flags (lit);
CADICAL_assert (f.shrinkable);
f.shrinkable = false;
#ifdef LOGGING
++reset;
#endif
}
LOG ("resetting %zu shrinkable variables", reset);
}
void Internal::mark_shrinkable_as_removable (
int blevel, std::vector<int>::size_type minimized_start) {
#ifdef LOGGING
size_t marked = 0, reset = 0;
#endif
#ifndef CADICAL_NDEBUG
unsigned kept = 0, minireset = 0;
for (; minimized_start < minimized.size (); ++minimized_start) {
const int lit = minimized[minimized_start];
Flags &f = flags (lit);
const Var &v = var (lit);
if (v.level == blevel) {
CADICAL_assert (!f.poison);
++minireset;
} else
++kept;
}
(void) kept;
(void) minireset;
#else
(void) blevel;
(void) minimized_start;
#endif
for (const int lit : shrinkable) {
Flags &f = flags (lit);
CADICAL_assert (f.shrinkable);
CADICAL_assert (!f.poison);
f.shrinkable = false;
#ifdef LOGGING
++reset;
#endif
if (f.removable)
continue;
f.removable = true;
minimized.push_back (lit);
#ifdef LOGGING
++marked;
#endif
}
LOG ("resetting %zu shrinkable variables", reset);
LOG ("marked %zu removable variables", marked);
}
int inline Internal::shrink_literal (int lit, int blevel,
unsigned max_trail) {
CADICAL_assert (val (lit) < 0);
Flags &f = flags (lit);
Var &v = var (lit);
CADICAL_assert (v.level <= blevel);
if (!v.level) {
LOG ("skipping root level assigned %d", (lit));
return 0;
}
if (v.reason == external_reason) {
CADICAL_assert (!opts.exteagerreasons);
v.reason = learn_external_reason_clause (-lit, 0, true);
if (!v.reason) {
CADICAL_assert (!v.level);
return 0;
}
}
CADICAL_assert (v.reason != external_reason);
if (f.shrinkable) {
LOG ("skipping already shrinkable literal %d", (lit));
return 0;
}
if (v.level < blevel) {
if (f.removable) {
LOG ("skipping removable thus shrinkable %d", (lit));
return 0;
}
const bool always_minimize_on_lower_blevel = (opts.shrink > 2);
if (always_minimize_on_lower_blevel && minimize_literal (-lit, 1)) {
LOG ("minimized thus shrinkable %d", (lit));
return 0;
}
LOG ("literal %d on lower blevel %u < %u not removable/shrinkable",
(lit), v.level, blevel);
return -1;
}
LOG ("marking %d as shrinkable", lit);
f.shrinkable = true;
f.poison = false;
shrinkable.push_back (lit);
if (opts.shrinkreap) {
CADICAL_assert (max_trail < trail.size ());
const unsigned dist = max_trail - v.trail;
reap.push (dist);
}
return 1;
}
unsigned Internal::shrunken_block_uip (
int uip, int blevel, std::vector<int>::reverse_iterator &rbegin_block,
std::vector<int>::reverse_iterator &rend_block,
std::vector<int>::size_type minimized_start, const int uip0) {
CADICAL_assert (clause[0] == uip0);
LOG ("UIP on level %u, uip: %i (replacing by %i)", blevel, uip, uip0);
CADICAL_assert (rend_block > rbegin_block);
CADICAL_assert (rend_block < clause.rend ());
unsigned block_shrunken = 0;
*rbegin_block = -uip;
Var &v = var (-uip);
Level &l = control[v.level];
l.seen.trail = v.trail;
l.seen.count = 1;
Flags &f = flags (-uip);
if (!f.seen) {
analyzed.push_back (-uip);
f.seen = true;
}
flags (-uip).keep = true;
for (auto p = rbegin_block + 1; p != rend_block; ++p) {
const int lit = *p;
if (lit == -uip0)
continue;
*p = uip0;
// if (lit == -uip) continue;
++block_shrunken;
CADICAL_assert (clause[0] == uip0);
}
mark_shrinkable_as_removable (blevel, minimized_start);
CADICAL_assert (clause[0] == uip0);
return block_shrunken;
}
void inline Internal::shrunken_block_no_uip (
const std::vector<int>::reverse_iterator &rbegin_block,
const std::vector<int>::reverse_iterator &rend_block,
unsigned &block_minimized, const int uip0) {
STOP (shrink);
START (minimize);
CADICAL_assert (rend_block > rbegin_block);
LOG ("no UIP found, now minimizing");
for (auto p = rbegin_block; p != rend_block; ++p) {
CADICAL_assert (p != clause.rend () - 1);
const int lit = *p;
if (opts.minimize && minimize_literal (-lit)) {
CADICAL_assert (!flags (lit).keep);
++block_minimized;
*p = uip0;
} else {
flags (lit).keep = true;
CADICAL_assert (flags (lit).keep);
}
}
STOP (minimize);
START (shrink);
}
void Internal::push_literals_of_block (
const std::vector<int>::reverse_iterator &rbegin_block,
const std::vector<int>::reverse_iterator &rend_block, int blevel,
unsigned max_trail) {
CADICAL_assert (rbegin_block < rend_block);
for (auto p = rbegin_block; p != rend_block; ++p) {
CADICAL_assert (p != clause.rend () - 1);
CADICAL_assert (!flags (*p).keep);
const int lit = *p;
LOG ("pushing lit %i of blevel %i", lit, var (lit).level);
#ifndef CADICAL_NDEBUG
int tmp =
#endif
shrink_literal (lit, blevel, max_trail);
CADICAL_assert (tmp > 0);
}
}
unsigned inline Internal::shrink_next (int blevel, unsigned &open,
unsigned &max_trail) {
const auto &t = &trail;
if (opts.shrinkreap) {
CADICAL_assert (!reap.empty ());
const unsigned dist = reap.pop ();
--open;
CADICAL_assert (dist <= max_trail);
const unsigned pos = max_trail - dist;
CADICAL_assert (pos < t->size ());
const int uip = (*t)[pos];
CADICAL_assert (val (uip) > 0);
LOG ("trying to shrink literal %d at trail[%u] and level %d", uip, pos,
blevel);
return uip;
} else {
int uip;
#ifndef CADICAL_NDEBUG
unsigned init_max_trail = max_trail;
#endif
do {
CADICAL_assert (max_trail <= init_max_trail);
uip = (*t)[max_trail--];
} while (!flags (uip).shrinkable);
--open;
LOG ("open is now %d, uip = %d, level %d", open, uip, blevel);
return uip;
}
(void) blevel;
}
unsigned inline Internal::shrink_along_reason (int uip, int blevel,
bool resolve_large_clauses,
bool &failed_ptr,
unsigned max_trail) {
LOG ("shrinking along the reason of lit %i", uip);
unsigned open = 0;
#ifndef CADICAL_NDEBUG
const Flags &f = flags (uip);
#endif
const Var &v = var (uip);
CADICAL_assert (f.shrinkable);
CADICAL_assert (v.level == blevel);
CADICAL_assert (v.reason);
if (opts.minimizeticks)
stats.ticks.search[stable]++;
if (resolve_large_clauses || v.reason->size == 2) {
const Clause &c = *v.reason;
LOG (v.reason, "resolving with reason");
for (int lit : c) {
if (lit == uip)
continue;
CADICAL_assert (val (lit) < 0);
int tmp = shrink_literal (lit, blevel, max_trail);
if (tmp < 0) {
failed_ptr = true;
break;
}
if (tmp > 0) {
++open;
}
}
} else {
failed_ptr = true;
}
return open;
}
unsigned
Internal::shrink_block (std::vector<int>::reverse_iterator &rbegin_lits,
std::vector<int>::reverse_iterator &rend_block,
int blevel, unsigned &open,
unsigned &block_minimized, const int uip0,
unsigned max_trail) {
CADICAL_assert (shrinkable.empty ());
CADICAL_assert (blevel <= this->level);
CADICAL_assert (open < clause.size ());
CADICAL_assert (rbegin_lits >= clause.rbegin ());
CADICAL_assert (rend_block < clause.rend ());
CADICAL_assert (rbegin_lits < rend_block);
CADICAL_assert (opts.shrink);
#ifdef LOGGING
LOG ("trying to shrink %u literals on level %u", open, blevel);
const auto &t = &trail;
LOG ("maximum trail position %zd on level %u", t->size (), blevel);
if (opts.shrinkreap)
LOG ("shrinking up to %u", max_trail);
#endif
const bool resolve_large_clauses = (opts.shrink > 1);
bool failed = false;
unsigned block_shrunken = 0;
std::vector<int>::size_type minimized_start = minimized.size ();
int uip = uip0;
unsigned max_trail2 = max_trail;
if (!failed) {
push_literals_of_block (rbegin_lits, rend_block, blevel, max_trail);
CADICAL_assert (!opts.shrinkreap || reap.size () == open);
CADICAL_assert (open > 0);
while (!failed) {
CADICAL_assert (!opts.shrinkreap || reap.size () == open);
uip = shrink_next (blevel, open, max_trail);
if (open == 0) {
break;
}
open += shrink_along_reason (uip, blevel, resolve_large_clauses,
failed, max_trail2);
CADICAL_assert (open >= 1);
}
if (!failed)
LOG ("shrinking found UIP %i on level %i (open: %d)", uip, blevel,
open);
else
LOG ("shrinking failed on level %i", blevel);
}
if (failed)
reset_shrinkable (), shrunken_block_no_uip (rbegin_lits, rend_block,
block_minimized, uip0);
else
block_shrunken = shrunken_block_uip (uip, blevel, rbegin_lits,
rend_block, minimized_start, uip0);
if (opts.shrinkreap)
reap.clear ();
shrinkable.clear ();
return block_shrunken;
}
// Smaller level and trail. Comparing literals on their level is necessary
// for chronological backtracking, since trail order might in this case not
// respect level order.
struct shrink_trail_negative_rank {
Internal *internal;
shrink_trail_negative_rank (Internal *s) : internal (s) {}
typedef uint64_t Type;
Type operator() (int a) {
Var &v = internal->var (a);
uint64_t res = v.level;
res <<= 32;
res |= v.trail;
return ~res;
}
};
struct shrink_trail_larger {
Internal *internal;
shrink_trail_larger (Internal *s) : internal (s) {}
bool operator() (const int &a, const int &b) const {
return shrink_trail_negative_rank (internal) (a) <
shrink_trail_negative_rank (internal) (b);
}
};
// Finds the beginning of the block (rend_block, non-included) ending at
// rend_block (included). Then tries to shrinks and minimizes literals the
// block
std::vector<int>::reverse_iterator Internal::minimize_and_shrink_block (
std::vector<int>::reverse_iterator &rbegin_block,
unsigned &total_shrunken, unsigned &total_minimized, const int uip0)
{
LOG ("shrinking block");
CADICAL_assert (rbegin_block < clause.rend () - 1);
int blevel;
unsigned open = 0;
unsigned max_trail;
// find begining of block;
std::vector<int>::reverse_iterator rend_block;
{
CADICAL_assert (rbegin_block <= clause.rend ());
const int lit = *rbegin_block;
const int idx = vidx (lit);
blevel = vtab[idx].level;
max_trail = vtab[idx].trail;
LOG ("Block at level %i (first lit: %i)", blevel, lit);
rend_block = rbegin_block;
bool finished;
do {
CADICAL_assert (rend_block < clause.rend () - 1);
const int lit = *(++rend_block);
const int idx = vidx (lit);
finished = (blevel != vtab[idx].level);
if (!finished && (unsigned) vtab[idx].trail > max_trail)
max_trail = vtab[idx].trail;
++open;
LOG (
"testing if lit %i is on the same level (of lit: %i, global: %i)",
lit, vtab[idx].level, blevel);
} while (!finished);
}
CADICAL_assert (open > 0);
CADICAL_assert (open < clause.size ());
CADICAL_assert (rbegin_block < clause.rend ());
CADICAL_assert (rend_block < clause.rend ());
unsigned block_shrunken = 0, block_minimized = 0;
if (open < 2) {
flags (*rbegin_block).keep = true;
minimized.push_back (*rbegin_block);
} else
block_shrunken = shrink_block (rbegin_block, rend_block, blevel, open,
block_minimized, uip0, max_trail);
LOG ("shrunken %u literals on level %u (including %u minimized)",
block_shrunken, blevel, block_minimized);
total_shrunken += block_shrunken;
total_minimized += block_minimized;
return rend_block;
}
void Internal::shrink_and_minimize_clause () {
CADICAL_assert (opts.minimize || opts.shrink > 0);
LOG (clause, "shrink first UIP clause");
START (shrink);
external->check_learned_clause (); // check 1st UIP learned clause first
MSORT (opts.radixsortlim, clause.begin (), clause.end (),
shrink_trail_negative_rank (this), shrink_trail_larger (this));
unsigned total_shrunken = 0;
unsigned total_minimized = 0;
LOG (clause, "shrink first UIP clause (CADICAL_asserting lit: %i)", clause[0]);
auto rend_lits = clause.rend () - 1;
auto rend_block = clause.rbegin ();
const int uip0 = clause[0];
// for direct LRAT we remember how the clause used to look
vector<int> old_clause_lrat;
CADICAL_assert (minimize_chain.empty ());
if (lrat)
for (auto &i : clause)
old_clause_lrat.push_back (i);
while (rend_block != rend_lits) {
rend_block = minimize_and_shrink_block (rend_block, total_shrunken,
total_minimized, uip0);
}
LOG (clause,
"post shrink pass (with uips, not removed) first UIP clause");
LOG (old_clause_lrat, "(used for lratdirect) before shrink: clause");
#if defined(LOGGING) || !defined(CADICAL_NDEBUG)
const unsigned old_size = clause.size ();
#endif
std::vector<int> stack;
{
std::vector<int>::size_type i = 1;
for (std::vector<int>::size_type j = 1; j < clause.size (); ++j) {
CADICAL_assert (i <= j);
clause[i] = clause[j];
if (lrat) {
CADICAL_assert (j < old_clause_lrat.size ());
CADICAL_assert (mini_chain.empty ());
if (clause[j] != old_clause_lrat[j]) {
calculate_minimize_chain (-old_clause_lrat[j], stack);
for (auto p : mini_chain) {
minimize_chain.push_back (p);
}
mini_chain.clear ();
}
}
if (clause[j] == uip0) {
continue;
}
CADICAL_assert (flags (clause[i]).keep);
++i;
LOG ("keeping literal %i", clause[j]);
}
clause.resize (i);
}
CADICAL_assert (old_size ==
(unsigned) clause.size () + total_shrunken + total_minimized);
LOG (clause, "after shrinking first UIP clause");
LOG ("clause shrunken by %zd literals (including %u minimized)",
old_size - clause.size (), total_minimized);
stats.shrunken += total_shrunken;
stats.minishrunken += total_minimized;
STOP (shrink);
START (minimize);
clear_minimized_literals ();
for (auto p = minimize_chain.rbegin (); p != minimize_chain.rend ();
p++) {
lrat_chain.push_back (*p);
}
minimize_chain.clear ();
STOP (minimize);
}
} // namespace CaDiCaL
ABC_NAMESPACE_IMPL_END

View File

@ -1,144 +0,0 @@
#include "global.h"
#include "signal.hpp"
#include "cadical.hpp"
#include "resources.hpp"
/*------------------------------------------------------------------------*/
#include <cassert>
#include <csignal>
/*------------------------------------------------------------------------*/
#ifndef WIN32
extern "C" {
#include <unistd.h>
}
#endif
ABC_NAMESPACE_IMPL_START
/*------------------------------------------------------------------------*/
// Signal handlers for printing statistics even if solver is interrupted.
namespace CaDiCaL {
static volatile bool caught_signal = false;
static Handler *signal_handler;
#ifndef WIN32
static volatile bool caught_alarm = false;
static volatile bool alarm_set = false;
static int alarm_time = -1;
void Handler::catch_alarm () { catch_signal (SIGALRM); }
#endif
#define SIGNALS \
SIGNAL (SIGABRT) \
SIGNAL (SIGINT) \
SIGNAL (SIGSEGV) \
SIGNAL (SIGTERM)
#define SIGNAL(SIG) static void (*SIG##_handler) (int);
SIGNALS
#undef SIGNAL
#ifndef WIN32
static void (*SIGALRM_handler) (int);
void Signal::reset_alarm () {
if (!alarm_set)
return;
(void) signal (SIGALRM, SIGALRM_handler);
SIGALRM_handler = 0;
caught_alarm = false;
alarm_set = false;
alarm_time = -1;
}
#endif
void Signal::reset () {
signal_handler = 0;
#define SIGNAL(SIG) \
(void) signal (SIG, SIG##_handler); \
SIG##_handler = 0;
SIGNALS
#undef SIGNAL
#ifndef WIN32
reset_alarm ();
#endif
caught_signal = false;
}
const char *Signal::name (int sig) {
#define SIGNAL(SIG) \
if (sig == SIG) \
return #SIG;
SIGNALS
#undef SIGNAL
#ifndef WIN32
if (sig == SIGALRM)
return "SIGALRM";
#endif
return "UNKNOWN";
}
// TODO printing is not reentrant and might lead to deadlock if the signal
// is raised during another print attempt (and locked IO is used). To avoid
// this we have to either run our own low-level printing routine here or in
// 'Message' or just dump those statistics somewhere else were we have
// exclusive access to. All these solutions are painful and not elegant.
static void catch_signal (int sig) {
#ifndef WIN32
if (sig == SIGALRM && absolute_real_time () >= alarm_time) {
if (!caught_alarm) {
caught_alarm = true;
if (signal_handler)
signal_handler->catch_alarm ();
}
Signal::reset_alarm ();
} else
#endif
{
if (!caught_signal) {
caught_signal = true;
if (signal_handler)
signal_handler->catch_signal (sig);
}
Signal::reset ();
::raise (sig);
}
}
void Signal::set (Handler *h) {
signal_handler = h;
#define SIGNAL(SIG) SIG##_handler = signal (SIG, catch_signal);
SIGNALS
#undef SIGNAL
}
#ifndef WIN32
void Signal::alarm (int seconds) {
CADICAL_assert (seconds >= 0);
CADICAL_assert (!alarm_set);
CADICAL_assert (alarm_time < 0);
SIGALRM_handler = signal (SIGALRM, catch_signal);
alarm_set = true;
alarm_time = absolute_real_time () + seconds;
::alarm (seconds);
}
#endif
} // namespace CaDiCaL
ABC_NAMESPACE_IMPL_END

View File

@ -1,56 +0,0 @@
#include "global.h"
#include "internal.hpp"
ABC_NAMESPACE_IMPL_START
namespace CaDiCaL {
// Sam Buss suggested to debug the case where a solver incorrectly claims
// the formula to be unsatisfiable by checking every learned clause to be
// satisfied by a satisfying assignment. Thus the first inconsistent
// learned clause will be immediately flagged without the need to generate
// proof traces and perform forward proof checking. The incorrectly derived
// clause will raise an abort signal and thus allows to debug the issue with
// a symbolic debugger immediately.
void External::check_solution_on_learned_clause () {
CADICAL_assert (solution);
for (const auto &lit : internal->clause)
if (sol (internal->externalize (lit)) == lit)
return;
fatal_message_start ();
fputs ("learned clause unsatisfied by solution:\n", stderr);
for (const auto &lit : internal->clause)
fprintf (stderr, "%d ", lit);
fputc ('0', stderr);
fatal_message_end ();
}
void External::check_solution_on_shrunken_clause (Clause *c) {
CADICAL_assert (solution);
for (const auto &lit : *c)
if (sol (internal->externalize (lit)) == lit)
return;
fatal_message_start ();
for (const auto &lit : *c)
fprintf (stderr, "%d ", lit);
fputc ('0', stderr);
fatal_message_end ();
}
void External::check_no_solution_after_learning_empty_clause () {
CADICAL_assert (solution);
FATAL ("learned empty clause but got solution");
}
void External::check_solution_on_learned_unit_clause (int unit) {
CADICAL_assert (solution);
if (sol (internal->externalize (unit)) == unit)
return;
FATAL ("learned unit %d contradicts solution", unit);
}
} // namespace CaDiCaL
ABC_NAMESPACE_IMPL_END

File diff suppressed because it is too large Load Diff

View File

@ -1,39 +0,0 @@
#include "global.h"
#ifdef PROFILE_MODE
#include "internal.hpp"
ABC_NAMESPACE_IMPL_START
namespace CaDiCaL {
bool Internal::propagate_stable () {
CADICAL_assert (stable);
START (propstable);
bool res = propagate ();
STOP (propstable);
return res;
}
void Internal::analyze_stable () {
CADICAL_assert (stable);
START (analyzestable);
analyze ();
STOP (analyzestable);
}
int Internal::decide_stable () {
CADICAL_assert (stable);
return decide ();
}
}; // namespace CaDiCaL
ABC_NAMESPACE_IMPL_END
#else
ABC_NAMESPACE_IMPL_START
int stable_if_not_profile_mode_dummy;
ABC_NAMESPACE_IMPL_END
#endif

View File

@ -1,826 +0,0 @@
// vim: set tw=300: set VIM text width to 300 characters for this file.
#include "global.h"
#include "internal.hpp"
ABC_NAMESPACE_IMPL_START
namespace CaDiCaL {
/*------------------------------------------------------------------------*/
Stats::Stats () {
time.real = absolute_real_time ();
time.process = absolute_process_time ();
walk.minimum = LONG_MAX;
used.resize (2);
used[0].resize (127);
used[1].resize (127);
}
/*------------------------------------------------------------------------*/
#define PRT(FMT, ...) \
do { \
if (FMT[0] == ' ' && !all) \
break; \
MSG (FMT, __VA_ARGS__); \
} while (0)
/*------------------------------------------------------------------------*/
void Stats::print (Internal *internal) {
#ifdef CADICAL_QUIET
(void) internal;
#else
Stats &stats = internal->stats;
int all = internal->opts.verbose > 0 || internal->opts.stats;
#ifdef LOGGING
if (internal->opts.log)
all = true;
#endif // ifdef LOGGING
if (internal->opts.profile)
internal->print_profile ();
double t = internal->solve_time ();
int64_t propagations = 0;
propagations += stats.propagations.cover;
propagations += stats.propagations.probe;
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;
int64_t totalticks = searchticks + inprobeticks;
size_t extendbytes = internal->external->extension.size ();
extendbytes *= sizeof (int);
SECTION ("statistics");
if (all || stats.blocked) {
PRT ("blocked: %15" PRId64
" %10.2f %% of irredundant clauses",
stats.blocked, percent (stats.blocked, stats.added.irredundant));
PRT (" blockings: %15" PRId64 " %10.2f internal",
stats.blockings, relative (stats.conflicts, stats.blockings));
PRT (" candidates: %15" PRId64 " %10.2f per blocking ",
stats.blockcands, relative (stats.blockcands, stats.blockings));
PRT (" blockres: %15" PRId64 " %10.2f per candidate",
stats.blockres, relative (stats.blockres, stats.blockcands));
PRT (" pure: %15" PRId64 " %10.2f %% of all variables",
stats.all.pure, percent (stats.all.pure, stats.vars));
PRT (" pureclauses: %15" PRId64 " %10.2f per pure literal",
stats.blockpured, relative (stats.blockpured, stats.all.pure));
}
if (all || stats.chrono)
PRT ("chronological: %15" PRId64 " %10.2f %% of conflicts",
stats.chrono, percent (stats.chrono, stats.conflicts));
if (all)
PRT ("compacts: %15" PRId64 " %10.2f interval",
stats.compacts, relative (stats.conflicts, stats.compacts));
if (all || stats.conflicts) {
PRT ("conflicts: %15" PRId64 " %10.2f per second",
stats.conflicts, relative (stats.conflicts, t));
PRT (" backtracked: %15" PRId64 " %10.2f %% of conflicts",
stats.backtracks, percent (stats.backtracks, stats.conflicts));
}
if (all || stats.conditioned) {
PRT ("conditioned: %15" PRId64
" %10.2f %% of irredundant clauses",
stats.conditioned,
percent (stats.conditioned, stats.added.irredundant));
PRT (" conditionings: %15" PRId64 " %10.2f interval",
stats.conditionings,
relative (stats.conflicts, stats.conditionings));
PRT (" condcands: %15" PRId64 " %10.2f candidate clauses",
stats.condcands, relative (stats.condcands, stats.conditionings));
PRT (" condassinit: %17.1f %9.2f %% initial assigned",
relative (stats.condassinit, stats.conditionings),
percent (stats.condassinit, stats.condassvars));
PRT (" condcondinit: %17.1f %9.2f %% initial condition",
relative (stats.condcondinit, stats.conditionings),
percent (stats.condcondinit, stats.condassinit));
PRT (" condautinit: %17.1f %9.2f %% initial autarky",
relative (stats.condautinit, stats.conditionings),
percent (stats.condautinit, stats.condassinit));
PRT (" condassrem: %17.1f %9.2f %% final assigned",
relative (stats.condassrem, stats.conditioned),
percent (stats.condassrem, stats.condassirem));
PRT (" condcondrem: %19.3f %7.2f %% final conditional",
relative (stats.condcondrem, stats.conditioned),
percent (stats.condcondrem, stats.condassrem));
PRT (" condautrem: %19.3f %7.2f %% final autarky",
relative (stats.condautrem, stats.conditioned),
percent (stats.condautrem, stats.condassrem));
PRT (" condprops: %15" PRId64 " %10.2f per candidate",
stats.condprops, relative (stats.condprops, stats.condcands));
}
if (all || stats.cover.total) {
PRT ("covered: %15" PRId64
" %10.2f %% of irredundant clauses",
stats.cover.total,
percent (stats.cover.total, stats.added.irredundant));
PRT (" coverings: %15" PRId64 " %10.2f interval",
stats.cover.count, relative (stats.conflicts, stats.cover.count));
PRT (" asymmetric: %15" PRId64 " %10.2f %% of covered clauses",
stats.cover.asymmetric,
percent (stats.cover.asymmetric, stats.cover.total));
PRT (" blocked: %15" PRId64 " %10.2f %% of covered clauses",
stats.cover.blocked,
percent (stats.cover.blocked, stats.cover.total));
}
if (all || stats.decisions) {
PRT ("decisions: %15" PRId64 " %10.2f per second",
stats.decisions, relative (stats.decisions, t));
PRT (" searched: %15" PRId64 " %10.2f per decision",
stats.searched, relative (stats.searched, stats.decisions));
}
if (all || stats.all.eliminated) {
PRT ("eliminated: %15" PRId64 " %10.2f %% of all variables",
stats.all.eliminated, percent (stats.all.eliminated, stats.vars));
PRT (" fastelim: %15" PRId64 " %10.2f %% of eliminated",
stats.all.fasteliminated,
percent (stats.all.fasteliminated, stats.all.eliminated));
PRT (" elimphases: %15" PRId64 " %10.2f interval",
stats.elimphases, relative (stats.conflicts, stats.elimphases));
PRT (" elimrounds: %15" PRId64 " %10.2f per phase",
stats.elimrounds, relative (stats.elimrounds, stats.elimphases));
PRT (" elimtried: %15" PRId64 " %10.2f %% eliminated",
stats.elimtried, percent (stats.all.eliminated, stats.elimtried));
PRT (" elimgates: %15" PRId64 " %10.2f %% gates per tried",
stats.elimgates, percent (stats.elimgates, stats.elimtried));
PRT (" elimequivs: %15" PRId64 " %10.2f %% equivalence gates",
stats.elimequivs, percent (stats.elimequivs, stats.elimgates));
PRT (" elimands: %15" PRId64 " %10.2f %% and gates",
stats.elimands, percent (stats.elimands, stats.elimgates));
PRT (" elimites: %15" PRId64 " %10.2f %% if-then-else gates",
stats.elimites, percent (stats.elimites, stats.elimgates));
PRT (" elimxors: %15" PRId64 " %10.2f %% xor gates",
stats.elimxors, percent (stats.elimxors, stats.elimgates));
PRT (" elimdefs: %15" PRId64 " %10.2f %% definitions",
stats.definitions_extracted,
percent (stats.definitions_extracted, stats.elimgates));
PRT (" elimsubst: %15" PRId64 " %10.2f %% substituted",
stats.elimsubst, percent (stats.elimsubst, stats.all.eliminated));
PRT (" elimsubstequi: %15" PRId64 " %10.2f %% equivalence gates",
stats.eliminated_equi,
percent (stats.eliminated_equi, stats.elimsubst));
PRT (" elimsubstands: %15" PRId64 " %10.2f %% and gates",
stats.eliminated_and,
percent (stats.eliminated_and, stats.elimsubst));
PRT (" elimsubstites: %15" PRId64 " %10.2f %% if-then-else gates",
stats.eliminated_ite,
percent (stats.eliminated_ite, stats.elimsubst));
PRT (" elimsubstxors: %15" PRId64 " %10.2f %% xor gates",
stats.eliminated_xor,
percent (stats.eliminated_xor, stats.elimsubst));
PRT (" elimsubstdefs: %15" PRId64 " %10.2f %% definitions",
stats.eliminated_def,
percent (stats.eliminated_def, stats.elimsubst));
PRT (" elimres: %15" PRId64 " %10.2f per eliminated",
stats.elimres, relative (stats.elimres, stats.all.eliminated));
PRT (" elimrestried: %15" PRId64 " %10.2f %% per resolution",
stats.elimrestried, percent (stats.elimrestried, stats.elimres));
PRT (" def checked: %15" PRId64 " %10.2f per phase",
stats.definitions_checked,
relative (stats.definitions_checked, stats.elimphases));
PRT (" def extracted: %15" PRId64 " %10.2f %% per checked",
stats.definitions_extracted,
percent (stats.definitions_extracted, stats.definitions_checked));
PRT (" def units: %15" PRId64 " %10.2f %% per checked",
stats.definition_units,
percent (stats.definition_units, stats.definitions_checked));
}
if (all || stats.ext_prop.ext_cb) {
PRT ("ext.prop. calls: %15" PRId64 " %10.2f %% of queries",
stats.ext_prop.eprop_call,
percent (stats.ext_prop.eprop_call, stats.ext_prop.ext_cb));
PRT (" propagating: %15" PRId64 " %10.2f %% per eprop-call",
stats.ext_prop.eprop_prop,
percent (stats.ext_prop.eprop_prop, stats.ext_prop.eprop_call));
PRT (" explained: %15" PRId64 " %10.2f %% per eprop-call",
stats.ext_prop.eprop_expl,
percent (stats.ext_prop.eprop_expl, stats.ext_prop.eprop_call));
PRT (" falsified: %15" PRId64 " %10.2f %% per eprop-call",
stats.ext_prop.eprop_conf,
percent (stats.ext_prop.eprop_conf, stats.ext_prop.eprop_call));
PRT ("ext.clause calls:%15" PRId64 " %10.2f %% of queries",
stats.ext_prop.elearn_call,
percent (stats.ext_prop.elearn_call, stats.ext_prop.ext_cb));
PRT (" learned: %15" PRId64 " %10.2f %% per called",
stats.ext_prop.elearned,
percent (stats.ext_prop.elearned, stats.ext_prop.elearn_call));
PRT (" conflicting: %15" PRId64 " %10.2f %% per learned",
stats.ext_prop.elearn_conf,
percent (stats.ext_prop.elearn_conf, stats.ext_prop.elearned));
PRT (" propagating: %15" PRId64 " %10.2f %% per learned",
stats.ext_prop.elearn_prop,
percent (stats.ext_prop.elearn_prop, stats.ext_prop.elearned));
PRT ("ext.final check: %15" PRId64 " %10.2f %% of queries",
stats.ext_prop.echeck_call,
percent (stats.ext_prop.echeck_call, stats.ext_prop.ext_cb));
}
if (all || stats.factored) {
PRT ("factored: %15" PRId64 " %10.2f %% of variables",
stats.factored, percent (stats.factored, internal->max_var));
PRT (" factor: %15" PRId64 " %10.2f conflict interval",
stats.factor, relative (stats.conflicts, stats.factor));
PRT (" cls factored: %15" PRId64 " %10.2f per factored",
stats.factor_added, relative (stats.factor_added, factored));
PRT (" lits factored: %15" PRId64 " %10.2f per factored",
stats.literals_factored,
relative (stats.literals_factored, factored));
PRT (" cls unfactored:%15" PRId64 " %10.2f per factored",
stats.clauses_unfactored,
relative (stats.clauses_unfactored, factored));
PRT (" lits unfactored:%14" PRId64 " %10.2f per factored",
stats.literals_unfactored,
relative (stats.literals_unfactored, factored));
}
if (all || stats.all.fixed) {
PRT ("fixed: %15" PRId64 " %10.2f %% of all variables",
stats.all.fixed, percent (stats.all.fixed, stats.vars));
PRT (" failed: %15" PRId64 " %10.2f %% of all variables",
stats.failed, percent (stats.failed, stats.vars));
PRT (" probefailed: %15" PRId64 " %10.2f %% per failed",
stats.probefailed, percent (stats.probefailed, stats.failed));
PRT (" transredunits: %15" PRId64 " %10.2f %% per failed",
stats.transredunits, percent (stats.transredunits, stats.failed));
PRT (" inprobephases: %15" PRId64 " %10.2f interval",
stats.inprobingphases,
relative (stats.conflicts, stats.inprobingphases));
PRT (" inprobesuccess:%15" PRId64 " %10.2f %% phases",
stats.inprobesuccess,
percent (stats.inprobesuccess, stats.inprobingphases));
PRT (" probingrounds: %15" PRId64 " %10.2f per phase",
stats.probingrounds,
relative (stats.probingrounds, stats.inprobingphases));
PRT (" probed: %15" PRId64 " %10.2f per failed",
stats.probed, relative (stats.probed, stats.failed));
PRT (" hbrs: %15" PRId64 " %10.2f per probed",
stats.hbrs, relative (stats.hbrs, stats.probed));
PRT (" hbrsizes: %15" PRId64 " %10.2f per hbr",
stats.hbrsizes, relative (stats.hbrsizes, stats.hbrs));
PRT (" hbreds: %15" PRId64 " %10.2f %% per hbr",
stats.hbreds, percent (stats.hbreds, stats.hbrs));
PRT (" hbrsubs: %15" PRId64 " %10.2f %% per hbr",
stats.hbrsubs, percent (stats.hbrsubs, stats.hbrs));
}
PRT (" units: %15" PRId64 " %10.2f interval", stats.units,
relative (stats.conflicts, stats.units));
PRT (" binaries: %15" PRId64 " %10.2f interval",
stats.binaries, relative (stats.conflicts, stats.binaries));
if (all || stats.flush.learned) {
PRT ("flushed: %15" PRId64 " %10.2f %% per conflict",
stats.flush.learned,
percent (stats.flush.learned, stats.conflicts));
PRT (" hyper: %15" PRId64 " %10.2f %% per conflict",
stats.flush.hyper, relative (stats.flush.hyper, stats.conflicts));
PRT (" flushings: %15" PRId64 " %10.2f interval",
stats.flush.count, relative (stats.conflicts, stats.flush.count));
}
if (all || stats.instantiated) {
PRT ("instantiated: %15" PRId64 " %10.2f %% of tried",
stats.instantiated, percent (stats.instantiated, stats.instried));
PRT (" instrounds: %15" PRId64 " %10.2f %% of elimrounds",
stats.instrounds, percent (stats.instrounds, stats.elimrounds));
}
if (all || stats.conflicts) {
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",
stats.bumped, relative (stats.bumped, stats.learned.clauses));
PRT (" recomputed: %15" PRId64 " %10.2f %% per learned",
stats.recomputed,
percent (stats.recomputed, stats.learned.clauses));
PRT (" promoted1: %15" PRId64 " %10.2f %% per learned",
stats.promoted1, percent (stats.promoted1, stats.learned.clauses));
PRT (" promoted2: %15" PRId64 " %10.2f %% per learned",
stats.promoted2, percent (stats.promoted2, stats.learned.clauses));
PRT (" improvedglue: %15" PRId64 " %10.2f %% per learned",
stats.improvedglue,
percent (stats.improvedglue, stats.learned.clauses));
}
if (all || stats.lucky.succeeded) {
PRT ("lucky: %15" PRId64 " %10.2f %% of tried",
stats.lucky.succeeded,
percent (stats.lucky.succeeded, stats.lucky.tried));
PRT (" constantzero %15" PRId64 " %10.2f %% of tried",
stats.lucky.constant.zero,
percent (stats.lucky.constant.zero, stats.lucky.tried));
PRT (" constantone %15" PRId64 " %10.2f %% of tried",
stats.lucky.constant.one,
percent (stats.lucky.constant.one, stats.lucky.tried));
PRT (" backwardone %15" PRId64 " %10.2f %% of tried",
stats.lucky.backward.one,
percent (stats.lucky.backward.one, stats.lucky.tried));
PRT (" backwardzero %15" PRId64 " %10.2f %% of tried",
stats.lucky.backward.zero,
percent (stats.lucky.backward.zero, stats.lucky.tried));
PRT (" forwardone %15" PRId64 " %10.2f %% of tried",
stats.lucky.forward.one,
percent (stats.lucky.forward.one, stats.lucky.tried));
PRT (" forwardzero %15" PRId64 " %10.2f %% of tried",
stats.lucky.forward.zero,
percent (stats.lucky.forward.zero, stats.lucky.tried));
PRT (" positivehorn %15" PRId64 " %10.2f %% of tried",
stats.lucky.horn.positive,
percent (stats.lucky.horn.positive, stats.lucky.tried));
PRT (" negativehorn %15" PRId64 " %10.2f %% of tried",
stats.lucky.horn.negative,
percent (stats.lucky.horn.negative, stats.lucky.tried));
}
PRT (" extendbytes: %15zd %10.2f bytes and MB", extendbytes,
extendbytes / (double) (1l << 20));
if (all || stats.learned.clauses)
PRT ("learned_lits: %15" PRId64 " %10.2f %% learned literals",
stats.learned.literals,
percent (stats.learned.literals, stats.learned.literals));
PRT ("minimized: %15" PRId64 " %10.2f %% learned literals",
stats.minimized, percent (stats.minimized, stats.learned.literals));
PRT ("shrunken: %15" PRId64 " %10.2f %% learned literals",
stats.shrunken, percent (stats.shrunken, stats.learned.literals));
PRT ("minishrunken: %15" PRId64 " %10.2f %% learned literals",
stats.minishrunken,
percent (stats.minishrunken, stats.learned.literals));
if (all || stats.conflicts) {
PRT ("otfs: %15" PRId64 " %10.2f %% of conflict",
stats.otfs.subsumed + stats.otfs.strengthened,
percent (stats.otfs.subsumed + stats.otfs.strengthened,
stats.conflicts));
PRT (" subsumed %15" PRId64 " %10.2f %% of conflict",
stats.otfs.subsumed,
percent (stats.otfs.subsumed, stats.conflicts));
PRT (" strengthened %15" PRId64 " %10.2f %% of conflict",
stats.otfs.strengthened,
percent (stats.otfs.strengthened, stats.conflicts));
}
PRT ("propagations: %15" PRId64 " %10.2f M per second",
propagations, relative (propagations / 1e6, t));
PRT (" coverprops: %15" PRId64 " %10.2f %% of propagations",
stats.propagations.cover,
percent (stats.propagations.cover, propagations));
PRT (" probeprops: %15" PRId64 " %10.2f %% of propagations",
stats.propagations.probe,
percent (stats.propagations.probe, propagations));
PRT (" searchprops: %15" PRId64 " %10.2f %% of propagations",
stats.propagations.search,
percent (stats.propagations.search, propagations));
PRT (" transredprops: %15" PRId64 " %10.2f %% of propagations",
stats.propagations.transred,
percent (stats.propagations.transred, propagations));
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));
}
if (all || stats.reduced) {
PRT ("reduced: %15" PRId64 " %10.2f %% per conflict",
stats.reduced, percent (stats.reduced, stats.conflicts));
PRT (" reductions: %15" PRId64 " %10.2f interval",
stats.reductions, relative (stats.conflicts, stats.reductions));
PRT (" sqrt scheme: %15" PRId64 " %10.2f %% reductions",
stats.reduced_sqrt,
relative (stats.reduced_sqrt, stats.reductions));
PRT (" prct scheme: %15" PRId64 " %10.2f %% reductions",
stats.reduced_prct,
relative (stats.reduced_prct, stats.reductions));
PRT (" collections: %15" PRId64 " %10.2f interval",
stats.collections, relative (stats.conflicts, stats.collections));
}
if (all || stats.rephased.total) {
PRT ("rephased: %15" PRId64 " %10.2f interval",
stats.rephased.total,
relative (stats.conflicts, stats.rephased.total));
PRT (" rephasedbest: %15" PRId64 " %10.2f %% rephased best",
stats.rephased.best,
percent (stats.rephased.best, stats.rephased.total));
PRT (" rephasedflip: %15" PRId64 " %10.2f %% rephased flipping",
stats.rephased.flipped,
percent (stats.rephased.flipped, stats.rephased.total));
PRT (" rephasedinv: %15" PRId64 " %10.2f %% rephased inverted",
stats.rephased.inverted,
percent (stats.rephased.inverted, stats.rephased.total));
PRT (" rephasedorig: %15" PRId64 " %10.2f %% rephased original",
stats.rephased.original,
percent (stats.rephased.original, stats.rephased.total));
PRT (" rephasedrand: %15" PRId64 " %10.2f %% rephased random",
stats.rephased.random,
percent (stats.rephased.random, stats.rephased.total));
PRT (" rephasedwalk: %15" PRId64 " %10.2f %% rephased walk",
stats.rephased.walk,
percent (stats.rephased.walk, stats.rephased.total));
}
if (all)
PRT ("rescored: %15" PRId64 " %10.2f interval",
stats.rescored, relative (stats.conflicts, stats.rescored));
if (all || stats.restarts) {
PRT ("restarts: %15" PRId64 " %10.2f interval",
stats.restarts, relative (stats.conflicts, stats.restarts));
PRT (" reused: %15" PRId64 " %10.2f %% per restart",
stats.reused, percent (stats.reused, stats.restarts));
PRT (" reusedlevels: %15" PRId64 " %10.2f %% per restart levels",
stats.reusedlevels,
percent (stats.reusedlevels, stats.restartlevels));
}
if (all || stats.restored) {
PRT ("restored: %15" PRId64 " %10.2f %% per weakened",
stats.restored, percent (stats.restored, stats.weakened));
PRT (" restorations: %15" PRId64 " %10.2f %% per extension",
stats.restorations,
percent (stats.restorations, stats.extensions));
PRT (" literals: %15" PRId64 " %10.2f per restored clause",
stats.restoredlits, relative (stats.restoredlits, stats.restored));
}
if (all || stats.stabphases) {
PRT ("stabilizing: %15" PRId64 " %10.2f %% of conflicts",
stats.stabphases, percent (stats.stabconflicts, stats.conflicts));
PRT (" restartstab: %15" PRId64 " %10.2f %% of all restarts",
stats.restartstable,
percent (stats.restartstable, stats.restarts));
PRT (" reusedstab: %15" PRId64 " %10.2f %% per stable restarts",
stats.reusedstable,
percent (stats.reusedstable, stats.restartstable));
}
if (all || stats.all.substituted) {
PRT ("substituted: %15" PRId64 " %10.2f %% of all variables",
stats.all.substituted,
percent (stats.all.substituted, stats.vars));
PRT (" decompositions:%15" PRId64 " %10.2f per phase",
stats.decompositions,
relative (stats.decompositions, stats.inprobingphases));
}
if (all || stats.sweep_equivalences) {
PRT ("sweep equivs: %15" PRId64 " %10.2f %% of swept variables",
stats.sweep_equivalences,
percent (stats.sweep_equivalences, stats.sweep_variables));
PRT (" sweepings: %15" PRId64 " %10.2f vars per sweeping",
stats.sweep, relative (stats.sweep_variables, stats.sweep));
PRT (" swept vars: %15" PRId64 " %10.2f %% of all variables",
stats.sweep_variables,
percent (stats.sweep_variables, stats.vars));
PRT (" sweep units: %15" PRId64 " %10.2f %% of all variables",
stats.sweep_units, percent (stats.sweep_units, stats.vars));
PRT (" solved: %15" PRId64 " %10.2f per swept variable",
stats.sweep_solved,
relative (stats.sweep_solved, stats.sweep_variables));
PRT (" sat: %15" PRId64 " %10.2f %% solved",
stats.sweep_sat, percent (stats.sweep_sat, stats.sweep_solved));
PRT (" unsat: %15" PRId64 " %10.2f %% solved",
stats.sweep_unsat,
percent (stats.sweep_unsat, stats.sweep_solved));
PRT (" backbone solved:%14" PRId64 " %10.2f %% solved",
stats.sweep_solved_backbone,
percent (stats.sweep_solved_backbone, stats.sweep_solved));
PRT (" sat: %15" PRId64 " %10.2f %% backbone solved",
stats.sweep_sat_backbone,
percent (stats.sweep_sat_backbone, stats.sweep_solved_backbone));
PRT (" unsat: %15" PRId64 " %10.2f %% backbone solved",
stats.sweep_unsat_backbone,
percent (stats.sweep_unsat_backbone, stats.sweep_solved_backbone));
PRT (" unknown: %15" PRId64 " %10.2f %% backbone solved",
stats.sweep_unknown_backbone,
percent (stats.sweep_unknown_backbone,
stats.sweep_solved_backbone));
PRT (" fixed: %15" PRId64 " %10.2f per swept variable",
stats.sweep_fixed_backbone,
relative (stats.sweep_fixed_backbone, stats.sweep_variables));
PRT (" flip: %15" PRId64 " %10.2f per swept variable",
stats.sweep_flip_backbone,
relative (stats.sweep_flip_backbone, stats.sweep_variables));
PRT (" flipped: %15" PRId64 " %10.2f %% of backbone flip",
stats.sweep_flipped_backbone,
percent (stats.sweep_flipped_backbone, stats.sweep_flip_backbone));
PRT (" equiv solved: %15" PRId64 " %10.2f %% solved",
stats.sweep_solved_equivalences,
percent (stats.sweep_solved_equivalences, stats.sweep_solved));
PRT (" sat: %15" PRId64 " %10.2f %% equiv solved",
stats.sweep_sat_equivalences,
percent (stats.sweep_sat_equivalences,
stats.sweep_solved_equivalences));
PRT (" unsat: %15" PRId64 " %10.2f %% equiv solved",
stats.sweep_unsat_equivalences,
percent (stats.sweep_unsat_equivalences,
stats.sweep_solved_equivalences));
PRT (" unknown: %15" PRId64 " %10.2f %% equiv solved",
stats.sweep_unknown_equivalences,
percent (stats.sweep_unknown_equivalences,
stats.sweep_solved_equivalences));
PRT (" flip: %15" PRId64 " %10.2f per swept variable",
stats.sweep_flip_equivalences,
relative (stats.sweep_flip_equivalences, stats.sweep_variables));
PRT (" flipped: %15" PRId64 " %10.2f %% of equiv flip",
stats.sweep_flipped_equivalences,
percent (stats.sweep_flipped_equivalences,
stats.sweep_flip_equivalences));
PRT (" depth: %15" PRId64 " %10.2f per swept variable",
stats.sweep_depth,
relative (stats.sweep_depth, stats.sweep_variables));
PRT (" environment: %15" PRId64 " %10.2f per swept variable",
stats.sweep_environment,
relative (stats.sweep_environment, stats.sweep_variables));
PRT (" clauses: %15" PRId64 " %10.2f per swept variable",
stats.sweep_clauses,
relative (stats.sweep_clauses, stats.sweep_variables));
PRT (" completed: %15" PRId64 " %10.2f sweeps to complete",
stats.sweep_completed,
relative (stats.sweep, stats.sweep_completed));
}
if (all || stats.subsumed) {
PRT ("subsumed: %15" PRId64 " %10.2f %% of all clauses",
stats.subsumed, percent (stats.subsumed, stats.added.total));
PRT (" subsumephases: %15" PRId64 " %10.2f interval",
stats.subsumephases,
relative (stats.conflicts, stats.subsumephases));
PRT (" subsumerounds: %15" PRId64 " %10.2f per phase",
stats.subsumerounds,
relative (stats.subsumerounds, stats.subsumephases));
PRT (" deduplicated: %15" PRId64 " %10.2f %% per subsumed",
stats.deduplicated, percent (stats.deduplicated, stats.subsumed));
PRT (" transreds: %15" PRId64 " %10.2f interval",
stats.transreds, relative (stats.conflicts, stats.transreds));
PRT (" transitive: %15" PRId64 " %10.2f %% per subsumed",
stats.transitive, percent (stats.transitive, stats.subsumed));
PRT (" subirr: %15" PRId64 " %10.2f %% of subsumed",
stats.subirr, percent (stats.subirr, stats.subsumed));
PRT (" subred: %15" PRId64 " %10.2f %% of subsumed",
stats.subred, percent (stats.subred, stats.subsumed));
PRT (" subtried: %15" PRId64 " %10.2f tried per subsumed",
stats.subtried, relative (stats.subtried, stats.subsumed));
PRT (" subchecks: %15" PRId64 " %10.2f per tried",
stats.subchecks, relative (stats.subchecks, stats.subtried));
PRT (" subchecks2: %15" PRId64 " %10.2f %% per subcheck",
stats.subchecks2, percent (stats.subchecks2, stats.subchecks));
PRT (" elimotfsub: %15" PRId64 " %10.2f %% of subsumed",
stats.elimotfsub, percent (stats.elimotfsub, stats.subsumed));
PRT (" elimbwsub: %15" PRId64 " %10.2f %% of subsumed",
stats.elimbwsub, percent (stats.elimbwsub, stats.subsumed));
PRT (" eagersub: %15" PRId64 " %10.2f %% of subsumed",
stats.eagersub, percent (stats.eagersub, stats.subsumed));
PRT (" eagertried: %15" PRId64 " %10.2f tried per eagersub",
stats.eagertried, relative (stats.eagertried, stats.eagersub));
}
if (all || stats.strengthened) {
PRT ("strengthened: %15" PRId64 " %10.2f %% of all clauses",
stats.strengthened,
percent (stats.strengthened, stats.added.total));
PRT (" elimotfstr: %15" PRId64 " %10.2f %% of strengthened",
stats.elimotfstr, percent (stats.elimotfstr, stats.strengthened));
PRT (" elimbwstr: %15" PRId64 " %10.2f %% of strengthened",
stats.elimbwstr, percent (stats.elimbwstr, stats.strengthened));
}
if (all || stats.htrs) {
PRT ("ternary: %15" PRId64 " %10.2f %% of resolved",
stats.htrs, percent (stats.htrs, stats.ternres));
PRT (" phases: %15" PRId64 " %10.2f interval",
stats.ternary, relative (stats.conflicts, stats.ternary));
PRT (" htr3: %15" PRId64
" %10.2f %% ternary hyper ternres",
stats.htrs3, percent (stats.htrs3, stats.htrs));
PRT (" htr2: %15" PRId64 " %10.2f %% binary hyper ternres",
stats.htrs2, percent (stats.htrs2, stats.htrs));
}
PRT ("ticks: %15" PRId64 " %10.2f propagation", totalticks,
relative (totalticks, stats.propagations.search));
PRT (" searchticks: %15" PRId64 " %10.2f %% totalticks",
searchticks, percent (searchticks, totalticks));
PRT (" stableticks: %15" PRId64 " %10.2f %% searchticks",
stats.ticks.search[1], percent (stats.ticks.search[1], searchticks));
PRT (" unstableticks:%15" PRId64 " %10.2f %% searchticks",
stats.ticks.search[0], percent (stats.ticks.search[0], searchticks));
PRT (" inprobeticks: %15" PRId64 " %10.2f %% totalticks",
inprobeticks, percent (inprobeticks, totalticks));
PRT (" factorticks: %15" PRId64 " %10.2f %% searchticks",
stats.ticks.factor, percent (stats.ticks.factor, searchticks));
PRT (" probeticks: %15" PRId64 " %10.2f %% searchticks",
stats.ticks.probe, percent (stats.ticks.probe, searchticks));
PRT (" sweepticks: %15" PRId64 " %10.2f %% searchticks",
stats.ticks.sweep, percent (stats.ticks.sweep, searchticks));
PRT (" ternaryticks: %15" PRId64 " %10.2f %% searchticks",
stats.ticks.ternary, percent (stats.ticks.ternary, searchticks));
PRT (" vivifyticks: %15" PRId64 " %10.2f %% searchticks",
stats.ticks.vivify, percent (stats.ticks.vivify, searchticks));
if (all) {
PRT ("tier recomputed: %15" PRId64 " %10.2f interval",
stats.tierecomputed,
relative (stats.conflicts, stats.tierecomputed));
}
if (all || stats.ilbtriggers) {
PRT ("trail reuses: %15" PRId64 " %10.2f %% of incremental calls",
stats.ilbsuccess, percent (stats.ilbsuccess, stats.ilbtriggers));
PRT (" levels: %15" PRId64 " %10.2f per reuse",
stats.levelsreused,
relative (stats.levelsreused, stats.ilbsuccess));
PRT (" literals: %15" PRId64 " %10.2f per reuse",
stats.literalsreused,
relative (stats.literalsreused, stats.ilbsuccess));
PRT (" assumptions: %15" PRId64 " %10.2f per reuse",
stats.assumptionsreused,
relative (stats.assumptionsreused, stats.ilbsuccess));
}
if (all || vivified) {
PRT ("vivified: %15" PRId64 " %10.2f %% of all clauses",
vivified, percent (vivified, stats.added.total));
PRT (" vivifications: %15" PRId64 " %10.2f interval",
stats.vivifications,
relative (stats.conflicts, stats.vivifications));
PRT (" vivifychecks: %15" PRId64 " %10.2f %% per conflict",
stats.vivifychecks, percent (stats.vivifychecks, stats.conflicts));
PRT (" vivifysched: %15" PRId64 " %10.2f %% checks per scheduled",
stats.vivifysched,
percent (stats.vivifychecks, stats.vivifysched));
PRT (" vivifyunits: %15" PRId64 " %10.2f %% per vivify check",
stats.vivifyunits,
percent (stats.vivifyunits, stats.vivifychecks));
PRT (" vivifyinst: %15" PRId64 " %10.2f %% per vivify check",
stats.vivifyinst, percent (stats.vivifyinst, stats.vivifychecks));
PRT (" vivifysubs: %15" PRId64 " %10.2f %% per subsumed",
stats.vivifysubs, percent (stats.vivifysubs, stats.subsumed));
PRT (" vivifysubred: %15" PRId64 " %10.2f %% per subs",
stats.vivifysubred,
percent (stats.vivifysubred, stats.vivifysubs));
PRT (" vivifysubirr: %15" PRId64 " %10.2f %% per subs",
stats.vivifysubirr,
percent (stats.vivifysubirr, stats.vivifysubs));
PRT (" vivifystrs: %15" PRId64 " %10.2f %% per strengthened",
stats.vivifystrs, percent (stats.vivifystrs, stats.strengthened));
PRT (" vivifystrirr: %15" PRId64 " %10.2f %% per vivifystrs",
stats.vivifystrirr,
percent (stats.vivifystrirr, stats.vivifystrs));
PRT (" vivifystred1: %15" PRId64 " %10.2f %% per vivifystrs",
stats.vivifystred1,
percent (stats.vivifystred1, stats.vivifystrs));
PRT (" vivifystred2: %15" PRId64 " %10.2f %% per viviyfstrs",
stats.vivifystred2,
percent (stats.vivifystred2, stats.vivifystrs));
PRT (" vivifystred3: %15" PRId64 " %10.2f %% per vivifystrs",
stats.vivifystred3,
percent (stats.vivifystred3, stats.vivifystrs));
PRT (" vivifydemote: %15" PRId64 " %10.2f %% per vivifystrs",
stats.vivifydemote,
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",
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));
#ifndef CADICAL_QUIET
if (internal->profiles.walk.value > 0)
PRT (" flips: %15" PRId64 " %10.2f M per second",
stats.walk.flips,
relative (1e-6 * stats.walk.flips,
internal->profiles.walk.value));
else
#endif
PRT (" flips: %15" PRId64 " %10.2f per walk",
stats.walk.flips, relative (stats.walk.flips, stats.walk.count));
if (stats.walk.minimum < LONG_MAX)
PRT (" minimum: %15" PRId64 " %10.2f %% clauses",
stats.walk.minimum,
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));
}
if (all || stats.weakened) {
PRT ("weakened: %15" PRId64 " %10.2f average size",
stats.weakened, relative (stats.weakenedlen, stats.weakened));
PRT (" extensions: %15" PRId64 " %10.2f interval",
stats.extensions, relative (stats.conflicts, stats.extensions));
PRT (" flipped: %15" PRId64 " %10.2f per weakened",
stats.extended, relative (stats.extended, stats.weakened));
}
if (all || stats.congruence.gates) {
PRT ("congruence: %15" PRId64 " %10.2f interval",
stats.congruence.rounds,
relative (stats.conflicts, stats.congruence.rounds));
PRT (" units: %15" PRId64 " %10.2f per congruent",
stats.congruence.units,
relative (stats.congruence.units, stats.congruence.congruent));
PRT (" cong-and: %15" PRId64 " %10.2f per found gates",
stats.congruence.ands,
relative (stats.congruence.ands, stats.congruence.gates));
PRT (" cong-ite: %15" PRId64 " %10.2f per found gates",
stats.congruence.ites,
relative (stats.congruence.ites, stats.congruence.gates));
PRT (" cong-xor: %15" PRId64 " %10.2f per found gates",
stats.congruence.xors,
relative (stats.congruence.xors, stats.congruence.gates));
PRT (" congruent: %15" PRId64 " %10.2f per round",
stats.congruence.congruent,
relative (stats.congruence.rounds, stats.congruence.congruent));
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",
stats.congruence.rewritten_ands,
relative (stats.congruence.rounds,
stats.congruence.rewritten_ands));
PRT (" subsumed: %15" PRId64 " %10.2f per round",
stats.congruence.subsumed,
relative (stats.congruence.rounds, stats.congruence.subsumed));
}
LINE ();
MSG ("%sseconds are measured in %s time for solving%s",
tout.magenta_code (), internal->opts.realtime ? "real" : "process",
tout.normal_code ());
#endif // ifndef CADICAL_QUIET
}
void Internal::print_resource_usage () {
#ifndef CADICAL_QUIET
SECTION ("resources");
uint64_t m = maximum_resident_set_size ();
MSG ("total process time since initialization: %12.2f seconds",
internal->process_time ());
MSG ("total real time since initialization: %12.2f seconds",
internal->real_time ());
MSG ("maximum resident set size of process: %12.2f MB",
m / (double) (1l << 20));
#endif
}
/*------------------------------------------------------------------------*/
void Checker::print_stats () {
if (!stats.added && !stats.deleted)
return;
SECTION ("checker statistics");
MSG ("checks: %15" PRId64 "", stats.checks);
MSG ("assumptions: %15" PRId64 " %10.2f per check",
stats.assumptions, relative (stats.assumptions, stats.checks));
MSG ("propagations: %15" PRId64 " %10.2f per check",
stats.propagations, relative (stats.propagations, stats.checks));
MSG ("original: %15" PRId64 " %10.2f %% of all clauses",
stats.original, percent (stats.original, stats.added));
MSG ("derived: %15" PRId64 " %10.2f %% of all clauses",
stats.derived, percent (stats.derived, stats.added));
MSG ("deleted: %15" PRId64 " %10.2f %% of all clauses",
stats.deleted, percent (stats.deleted, stats.added));
MSG ("insertions: %15" PRId64 " %10.2f %% of all clauses",
stats.insertions, percent (stats.insertions, stats.added));
MSG ("collections: %15" PRId64 " %10.2f deleted per collection",
stats.collections, relative (stats.collections, stats.deleted));
MSG ("collisions: %15" PRId64 " %10.2f per search",
stats.collisions, relative (stats.collisions, stats.searches));
MSG ("searches: %15" PRId64 "", stats.searches);
MSG ("units: %15" PRId64 "", stats.units);
}
void LratChecker::print_stats () {
if (!stats.added && !stats.deleted)
return;
SECTION ("lrat checker statistics");
MSG ("checks: %15" PRId64 "", stats.checks);
MSG ("insertions: %15" PRId64 " %10.2f %% of all clauses",
stats.insertions, percent (stats.insertions, stats.added));
MSG ("original: %15" PRId64 " %10.2f %% of all clauses",
stats.original, percent (stats.original, stats.added));
MSG ("derived: %15" PRId64 " %10.2f %% of all clauses",
stats.derived, percent (stats.derived, stats.added));
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",
stats.finalized, percent (stats.finalized, stats.added));
MSG ("collections: %15" PRId64 " %10.2f deleted per collection",
stats.collections, relative (stats.collections, stats.deleted));
MSG ("collisions: %15" PRId64 " %10.2f per search",
stats.collisions, relative (stats.collisions, stats.searches));
MSG ("searches: %15" PRId64 "", stats.searches);
}
} // namespace CaDiCaL
ABC_NAMESPACE_IMPL_END

View File

@ -1,652 +0,0 @@
#include "global.h"
#include "internal.hpp"
ABC_NAMESPACE_IMPL_START
namespace CaDiCaL {
/*------------------------------------------------------------------------*/
// This file implements a global forward subsumption algorithm, which is run
// frequently during search. It works both on original (irredundant)
// clauses and on 'sticky' learned clauses which are likely to be kept.
// This is abstracted away in the 'likely_to_be_kept_clause' function, which
// implicitly relies on 'opts.reducetier1glue' (glucose level of clauses
// which are not reduced) as well as dynamically determined size and glucose
// level ('lim.keptglue' and 'lim.keptsize') of clauses kept in 'reduce'.
//
// Note, that 'forward' means that the clause from which the subsumption
// check is started is checked for being subsumed by other (smaller or equal
// size) clauses. Since 'vivification' is an extended version of subsume,
// more powerful, but also slower, we schedule 'vivify' right after
// 'subsume', which in contrast to 'subsume' is not run until to completion.
//
// This implementation is inspired by Bayardo's SDM'11 analysis of our
// subsumption algorithm in our SATeLite preprocessor in the context of
// finding extremal sets in data mining and his suggested improvements.
// Our original subsumption algorithm in 'Quantor' and 'SATeLite' (and in
// MiniSAT and descendants) is based on backward subsumption. It uses the
// observation that only the occurrence list of one literal of a clause has
// to be traversed in order to find all potential clauses which are subsumed
// by the candidate. Thus the literal with the smallest number of
// occurrences is used. However, that scheme requires to connect all
// literals of surviving clauses, while forward algorithms only need to
// connect one literal. On the other hand forward checking requires to
// traverse the occurrence lists of all literals of the candidate clause to
// find subsuming clauses. During connecting the single literal (similar to
// the one-watch scheme by Lintao Zhang) one can connect a literal with a
// minimal number of occurrence so far, which implicitly also reduces future
// occurrence list traversal time.
// Also the actual subsumption check is cheaper since during backward
// checking the short subsuming candidate clause is marked and all the
// literals in the larger subsume candidate clauses have to be traversed,
// while for our forward approach the long subsumed candidate clause is only
// marked once, while the literals of the shorter subsuming clauses have to
// be checked. We also use a fixed special more cache friendly data
// structure for binary clauses, to avoid traversing them directly.
// In our forward scheme it is still possible to skip occurrence lists of
// literals which were not added since the last subsumption round, since
// only those can contain subsuming candidates. Actually, clauses which
// contain at least one literal, which was not added since the last
// subsumption round do not have to be connected at all, even though they
// might still be subsumed them self.
// Bayardo suggests to sort the literals in clauses and perform some kind of
// partial merge-sort, while we mark literals, but do sort literals during
// connecting a clause w.r.t. the number of occurrences, in order to find
// literals which do not occur in the subsumed candidate fast with high
// probability (less occurring literals have a higher chance).
// This is the actual subsumption and strengthening check. We assume that
// all the literals of the candidate clause to be subsumed or strengthened
// are marked, so we only have to check that all the literals of the
// argument clause 'subsuming', which is checked for subsuming the candidate
// clause 'subsumed', has all its literals marked (in the correct phase).
// If exactly one is in the opposite phase we can still strengthen the
// candidate clause by this single literal which occurs in opposite phase.
//
// The result is INT_MIN if all literals are marked and thus the candidate
// clause can be subsumed. It is zero if neither subsumption nor
// strengthening is possible. Otherwise the candidate clause can be
// strengthened and as a result the negation of the literal which can be
// removed is returned.
inline int Internal::subsume_check (Clause *subsuming, Clause *subsumed) {
#ifdef CADICAL_NDEBUG
(void) subsumed;
#endif
// Only use 'subsumed' for these following CADICAL_assertion checks. Otherwise we
// only require that 'subsumed' has all its literals marked.
//
CADICAL_assert (!subsumed->garbage);
CADICAL_assert (!subsuming->garbage);
CADICAL_assert (subsuming != subsumed);
CADICAL_assert (subsuming->size <= subsumed->size);
stats.subchecks++;
if (subsuming->size == 2)
stats.subchecks2++;
int flipped = 0, prev = 0;
bool failed = false;
const auto eoc = subsuming->end ();
for (auto i = subsuming->begin (); !failed && i != eoc; i++) {
int lit = *i;
*i = prev;
prev = lit;
const int tmp = marked (lit);
if (!tmp)
failed = true;
else if (tmp > 0)
continue;
else if (flipped)
failed = true;
else
flipped = lit;
}
CADICAL_assert (prev);
CADICAL_assert (!subsuming->literals[0]);
subsuming->literals[0] = prev;
if (failed)
return 0;
if (!flipped)
return INT_MIN; // subsumed!!
else if (!opts.subsumestr)
return 0;
else
return flipped; // strengthen!!
}
/*------------------------------------------------------------------------*/
// Candidate clause 'subsumed' is subsumed by 'subsuming'.
inline void Internal::subsume_clause (Clause *subsuming, Clause *subsumed) {
stats.subsumed++;
CADICAL_assert (subsuming->size <= subsumed->size);
LOG (subsumed, "subsumed");
if (subsumed->redundant)
stats.subred++;
else
stats.subirr++;
if (subsumed->redundant || !subsuming->redundant) {
mark_garbage (subsumed);
return;
}
LOG ("turning redundant subsuming clause into irredundant clause");
subsuming->redundant = false;
if (proof)
proof->strengthen (subsuming->id);
mark_garbage (subsumed);
stats.current.irredundant++;
stats.added.irredundant++;
stats.irrlits += subsuming->size;
CADICAL_assert (stats.current.redundant > 0);
stats.current.redundant--;
CADICAL_assert (stats.added.redundant > 0);
stats.added.redundant--;
// ... and keep 'stats.added.total'.
}
/*------------------------------------------------------------------------*/
// Candidate clause 'c' is strengthened by removing 'lit'.
void Internal::strengthen_clause (Clause *c, int lit) {
if (opts.check && is_external_forgettable (c->id))
mark_garbage_external_forgettable (c->id);
stats.strengthened++;
CADICAL_assert (c->size > 2);
LOG (c, "removing %d in", lit);
if (proof) {
LOG (lrat_chain, "strengthening clause with chain");
proof->strengthen_clause (c, lit, lrat_chain);
}
if (!c->redundant)
mark_removed (lit);
auto new_end = remove (c->begin (), c->end (), lit);
CADICAL_assert (new_end + 1 == c->end ()), (void) new_end;
(void) shrink_clause (c, c->size - 1);
// bump_clause2 (c);
LOG (c, "strengthened");
external->check_shrunken_clause (c);
}
/*------------------------------------------------------------------------*/
// Find clauses connected in the occurrence lists 'occs' which subsume the
// candidate clause 'c' given as first argument. If this is the case the
// clause is subsumed and the result is positive. If the clause was
// strengthened the result is negative. Otherwise the candidate clause
// can not be subsumed nor strengthened and zero is returned.
inline int Internal::try_to_subsume_clause (Clause *c,
vector<Clause *> &shrunken) {
stats.subtried++;
CADICAL_assert (!level);
LOG (c, "trying to subsume");
mark (c); // signed!
Clause *d = 0;
int flipped = 0;
for (const auto &lit : *c) {
// Only clauses which have a variable which has recently been added are
// checked for being subsumed. The idea is that all these newly added
// clauses are candidates for subsuming the clause. Then we also only
// need to check occurrences of these variables. The occurrence lists
// of other literal do not have to be checked.
//
if (!flags (lit).subsume)
continue;
for (int sign = -1; !d && sign <= 1; sign += 2) {
// First we check against all binary clauses. The other literals of
// all binary clauses of 'sign*lit' are stored in one consecutive
// array, which is way faster than storing clause pointers and
// dereferencing them. Since this binary clause array is also not
// shrunken, we also can bail out earlier if subsumption or
// strengthening is determined.
// In both cases the (self-)subsuming clause is stored in 'd', which
// makes it nonzero and forces aborting both the outer and inner loop.
// If the binary clause can strengthen the candidate clause 'c'
// (through self-subsuming resolution), then 'filled' is set to the
// literal which can be removed in 'c', otherwise to 'INT_MIN' which
// is a non-valid literal.
for (const auto &bin : bins (sign * lit)) {
const auto &other = bin.lit;
const int tmp = marked (other);
if (!tmp)
continue;
if (tmp < 0 && sign < 0)
continue;
if (tmp < 0) {
if (sign < 0)
continue; // tautological resolvent
dummy_binary->literals[0] = lit;
dummy_binary->literals[1] = other;
flipped = other;
} else {
dummy_binary->literals[0] = sign * lit;
dummy_binary->literals[1] = other;
flipped = (sign < 0) ? -lit : INT_MIN;
}
// This dummy binary clauses is initialized in 'Internal::Internal'
// and only changes it literals in the lines above. By using such
// a faked binary clause we can simply reuse 'subsume_clause' as
// well as the code around 'strengthen_clause' uniform for both real
// clauses and this special case for binary clauses
dummy_binary->id = bin.id;
d = dummy_binary;
break;
}
if (d)
break;
// In this second loop we check for larger than binary clauses to
// subsume or strengthen the candidate clause. This is more costly,
// and needs a call to 'subsume_check'. Otherwise the same contract
// as above for communicating 'subsumption' or 'strengthening' to the
// code after the loop is used.
//
const Occs &os = occs (sign * lit);
for (const auto &e : os) {
CADICAL_assert (!e->garbage); // sanity check
if (e->garbage)
continue; // defensive: not needed
flipped = subsume_check (e, c);
if (!flipped)
continue;
d = e; // leave also outer loop
break;
}
}
if (d)
break;
}
unmark (c);
if (flipped == INT_MIN) {
LOG (d, "subsuming");
subsume_clause (d, c);
return 1;
}
if (flipped) {
LOG (d, "strengthening");
if (lrat) {
CADICAL_assert (lrat_chain.empty ());
lrat_chain.push_back (c->id);
lrat_chain.push_back (d->id);
}
if (d->used > c->used)
c->used = d->used;
strengthen_clause (c, -flipped);
lrat_chain.clear ();
CADICAL_assert (likely_to_be_kept_clause (c));
shrunken.push_back (c);
return -1;
}
return 0;
}
struct subsume_less_noccs {
Internal *internal;
subsume_less_noccs (Internal *i) : internal (i) {}
bool operator() (int a, int b) {
const signed char u = internal->val (a), v = internal->val (b);
if (!u && v)
return true;
if (u && !v)
return false;
const int64_t m = internal->noccs (a), n = internal->noccs (b);
if (m < n)
return true;
if (m > n)
return false;
return abs (a) < abs (b);
}
};
/*------------------------------------------------------------------------*/
// Usually called from 'subsume' below if 'subsuming' triggered it. Then
// the idea is to subsume both redundant and irredundant clauses. It is also
// called in the elimination loop in 'elim' in which case we focus on
// irredundant clauses only to help bounded variable elimination. The
// function returns true of an irredundant clause was removed or
// strengthened, which might then in the second usage scenario trigger new
// variable eliminations.
bool Internal::subsume_round () {
if (!opts.subsume)
return false;
if (unsat)
return false;
if (terminated_asynchronously ())
return false;
if (!stats.current.redundant && !stats.current.irredundant)
return false;
START_SIMPLIFIER (subsume, SUBSUME);
stats.subsumerounds++;
int64_t check_limit;
if (opts.subsumelimited) {
int64_t delta = stats.propagations.search;
delta *= 1e-3 * opts.subsumeeffort;
if (delta < opts.subsumemineff)
delta = opts.subsumemineff;
if (delta > opts.subsumemaxeff)
delta = opts.subsumemaxeff;
delta = max (delta, (int64_t) 2l * active ());
PHASE ("subsume-round", stats.subsumerounds,
"limit of %" PRId64 " subsumption checks", delta);
check_limit = stats.subchecks + delta;
} else {
PHASE ("subsume-round", stats.subsumerounds,
"unlimited subsumption checks");
check_limit = LONG_MAX;
}
int old_marked_candidate_variables_for_elimination = stats.mark.elim;
CADICAL_assert (!level);
// Allocate schedule and occurrence lists.
//
vector<ClauseSize> schedule;
init_noccs ();
// Determine candidate clauses and sort them by size.
//
int64_t left_over_from_last_subsumption_round = 0;
for (auto c : clauses) {
if (c->garbage)
continue;
if (c->size > opts.subsumeclslim)
continue;
if (!likely_to_be_kept_clause (c))
continue;
bool fixed = false;
int subsume = 0;
for (const auto &lit : *c)
if (val (lit))
fixed = true;
else if (flags (lit).subsume)
subsume++;
// If the clause contains a root level assigned (fixed) literal we will
// not work on it. This simplifies the code substantially since we do
// not have to care about assignments at all. Strengthening becomes
// much simpler too.
//
if (fixed) {
LOG (c, "skipping (fixed literal)");
continue;
}
// Further, if less than two variables in the clause were added since
// the last subsumption round, the clause is ignored too.
//
if (subsume < 2) {
LOG (c, "skipping (only %d added literals)", subsume);
continue;
}
if (c->subsume)
left_over_from_last_subsumption_round++;
schedule.push_back (ClauseSize (c->size, c));
for (const auto &lit : *c)
noccs (lit)++;
}
shrink_vector (schedule);
// Smaller clauses are checked and connected first.
//
rsort (schedule.begin (), schedule.end (), smaller_clause_size_rank ());
if (!left_over_from_last_subsumption_round)
for (auto cs : schedule)
if (cs.clause->size > 2)
cs.clause->subsume = true;
#ifndef CADICAL_QUIET
int64_t scheduled = schedule.size ();
int64_t total = stats.current.irredundant + stats.current.redundant;
PHASE ("subsume-round", stats.subsumerounds,
"scheduled %" PRId64 " clauses %.0f%% out of %" PRId64 " clauses",
scheduled, percent (scheduled, total), total);
#endif
// Now go over the scheduled clauses in the order of increasing size and
// try to forward subsume and strengthen them. Forward subsumption tries
// to find smaller or same size clauses which subsume or might strengthen
// the candidate. After the candidate has been processed connect one
// of its literals (with smallest number of occurrences at this point) in
// a one-watched scheme.
int64_t subsumed = 0, strengthened = 0, checked = 0;
vector<Clause *> shrunken;
init_occs ();
init_bins ();
for (const auto &s : schedule) {
if (terminated_asynchronously ())
break;
if (stats.subchecks >= check_limit)
break;
Clause *c = s.clause;
CADICAL_assert (!c->garbage);
checked++;
// First try to subsume or strengthen this candidate clause. For binary
// clauses this could be done much faster by hashing and is costly due
// to a usually large number of binary clauses. There is further the
// issue, that strengthening binary clauses (through double
// self-subsuming resolution) would produce units, which needs much more
// care. In the same (lazy) spirit we also ignore clauses with fixed
// literals (false or true).
//
if (c->size > 2 && c->subsume) {
c->subsume = false;
const int tmp = try_to_subsume_clause (c, shrunken);
if (tmp > 0) {
subsumed++;
continue;
}
if (tmp < 0)
strengthened++;
}
// If not subsumed connect smallest occurring literal, where occurring
// means the number of times it was used to connect (as a one-watch) a
// previous smaller or equal sized clause. This minimizes the length of
// the occurrence lists traversed during 'try_to_subsume_clause'. Also
// note that this number is usually way smaller than the number of
// occurrences computed before and stored in 'noccs'.
//
int minlit = 0;
int64_t minoccs = 0;
size_t minsize = 0;
bool subsume = true;
bool binary = (c->size == 2 && !c->redundant);
for (const auto &lit : *c) {
if (!flags (lit).subsume)
subsume = false;
const size_t size = binary ? bins (lit).size () : occs (lit).size ();
if (minlit && minsize <= size)
continue;
const int64_t tmp = noccs (lit);
if (minlit && minsize == size && tmp <= minoccs)
continue;
minlit = lit, minsize = size, minoccs = tmp;
}
// If there is a variable in a clause different from is not 'subsume'
// (has been added since the last subsumption round), then this clause
// can not serve to strengthen or subsume another clause, since all
// shrunken or added clauses mark all their variables as 'subsume'.
//
if (!subsume)
continue;
if (!binary) {
// If smallest occurring literal occurs too often do not connect.
//
if (minsize > (size_t) opts.subsumeocclim)
continue;
LOG (c,
"watching %d with %zd current and total %" PRId64 " occurrences",
minlit, minsize, minoccs);
occs (minlit).push_back (c);
// This sorting should give faster failures for assumption checks
// since the less occurring variables are put first in a clause and
// thus will make it more likely to be found as witness for a clause
// not to be subsuming. One could in principle (see also the
// discussion on 'subsumption' in our 'Splatz' solver) replace marking
// by a kind of merge sort, as also suggested by Bayardo. It would
// avoid 'marked' calls and thus might be slightly faster but could
// not take benefit of this sorting optimization.
//
sort (c->begin (), c->end (), subsume_less_noccs (this));
} else {
// If smallest occurring literal occurs too often do not connect.
//
if (minsize > (size_t) opts.subsumebinlim)
continue;
LOG (c,
"watching %d with %zd current binary and total %" PRId64
" occurrences",
minlit, minsize, minoccs);
const int minlit_pos = (c->literals[1] == minlit);
const int other = c->literals[!minlit_pos];
bins (minlit).push_back (Bin{other, c->id});
}
}
PHASE ("subsume-round", stats.subsumerounds,
"subsumed %" PRId64 " and strengthened %" PRId64 " out of %" PRId64
" clauses %.0f%%",
subsumed, strengthened, scheduled,
percent (subsumed + strengthened, scheduled));
const int64_t remain = schedule.size () - checked;
const bool completed = !remain;
if (completed)
PHASE ("subsume-round", stats.subsumerounds,
"checked all %" PRId64 " scheduled clauses", checked);
else
PHASE ("subsume-round", stats.subsumerounds,
"checked %" PRId64 " clauses %.0f%% of scheduled (%" PRId64
" remain)",
checked, percent (checked, scheduled), remain);
// Release occurrence lists and schedule.
//
erase_vector (schedule);
reset_noccs ();
reset_occs ();
reset_bins ();
// Reset all old 'added' flags and mark variables in shrunken
// clauses as 'added' for the next subsumption round.
//
if (completed)
reset_subsume_bits ();
for (const auto &c : shrunken)
mark_added (c);
erase_vector (shrunken);
report ('s', !opts.reportall && !(subsumed + strengthened));
STOP_SIMPLIFIER (subsume, SUBSUME);
return old_marked_candidate_variables_for_elimination < stats.mark.elim;
}
/*------------------------------------------------------------------------*/
void Internal::subsume () {
if (!stats.current.redundant && !stats.current.irredundant)
return;
if (unsat)
return;
backtrack ();
if (!propagate ()) {
learn_empty_clause ();
return;
}
stats.subsumephases++;
if (external_prop) {
CADICAL_assert (!level);
private_steps = true;
}
if (opts.subsume) {
reset_watches ();
subsume_round ();
init_watches ();
connect_watches ();
if (!unsat && !propagate ()) {
LOG ("propagation after subsume rounds results in inconsistency");
learn_empty_clause ();
}
}
transred ();
if (external_prop) {
CADICAL_assert (!level);
private_steps = false;
}
}
} // namespace CaDiCaL
ABC_NAMESPACE_IMPL_END

File diff suppressed because it is too large Load Diff

View File

@ -1,49 +0,0 @@
#include "global.h"
#include "internal.hpp"
#ifdef WIN32
#include <io.h>
#define isatty _isatty
#endif
ABC_NAMESPACE_IMPL_START
namespace CaDiCaL {
Terminal::Terminal (FILE *f) : file (f), reset_on_exit (false) {
CADICAL_assert (file);
int fd = fileno (f);
CADICAL_assert (fd == 1 || fd == 2);
use_colors = connected = isatty (fd);
}
void Terminal::force_colors () { use_colors = connected = true; }
void Terminal::force_no_colors () { use_colors = false; }
void Terminal::force_reset_on_exit () { reset_on_exit = true; }
void Terminal::reset () {
if (!connected)
return;
erase_until_end_of_line ();
cursor (true);
normal ();
fflush (file);
}
void Terminal::disable () {
reset ();
connected = use_colors = false;
}
Terminal::~Terminal () {
if (reset_on_exit)
reset ();
}
Terminal tout (stdout);
Terminal terr (stderr);
} // namespace CaDiCaL
ABC_NAMESPACE_IMPL_END

View File

@ -1,456 +0,0 @@
#include "global.h"
#include "internal.hpp"
ABC_NAMESPACE_IMPL_START
namespace CaDiCaL {
/*------------------------------------------------------------------------*/
// This procedure can be used to produce all possible hyper ternary
// resolvents from ternary clauses. In contrast to hyper binary resolution
// we would only try to produce ternary clauses from ternary clauses, i.e.,
// do not consider quaternary clauses as antecedents. Of course if a binary
// clause is generated we keep it too. In any case we have to make sure
// though that we do not add clauses which are already in the formula or are
// subsumed by a binary clause in the formula. This procedure simulates
// structural hashing for multiplexer (if-then-else) and binary XOR gates in
// combination with equivalent literal substitution ('decompose') if run
// until to completion (which in the current implementation is too costly
// though and would need to be interleaved more eagerly with equivalent
// literal substitution). For more information see our CPAIOR'13 paper.
/*------------------------------------------------------------------------*/
// Check whether a binary clause consisting of the permutation of the given
// literals already exists.
bool Internal::ternary_find_binary_clause (int a, int b) {
CADICAL_assert (occurring ());
CADICAL_assert (active (a));
CADICAL_assert (active (b));
size_t s = occs (a).size ();
size_t t = occs (b).size ();
int lit = s < t ? a : b;
if (opts.ternaryocclim < (int) occs (lit).size ())
return true;
for (const auto &c : occs (lit)) {
if (c->size != 2)
continue;
const int *lits = c->literals;
if (lits[0] == a && lits[1] == b)
return true;
if (lits[0] == b && lits[1] == a)
return true;
}
return false;
}
/*------------------------------------------------------------------------*/
// Check whether a ternary clause consisting of the permutation of the given
// literals already exists or is subsumed by an existing binary clause.
bool Internal::ternary_find_ternary_clause (int a, int b, int c) {
CADICAL_assert (occurring ());
CADICAL_assert (active (a));
CADICAL_assert (active (b));
CADICAL_assert (active (c));
size_t r = occs (a).size ();
size_t s = occs (b).size ();
size_t t = occs (c).size ();
int lit;
if (r < s)
lit = (t < r) ? c : a;
else
lit = (t < s) ? c : b;
if (opts.ternaryocclim < (int) occs (lit).size ())
return true;
for (const auto &d : occs (lit)) {
const int *lits = d->literals;
if (d->size == 2) {
if (lits[0] == a && lits[1] == b)
return true;
if (lits[0] == b && lits[1] == a)
return true;
if (lits[0] == a && lits[1] == c)
return true;
if (lits[0] == c && lits[1] == a)
return true;
if (lits[0] == b && lits[1] == c)
return true;
if (lits[0] == c && lits[1] == b)
return true;
} else {
CADICAL_assert (d->size == 3);
if (lits[0] == a && lits[1] == b && lits[2] == c)
return true;
if (lits[0] == a && lits[1] == c && lits[2] == b)
return true;
if (lits[0] == b && lits[1] == a && lits[2] == c)
return true;
if (lits[0] == b && lits[1] == c && lits[2] == a)
return true;
if (lits[0] == c && lits[1] == a && lits[2] == b)
return true;
if (lits[0] == c && lits[1] == b && lits[2] == a)
return true;
}
}
return false;
}
/*------------------------------------------------------------------------*/
// Try to resolve the two ternary clauses on the given pivot (assumed to
// occur positively in the first clause, negatively in the second). If the
// resolvent has four literals, is tautological, already exists or in the
// case of a ternary resolvent is subsumed by an existing binary clause then
// 'false' is returned. The global 'clause' contains the resolvent and
// needs to be cleared in any case.
bool Internal::hyper_ternary_resolve (Clause *c, int pivot, Clause *d) {
LOG ("hyper binary resolving on pivot %d", pivot);
LOG (c, "1st antecedent");
LOG (d, "2nd antecedent");
stats.ternres++;
CADICAL_assert (c->size == 3);
CADICAL_assert (d->size == 3);
CADICAL_assert (clause.empty ());
for (const auto &lit : *c)
if (lit != pivot)
clause.push_back (lit);
for (const auto &lit : *d) {
if (lit == -pivot)
continue;
if (lit == clause[0])
continue;
if (lit == -clause[0])
return false;
if (lit == clause[1])
continue;
if (lit == -clause[1])
return false;
clause.push_back (lit);
}
size_t size = clause.size ();
if (size > 3)
return false;
if (size == 2 && ternary_find_binary_clause (clause[0], clause[1]))
return false;
if (size == 3 &&
ternary_find_ternary_clause (clause[0], clause[1], clause[2]))
return false;
return true;
}
/*------------------------------------------------------------------------*/
// Produce all ternary resolvents on literal 'pivot' and increment the
// 'steps' counter by the number of clauses containing 'pivot' which are
// used during this process. The reason for choosing this metric to measure
// the effort spent in 'ternary' is that it should be similar to one
// propagation step during search.
void Internal::ternary_lit (int pivot, int64_t &steps, int64_t &htrs) {
LOG ("starting hyper ternary resolutions on pivot %d", pivot);
steps -= 1 + cache_lines (occs (pivot).size (), sizeof (Clause *));
for (const auto &c : occs (pivot)) {
if (steps < 0)
break;
if (htrs < 0)
break;
if (c->garbage)
continue;
if (c->size != 3) {
CADICAL_assert (c->size == 2);
continue;
}
if (--steps < 0)
break;
bool assigned = false;
for (const auto &lit : *c)
if (val (lit)) {
assigned = true;
break;
}
if (assigned)
continue;
steps -= 1 + cache_lines (occs (-pivot).size (), sizeof (Clause *));
for (const auto &d : occs (-pivot)) {
if (htrs < 0)
break;
if (--steps < 0)
break;
if (d->garbage)
continue;
if (d->size != 3) {
CADICAL_assert (d->size == 2);
continue;
}
for (const auto &lit : *d)
if (val (lit)) {
assigned = true;
break;
}
if (assigned)
continue;
CADICAL_assert (clause.empty ());
htrs--;
if (hyper_ternary_resolve (c, pivot, d)) {
size_t size = clause.size ();
bool red = (size == 3 || (c->redundant && d->redundant));
if (lrat) {
CADICAL_assert (lrat_chain.empty ());
lrat_chain.push_back (c->id);
lrat_chain.push_back (d->id);
}
Clause *r = new_hyper_ternary_resolved_clause (red);
if (red)
r->hyper = true;
lrat_chain.clear ();
clause.clear ();
LOG (r, "hyper ternary resolved");
stats.htrs++;
for (const auto &lit : *r)
occs (lit).push_back (r);
if (size == 2) {
LOG ("hyper ternary resolvent subsumes both antecedents");
mark_garbage (c);
mark_garbage (d);
stats.htrs2++;
break;
} else {
CADICAL_assert (r->size == 3);
stats.htrs3++;
}
} else {
LOG (clause, "ignoring size %zd resolvent", clause.size ());
clause.clear ();
}
}
}
}
/*------------------------------------------------------------------------*/
// Same as 'ternary_lit' but pick the phase of the variable based on the
// number of positive and negative occurrence.
void Internal::ternary_idx (int idx, int64_t &steps, int64_t &htrs) {
CADICAL_assert (0 < idx);
CADICAL_assert (idx <= max_var);
steps -= 3;
if (!active (idx))
return;
if (!flags (idx).ternary)
return;
int pos = occs (idx).size ();
int neg = occs (-idx).size ();
if (pos <= opts.ternaryocclim && neg <= opts.ternaryocclim) {
LOG ("index %d has %zd positive and %zd negative occurrences", idx,
occs (idx).size (), occs (-idx).size ());
int pivot = (neg < pos ? -idx : idx);
ternary_lit (pivot, steps, htrs);
}
flags (idx).ternary = false;
}
/*------------------------------------------------------------------------*/
// One round of ternary resolution over all variables. As in 'block' and
// 'elim' we maintain a persistent global flag 'ternary' for each variable,
// which records, whether a ternary clause containing it was added. Then we
// can focus on those variables for which we have not tried ternary
// resolution before and nothing changed for them since then. This works
// across multiple calls to 'ternary' as well as 'ternary_round' since this
// 'ternary' variable flag is updated during adding (ternary) resolvents.
// This function goes over each variable just once.
bool Internal::ternary_round (int64_t &steps_limit, int64_t &htrs_limit) {
CADICAL_assert (!unsat);
#ifndef CADICAL_QUIET
int64_t bincon = 0;
int64_t terncon = 0;
#endif
init_occs ();
steps_limit -= 1 + cache_lines (clauses.size (), sizeof (Clause *));
for (const auto &c : clauses) {
steps_limit--;
if (c->garbage)
continue;
if (c->size > 3)
continue;
bool assigned = false, marked = false;
for (const auto &lit : *c) {
if (val (lit)) {
assigned = true;
break;
}
if (flags (lit).ternary)
marked = true;
}
if (assigned)
continue;
if (c->size == 2) {
#ifndef CADICAL_QUIET
bincon++;
#endif
} else {
CADICAL_assert (c->size == 3);
if (!marked)
continue;
#ifndef CADICAL_QUIET
terncon++;
#endif
}
for (const auto &lit : *c)
occs (lit).push_back (c);
}
PHASE ("ternary", stats.ternary,
"connected %" PRId64 " ternary %.0f%% "
"and %" PRId64 " binary clauses %.0f%%",
terncon, percent (terncon, clauses.size ()), bincon,
percent (bincon, clauses.size ()));
// Try ternary resolution on all variables once.
//
for (auto idx : vars) {
if (terminated_asynchronously ())
break;
if (steps_limit < 0)
break;
if (htrs_limit < 0)
break;
ternary_idx (idx, steps_limit, htrs_limit);
}
// Gather some statistics for the verbose messages below and also
// determine whether new variables have been marked and it would make
// sense to run another round of ternary resolution over those variables.
//
int remain = 0;
for (auto idx : vars) {
if (!active (idx))
continue;
if (!flags (idx).ternary)
continue;
remain++;
}
if (remain)
PHASE ("ternary", stats.ternary, "%d variables remain %.0f%%", remain,
percent (remain, max_var));
else
PHASE ("ternary", stats.ternary, "completed hyper ternary resolution");
reset_occs ();
CADICAL_assert (!unsat);
return remain; // Are there variables that should be tried again?
}
/*------------------------------------------------------------------------*/
bool Internal::ternary () {
if (!opts.ternary)
return false;
if (unsat)
return false;
if (terminated_asynchronously ())
return false;
// No new ternary clauses added since last time?
//
if (last.ternary.marked == stats.mark.ternary)
return false;
SET_EFFORT_LIMIT (limit, ternary, true);
START_SIMPLIFIER (ternary, TERNARY);
stats.ternary++;
CADICAL_assert (!level);
CADICAL_assert (!unsat);
if (watching ())
reset_watches ();
// The number of clauses derived through ternary resolution can grow
// substantially, particularly for random formulas. Thus we limit the
// number of added clauses too (actually the number of 'htrs').
//
int64_t htrs_limit = stats.current.redundant + stats.current.irredundant;
htrs_limit *= opts.ternarymaxadd;
htrs_limit /= 100;
// approximation of ternary ticks.
// TODO: count with ternary.ticks directly.
int64_t steps_limit = stats.ticks.ternary - limit;
stats.ticks.ternary = limit;
// With 'stats.ternary' we actually count the number of calls to
// 'ternary_round' and not the number of calls to 'ternary'. But before
// the first round we want to show the limit on the number of steps and
// thus we increase counter for the first round here and skip increasing
// it in the loop below.
//
PHASE ("ternary", stats.ternary,
"will run a maximum of %d rounds "
"limited to %" PRId64 " steps and %" PRId64 " clauses",
opts.ternaryrounds, steps_limit, htrs_limit);
bool resolved_binary_clause = false;
bool completed = false;
for (int round = 0;
!terminated_asynchronously () && round < opts.ternaryrounds;
round++) {
if (htrs_limit < 0)
break;
if (steps_limit < 0)
break;
if (round)
stats.ternary++;
int old_htrs2 = stats.htrs2;
int old_htrs3 = stats.htrs3;
completed = ternary_round (steps_limit, htrs_limit);
int delta_htrs2 = stats.htrs2 - old_htrs2;
int delta_htrs3 = stats.htrs3 - old_htrs3;
PHASE ("ternary", stats.ternary,
"derived %d ternary and %d binary resolvents", delta_htrs3,
delta_htrs2);
report ('3', !opts.reportall && !(delta_htrs2 + delta_htrs2));
if (delta_htrs2)
resolved_binary_clause = true;
if (!delta_htrs3)
break;
}
CADICAL_assert (!occurring ());
CADICAL_assert (!unsat);
init_watches ();
connect_watches ();
if (!propagate ()) {
LOG ("propagation after connecting watches results in inconsistency");
learn_empty_clause ();
}
if (completed)
last.ternary.marked = stats.mark.ternary;
STOP_SIMPLIFIER (ternary, TERNARY);
return resolved_binary_clause;
}
} // namespace CaDiCaL
ABC_NAMESPACE_IMPL_END

View File

@ -1,59 +0,0 @@
#include "global.h"
#include "internal.hpp"
ABC_NAMESPACE_IMPL_START
namespace CaDiCaL {
void Internal::recompute_tier () {
if (!opts.recomputetier)
return;
++stats.tierecomputed;
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);
#ifndef CADICAL_NDEBUG
uint64_t total_used = 0;
for (auto u : stats.used[stable])
total_used += u;
CADICAL_assert (total_used == stats.bump_used[stable]);
#endif
if (!stats.bump_used[stable]) {
tier1[stable] = opts.reducetier1glue;
tier2[stable] = opts.reducetier2glue;
LOG ("tier1 limit = %d", tier1[stable]);
LOG ("tier2 limit = %d", tier2[stable]);
return;
} else {
uint64_t accumulated_tier1_limit =
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) {
const uint64_t u = stats.used[stable][glue];
accumulated_used += u;
if (accumulated_used <= accumulated_tier1_limit) {
tier1[stable] = glue;
}
if (accumulated_used >= accumulated_tier2_limit) {
tier2[stable] = glue;
break;
}
}
}
LOG ("tier1 limit = %d in %s mode", tier1[stable],
stable ? "stable" : "focused");
LOG ("tier2 limit = %d in %s mode", tier2[stable],
stable ? "stable" : "focused");
}
} // namespace CaDiCaL
ABC_NAMESPACE_IMPL_END

View File

@ -1,259 +0,0 @@
#include "global.h"
#include "internal.hpp"
ABC_NAMESPACE_IMPL_START
namespace CaDiCaL {
// Implement transitive reduction in the binary implication graph. This is
// important for hyper binary resolution, which has the risk to produce too
// many hyper binary resolvents otherwise. This algorithm only works on
// binary clauses and is usually pretty fast. It will also find some failed
// literals (in the binary implication graph).
void Internal::transred () {
if (!opts.transred)
return;
if (unsat)
return;
if (terminated_asynchronously ())
return;
if (!stats.current.redundant && !stats.current.irredundant)
return;
CADICAL_assert (opts.transred);
CADICAL_assert (!level);
START_SIMPLIFIER (transred, TRANSRED);
stats.transreds++;
// Transitive reduction can not be run to completion for larger formulas
// with many binary clauses. We bound it in the same way as 'probe_core'.
//
int64_t limit = stats.propagations.search;
limit -= last.transred.propagations;
limit *= 1e-3 * opts.transredeffort;
if (limit < opts.transredmineff)
limit = opts.transredmineff;
if (limit > opts.transredmaxeff)
limit = opts.transredmaxeff;
PHASE ("transred", stats.transreds,
"transitive reduction limit of %" PRId64 " propagations", limit);
const auto end = clauses.end ();
auto i = clauses.begin ();
// Find first clause not checked for being transitive yet.
//
for (; i != end; i++) {
Clause *c = *i;
if (c->garbage)
continue;
if (c->size != 2)
continue;
if (c->redundant && c->hyper)
continue;
if (!c->transred)
break;
}
// If all candidate clauses have been checked reschedule all.
//
if (i == end) {
PHASE ("transred", stats.transreds,
"rescheduling all clauses since no clauses to check left");
for (i = clauses.begin (); i != end; i++) {
Clause *c = *i;
if (c->transred)
c->transred = false;
}
i = clauses.begin ();
}
// Move watches of binary clauses to the front. Thus we can stop iterating
// watches as soon a long clause is found during watch traversal.
//
sort_watches ();
// This working stack plays the same role as the 'trail' during standard
// propagation.
//
vector<int> work;
int64_t propagations = 0, units = 0, removed = 0;
while (!unsat && i != end && !terminated_asynchronously () &&
propagations < limit) {
Clause *c = *i++;
// A clause is a candidate for being transitive if it is binary, and not
// the result of hyper binary resolution. The reason for excluding
// those, is that they come in large numbers, most of them are reduced
// away anyhow and further are non-transitive at the point they are
// added (see the code in 'hyper_binary_resolve' in 'prope.cpp' and
// also check out our CPAIOR paper on tree-based look ahead).
//
if (c->garbage)
continue;
if (c->size != 2)
continue;
if (c->redundant && c->hyper)
continue;
if (c->transred)
continue; // checked before?
c->transred = true; // marked as checked
LOG (c, "checking transitive reduction of");
// Find a different path from 'src' to 'dst' in the binary implication
// graph, not using 'c'. Since this is the same as checking whether
// there is a path from '-dst' to '-src', we can do the reverse search
// if the number of watches of '-dst' is larger than those of 'src'.
//
int src = -c->literals[0];
int dst = c->literals[1];
if (val (src) || val (dst))
continue;
if (watches (-src).size () < watches (dst).size ()) {
int tmp = dst;
dst = -src;
src = -tmp;
}
LOG ("searching path from %d to %d", src, dst);
// If the candidate clause is irredundant then we can not use redundant
// binary clauses in the implication graph. See our inprocessing rules
// paper, why this restriction is required.
//
const bool irredundant = !c->redundant;
CADICAL_assert (work.empty ());
mark (src);
work.push_back (src);
LOG ("transred assign %d", src);
bool transitive = false; // found path from 'src' to 'dst'?
bool failed = false; // 'src' failed literal?
size_t j = 0; // 'propagated' in BFS
CADICAL_assert (lrat_chain.empty ());
CADICAL_assert (mini_chain.empty ());
vector<int> parents;
while (!transitive && !failed && j < work.size ()) {
const int lit = work[j++];
CADICAL_assert (marked (lit) > 0);
LOG ("transred propagating %d", lit);
propagations++;
const Watches &ws = watches (-lit);
const const_watch_iterator eow = ws.end ();
const_watch_iterator k;
for (k = ws.begin (); !transitive && !failed && k != eow; k++) {
const Watch &w = *k;
if (!w.binary ())
break; // since we sorted watches above
Clause *d = w.clause;
if (d == c)
continue;
if (irredundant && d->redundant)
continue;
if (d->garbage)
continue;
const int other = w.blit;
if (other == dst)
transitive = true; // 'dst' reached
else {
const int tmp = marked (other);
if (tmp > 0)
continue;
else if (tmp < 0) {
if (lrat) {
parents.push_back (lit);
mini_chain.push_back (d->id);
work.push_back (other);
}
LOG ("found both %d and %d reachable", -other, other);
failed = true;
} else {
if (lrat) {
parents.push_back (lit);
mini_chain.push_back (d->id);
}
mark (other);
work.push_back (other);
LOG ("transred assign %d", other);
}
}
}
}
int failed_lit = work.back ();
int next_pos = 0;
int next_neg = 0;
// Unassign all assigned literals (same as '[bp]acktrack').
//
while (!work.empty ()) {
const int lit = work.back ();
work.pop_back ();
if (lrat && failed && !work.empty ()) {
CADICAL_assert (!parents.empty () && !mini_chain.empty ());
LOG ("transred LRAT current lit %d next pos %d next neg %d", lit,
next_pos, next_neg);
if (lit == failed_lit || lit == next_pos) {
lrat_chain.push_back (mini_chain.back ());
next_pos = parents.back ();
} else if (lit == -failed_lit || lit == next_neg) {
lrat_chain.push_back (mini_chain.back ());
next_neg = parents.back ();
}
parents.pop_back ();
mini_chain.pop_back ();
}
unmark (lit);
}
mini_chain.clear ();
CADICAL_assert (mini_chain.empty ());
if (lrat && failed) {
reverse (lrat_chain.begin (), lrat_chain.end ());
}
if (transitive) {
removed++;
stats.transitive++;
LOG (c, "transitive redundant");
mark_garbage (c);
} else if (failed) {
units++;
LOG ("found failed literal %d during transitive reduction", src);
stats.failed++;
stats.transredunits++;
assign_unit (-src);
if (!propagate ()) {
VERBOSE (1, "propagating new unit results in conflict");
learn_empty_clause ();
}
}
lrat_chain.clear ();
}
last.transred.propagations = stats.propagations.search;
stats.propagations.transred += propagations;
erase_vector (work);
PHASE ("transred", stats.transreds,
"removed %" PRId64 " transitive clauses, found %" PRId64 " units",
removed, units);
STOP_SIMPLIFIER (transred, TRANSRED);
report ('t', !opts.reportall && !(removed + units));
}
} // namespace CaDiCaL
ABC_NAMESPACE_IMPL_END

View File

@ -1,38 +0,0 @@
#include "global.h"
#ifdef PROFILE_MODE
#include "internal.hpp"
ABC_NAMESPACE_IMPL_START
namespace CaDiCaL {
bool Internal::propagate_unstable () {
CADICAL_assert (!stable);
START (propunstable);
bool res = propagate ();
STOP (propunstable);
return res;
}
void Internal::analyze_unstable () {
CADICAL_assert (!stable);
START (analyzeunstable);
analyze ();
STOP (analyzeunstable);
}
int Internal::decide_unstable () {
CADICAL_assert (!stable);
return decide ();
}
}; // namespace CaDiCaL
ABC_NAMESPACE_IMPL_END
#else
ABC_NAMESPACE_IMPL_START
int unstable_if_no_profile_mode;
ABC_NAMESPACE_IMPL_END
#endif

View File

@ -1,135 +0,0 @@
#include "global.h"
#include "internal.hpp"
ABC_NAMESPACE_IMPL_START
namespace CaDiCaL {
/*------------------------------------------------------------------------*/
bool parse_int_str (const char *val_str, int &val) {
if (!strcmp (val_str, "true"))
val = 1;
else if (!strcmp (val_str, "false"))
val = 0;
else {
const char *p = val_str;
int sign;
if (*p == '-')
sign = -1, p++;
else
sign = 1;
int ch;
if (!isdigit ((ch = *p++)))
return false;
const int64_t bound = -(int64_t) INT_MIN;
int64_t mantissa = ch - '0';
while (isdigit (ch = *p++)) {
if (bound / 10 < mantissa)
mantissa = bound;
else
mantissa *= 10;
const int digit = ch - '0';
if (bound - digit < mantissa)
mantissa = bound;
else
mantissa += digit;
}
int exponent = 0;
if (ch == 'e') {
while (isdigit ((ch = *p++)))
exponent = exponent ? 10 : ch - '0';
if (ch)
return false;
} else if (ch)
return false;
CADICAL_assert (exponent <= 10);
int64_t val64 = mantissa;
for (int i = 0; i < exponent; i++)
val64 *= 10;
if (sign < 0) {
val64 = -val64;
if (val64 < INT_MIN)
val64 = INT_MIN;
} else {
if (val64 > INT_MAX)
val64 = INT_MAX;
}
CADICAL_assert (INT_MIN <= val64);
CADICAL_assert (val64 <= INT_MAX);
val = val64;
}
return true;
}
/*------------------------------------------------------------------------*/
bool has_suffix (const char *str, const char *suffix) {
size_t k = strlen (str), l = strlen (suffix);
return k > l && !strcmp (str + k - l, suffix);
}
bool has_prefix (const char *str, const char *prefix) {
for (const char *p = str, *q = prefix; *q; q++, p++)
if (*q != *p)
return false;
return true;
}
/*------------------------------------------------------------------------*/
bool is_color_option (const char *arg) {
return !strcmp (arg, "--color") || !strcmp (arg, "--colors") ||
!strcmp (arg, "--colour") || !strcmp (arg, "--colours") ||
!strcmp (arg, "--color=1") || !strcmp (arg, "--colors=1") ||
!strcmp (arg, "--colour=1") || !strcmp (arg, "--colours=1") ||
!strcmp (arg, "--color=true") || !strcmp (arg, "--colors=true") ||
!strcmp (arg, "--colour=true") || !strcmp (arg, "--colours=true");
}
bool is_no_color_option (const char *arg) {
return !strcmp (arg, "--no-color") || !strcmp (arg, "--no-colors") ||
!strcmp (arg, "--no-colour") || !strcmp (arg, "--no-colours") ||
!strcmp (arg, "--color=0") || !strcmp (arg, "--colors=0") ||
!strcmp (arg, "--colour=0") || !strcmp (arg, "--colours=0") ||
!strcmp (arg, "--color=false") ||
!strcmp (arg, "--colors=false") ||
!strcmp (arg, "--colour=false") ||
!strcmp (arg, "--colours=false");
}
/*------------------------------------------------------------------------*/
static uint64_t primes[] = {
1111111111111111111lu, 2222222222222222249lu, 3333333333333333347lu,
4444444444444444537lu, 5555555555555555621lu, 6666666666666666677lu,
7777777777777777793lu, 8888888888888888923lu, 9999999999999999961lu,
};
uint64_t hash_string (const char *str) {
const unsigned size = sizeof primes / sizeof *primes;
uint64_t res = 0;
unsigned char ch;
unsigned i = 0;
for (const char *p = str; (ch = *p); p++) {
res += ch;
res *= primes[i++];
if (i == size)
i = 0;
}
return res;
}
} // namespace CaDiCaL
ABC_NAMESPACE_IMPL_END

View File

@ -1,45 +0,0 @@
#include "global.h"
#include "internal.hpp"
ABC_NAMESPACE_IMPL_START
namespace CaDiCaL {
void Internal::reset_subsume_bits () {
LOG ("marking all variables as not subsume");
for (auto idx : vars)
flags (idx).subsume = false;
}
void Internal::check_var_stats () {
#ifndef CADICAL_NDEBUG
int64_t fixed = 0, eliminated = 0, substituted = 0, pure = 0, unused = 0;
for (auto idx : vars) {
Flags &f = flags (idx);
if (f.active ())
continue;
if (f.fixed ())
fixed++;
if (f.eliminated ())
eliminated++;
if (f.substituted ())
substituted++;
if (f.unused ())
unused++;
if (f.pure ())
pure++;
}
CADICAL_assert (stats.now.fixed == fixed);
CADICAL_assert (stats.now.eliminated == eliminated);
CADICAL_assert (stats.now.substituted == substituted);
CADICAL_assert (stats.now.pure == pure);
int64_t inactive = unused + fixed + eliminated + substituted + pure;
CADICAL_assert (stats.inactive == inactive);
CADICAL_assert (max_var == stats.active + stats.inactive);
#endif
}
} // namespace CaDiCaL
ABC_NAMESPACE_IMPL_END

View File

@ -1,400 +0,0 @@
#include "global.h"
#include "internal.hpp"
ABC_NAMESPACE_IMPL_START
namespace CaDiCaL {
/*------------------------------------------------------------------------*/
VeripbTracer::VeripbTracer (Internal *i, File *f, bool b, bool a, bool c)
: internal (i), file (f), with_antecedents (a), checked_deletions (c),
num_clauses (0), size_clauses (0), clauses (0), last_hash (0),
last_id (0), last_clause (0)
#ifndef CADICAL_QUIET
,
added (0), deleted (0)
#endif
{
(void) internal;
// Initialize random number table for hash function.
//
Random random (42);
for (unsigned n = 0; n < num_nonces; n++) {
uint64_t nonce = random.next ();
if (!(nonce & 1))
nonce++;
CADICAL_assert (nonce), CADICAL_assert (nonce & 1);
nonces[n] = nonce;
}
#ifndef CADICAL_NDEBUG
binary = b;
#else
(void) b;
#endif
}
void VeripbTracer::connect_internal (Internal *i) {
internal = i;
file->connect_internal (internal);
LOG ("VERIPB TRACER connected to internal");
}
VeripbTracer::~VeripbTracer () {
LOG ("VERIPB TRACER delete");
delete file;
for (size_t i = 0; i < size_clauses; i++)
for (HashId *c = clauses[i], *next; c; c = next)
next = c->next, delete_clause (c);
delete[] clauses;
}
/*------------------------------------------------------------------------*/
void VeripbTracer::enlarge_clauses () {
CADICAL_assert (num_clauses == size_clauses);
const uint64_t new_size_clauses = size_clauses ? 2 * size_clauses : 1;
LOG ("VeriPB Tracer enlarging clauses from %" PRIu64 " to %" PRIu64,
(uint64_t) size_clauses, (uint64_t) new_size_clauses);
HashId **new_clauses;
new_clauses = new HashId *[new_size_clauses];
clear_n (new_clauses, new_size_clauses);
for (uint64_t i = 0; i < size_clauses; i++) {
for (HashId *c = clauses[i], *next; c; c = next) {
next = c->next;
const uint64_t h = reduce_hash (c->hash, new_size_clauses);
c->next = new_clauses[h];
new_clauses[h] = c;
}
}
delete[] clauses;
clauses = new_clauses;
size_clauses = new_size_clauses;
}
HashId *VeripbTracer::new_clause () {
HashId *res = new HashId ();
res->next = 0;
res->hash = last_hash;
res->id = last_id;
last_clause = res;
num_clauses++;
return res;
}
void VeripbTracer::delete_clause (HashId *c) {
CADICAL_assert (c);
num_clauses--;
delete c;
}
uint64_t VeripbTracer::reduce_hash (uint64_t hash, uint64_t size) {
CADICAL_assert (size > 0);
unsigned shift = 32;
uint64_t res = hash;
while ((((uint64_t) 1) << shift) > size) {
res ^= res >> shift;
shift >>= 1;
}
res &= size - 1;
CADICAL_assert (res < size);
return res;
}
uint64_t VeripbTracer::compute_hash (const int64_t id) {
CADICAL_assert (id > 0);
unsigned j = id % num_nonces; // Dont know if this is a good
uint64_t tmp = nonces[j] * (uint64_t) id; // hash funktion or even better
return last_hash = tmp; // than just using id.
}
bool VeripbTracer::find_and_delete (const int64_t id) {
if (!num_clauses)
return false;
/*
if (last_clause && last_clause->id == id) {
const uint64_t h = reduce_hash (last_clause->hash, size_clauses);
clauses[h] = last_clause->next;
delete last_clause;
return true;
}
*/
HashId **res = 0, *c;
const uint64_t hash = compute_hash (id);
const uint64_t h = reduce_hash (hash, size_clauses);
for (res = clauses + h; (c = *res); res = &c->next) {
if (c->hash == hash && c->id == id) {
break;
}
if (!c->next)
return false;
}
if (!c)
return false;
CADICAL_assert (c && res);
*res = c->next;
delete_clause (c);
return true;
}
void VeripbTracer::insert () {
if (num_clauses == size_clauses)
enlarge_clauses ();
const uint64_t h = reduce_hash (compute_hash (last_id), size_clauses);
HashId *c = new_clause ();
c->next = clauses[h];
clauses[h] = c;
}
/*------------------------------------------------------------------------*/
inline void VeripbTracer::put_binary_zero () {
CADICAL_assert (binary);
CADICAL_assert (file);
file->put ((unsigned char) 0);
}
inline void VeripbTracer::put_binary_lit (int lit) {
CADICAL_assert (binary);
CADICAL_assert (file);
CADICAL_assert (lit != INT_MIN);
unsigned x = 2 * abs (lit) + (lit < 0);
unsigned char ch;
while (x & ~0x7f) {
ch = (x & 0x7f) | 0x80;
file->put (ch);
x >>= 7;
}
ch = x;
file->put (ch);
}
inline void VeripbTracer::put_binary_id (int64_t id, bool can_be_negative) {
CADICAL_assert (binary);
CADICAL_assert (file);
uint64_t x = abs (id);
if (can_be_negative) {
x = 2 * x + (id < 0);
}
unsigned char ch;
while (x & ~0x7f) {
ch = (x & 0x7f) | 0x80;
file->put (ch);
x >>= 7;
}
ch = x;
file->put (ch);
}
/*------------------------------------------------------------------------*/
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;
for (auto p = chain.rbegin (); p != chain.rend (); p++) {
auto cid = *p;
if (first) {
first = false;
file->put (cid);
} else {
file->put (' ');
file->put (cid);
file->put (" + s");
}
}
file->put ("\n");
file->put ("e ");
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 (id);
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) {
file->put ("rup ");
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 ;\n");
if (!redundant && checked_deletions) {
file->put ("core id ");
file->put (id);
file->put ("\n");
}
}
void VeripbTracer::veripb_begin_proof (int64_t reserved_ids) {
file->put ("pseudo-Boolean proof version 2.0\n");
file->put ("f ");
file->put (reserved_ids);
file->put ("\n");
}
void VeripbTracer::veripb_delete_clause (int64_t id, bool redundant) {
if (!redundant && checked_deletions && find_and_delete (id))
return;
if (redundant || !checked_deletions)
file->put ("del id ");
else {
file->put ("delc ");
}
file->put (id);
file->put ("\n");
}
void VeripbTracer::veripb_report_status (bool unsat, int64_t conflict_id) {
file->put ("output NONE\n");
if (unsat) {
file->put ("conclusion UNSAT : ");
file->put (conflict_id);
file->put (" \n");
} else
file->put ("conclusion NONE\n");
file->put ("end pseudo-Boolean proof\n");
}
void VeripbTracer::veripb_strengthen (int64_t id) {
if (!checked_deletions)
return;
file->put ("core id ");
file->put (id);
file->put ("\n");
}
/*------------------------------------------------------------------------*/
void VeripbTracer::begin_proof (int64_t id) {
if (file->closed ())
return;
LOG ("VERIPB TRACER tracing start of proof with %" PRId64
"original clauses",
id);
veripb_begin_proof (id);
}
void VeripbTracer::add_derived_clause (int64_t id, bool redundant,
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)
veripb_add_derived_clause (id, redundant, clause, chain);
else
veripb_add_derived_clause (id, redundant, clause);
#ifndef CADICAL_QUIET
added++;
#endif
}
void VeripbTracer::delete_clause (int64_t id, bool redundant,
const vector<int> &) {
if (file->closed ())
return;
LOG ("VERIPB TRACER tracing deletion of clause[%" PRId64 "]", id);
veripb_delete_clause (id, redundant);
#ifndef CADICAL_QUIET
deleted++;
#endif
}
void VeripbTracer::report_status (int status, int64_t conflict_id) {
if (file->closed ())
return;
#ifdef LOGGING
if (conflict_id)
LOG ("VERIPB TRACER tracing finalization of proof with empty "
"clause[%" PRId64 "]",
conflict_id);
#endif
veripb_report_status (status == UNSATISFIABLE, conflict_id);
}
void VeripbTracer::weaken_minus (int64_t id, const vector<int> &) {
if (!checked_deletions)
return;
if (file->closed ())
return;
LOG ("VERIPB TRACER tracing weaken minus of clause[%" PRId64 "]", id);
last_id = id;
insert ();
}
void VeripbTracer::strengthen (int64_t id) {
if (file->closed ())
return;
LOG ("VERIPB TRACER tracing strengthen of clause[%" PRId64 "]", id);
veripb_strengthen (id);
}
/*------------------------------------------------------------------------*/
bool VeripbTracer::closed () { return file->closed (); }
#ifndef CADICAL_QUIET
void VeripbTracer::print_statistics () {
// TODO complete
uint64_t bytes = file->bytes ();
uint64_t total = added + deleted;
MSG ("VeriPB %" PRId64 " added clauses %.2f%%", added,
percent (added, total));
MSG ("VeriPB %" PRId64 " deleted clauses %.2f%%", deleted,
percent (deleted, total));
MSG ("VeriPB %" PRId64 " bytes (%.2f MB)", bytes,
bytes / (double) (1 << 20));
}
#endif
void VeripbTracer::close (bool print) {
CADICAL_assert (!closed ());
file->close ();
#ifndef CADICAL_QUIET
if (print) {
MSG ("VeriPB proof file '%s' closed", file->name ());
print_statistics ();
}
#else
(void) print;
#endif
}
void VeripbTracer::flush (bool print) {
CADICAL_assert (!closed ());
file->flush ();
#ifndef CADICAL_QUIET
if (print) {
MSG ("VeriPB proof file '%s' flushed", file->name ());
print_statistics ();
}
#else
(void) print;
#endif
}
} // namespace CaDiCaL
ABC_NAMESPACE_IMPL_END

View File

@ -1,113 +0,0 @@
#include "global.h"
/*------------------------------------------------------------------------*/
// To simplify the build process without 'make', you can disable the
// generation of 'build.hpp' through '../scripts/make-build-header.sh' by
// defining '-DCADICAL_NBUILD'. Then we try to guess part of the configuration.
#ifndef CADICAL_NBUILD
#if __GNUC__ > 4
#if __has_include(<build.hpp>)
#include "build.hpp"
#endif // __has_include
#else
#include "build.hpp"
#endif // __GNUC > 4
#endif // CADICAL_NBUILD
/*------------------------------------------------------------------------*/
// We prefer short 3 character version numbers made of digits and lower case
// letters only, which gives 36^3 = 46656 different versions. The following
// macro is used for the non-standard build process and only set from
// the file '../VERSION' with '../scripts/update-version.sh'. The standard
// build process relies on 'VERSION' to be defined in 'build.hpp'.
#ifdef CADICAL_NBUILD
#ifndef VERSION
#define VERSION "2.2.0-rc1"
#endif // VERSION
#endif // CADICAL_NBUILD
/*------------------------------------------------------------------------*/
// The copyright of the code is here.
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 =
"JKU Linz, University of Freiburg, TU Wien";
/*------------------------------------------------------------------------*/
// Again if we do not have 'CADICAL_NBUILD' or if something during configuration is
// broken we still want to be able to compile the solver. In this case we
// then try our best to figure out 'COMPILER' and 'DATE', but for
// 'IDENTIFIER' and 'FLAGS' we can only use the '0' string, which marks
// those as unknown.
#ifndef COMPILER
#ifdef __clang__
#ifdef __VERSION__
#define COMPILER "clang++-" __VERSION__
#else
#define COMPILER "clang++"
#endif
#elif defined(__GNUC__)
#ifdef __VERSION__
#define COMPILER "g++-" __VERSION__
#else
#define COMPILER "g++"
#endif
#else
#define COMPILER 0
#endif
#endif
// GIT SHA2 identifier.
//
#ifndef IDENTIFIER
#define IDENTIFIER 0
#endif
#ifdef SHORTID
#define SHORTIDSTR "-" SHORTID
#else
#define SHORTIDSTR ""
#define SHORTID 0
#endif
// Compilation flags.
//
#ifndef FLAGS
#define FLAGS 0
#endif
// Build Time and operating system.
//
#ifndef DATE
#define DATE __DATE__ " " __TIME__
#endif
/*------------------------------------------------------------------------*/
#include "version.hpp"
ABC_NAMESPACE_IMPL_START
namespace CaDiCaL {
const char *version () { return VERSION; }
const char *copyright () { return COPYRIGHT; }
const char *authors () { return AUTHORS; }
const char *affiliations () { return AFFILIATIONS; }
const char *signature () { return "cadical-" VERSION SHORTIDSTR; }
const char *identifier () { return IDENTIFIER; }
const char *compiler () { return COMPILER; }
const char *date () { return DATE; }
const char *flags () { return FLAGS; }
} // namespace CaDiCaL
ABC_NAMESPACE_IMPL_END

File diff suppressed because it is too large Load Diff

Some files were not shown because too many files have changed in this diff Show More