From 11c4f998b25b6c07f4081cea10e2532ebcb7f4d4 Mon Sep 17 00:00:00 2001 From: Sean Cross Date: Sat, 19 Oct 2019 12:36:14 +0800 Subject: [PATCH 01/79] Makefile: break apart steps in `make clean` The `make clean` target consists of a single `rm` call that passes every generated file, object file, and dependency directory. This results in a command line that's around 53,800 characters long. On Linux, the maximum length of a command line is 131,072 or 262,144 characters, however on Windows the limit is 32,768. The 53,800 character command simply fails to run on Windows, which is a problem when the first command that gets run is `make clean`. Break this target into steps, first removing the output files, then the object files, then any generated garbage, and then the object depedency directories. This fixes `make clean` (and as a result yosys) on Windows. Signed-off-by: Sean Cross --- Makefile | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/Makefile b/Makefile index 583bc25bc6..641cd6ac1f 100644 --- a/Makefile +++ b/Makefile @@ -206,7 +206,10 @@ depend: $(DEP) clean: @echo "$(MSG_PREFIX)\`\` Cleaning up..." - $(VERBOSE)rm -rvf $(PROG) lib$(PROG).a $(OBJ) $(GARBAGE) $(OBJ:.o=.d) + $(VERBOSE)rm -rvf $(PROG) lib$(PROG).a + $(VERBOSE)rm -rvf $(OBJ) + $(VERBOSE)rm -rvf $(GARBAGE) + $(VERBOSE)rm -rvf $(OBJ:.o=.d) tags: etags `find . -type f -regex '.*\.\(c\|h\)'` From 09607e9055381f6e330a054ee600e7bd7117bb76 Mon Sep 17 00:00:00 2001 From: whitequark Date: Thu, 30 Apr 2020 17:02:04 +0000 Subject: [PATCH 02/79] Add support for WASI platform in tmpFile. --- src/misc/util/utilFile.c | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/src/misc/util/utilFile.c b/src/misc/util/utilFile.c index 4bb2f4c68a..b6835daa2a 100644 --- a/src/misc/util/utilFile.c +++ b/src/misc/util/utilFile.c @@ -102,6 +102,17 @@ int tmpFile(const char* prefix, const char* suffix, char** out_name) } assert(0); // -- could not open temporary file return 0; +#elif defined(__wasm) + static int seq = 0; // no risk of collision since we're in a sandbox + int fd; + *out_name = (char*)malloc(strlen(prefix) + strlen(suffix) + 9); + sprintf(*out_name, "%s%08d%s", prefix, seq++, suffix); + fd = open(*out_name, O_CREAT | O_EXCL | O_RDWR, S_IREAD | S_IWRITE); + if (fd == -1){ + free(*out_name); + *out_name = NULL; + } + return fd; #else int fd; *out_name = (char*)malloc(strlen(prefix) + strlen(suffix) + 7); From eea20ff4667abf36f28679520b06f4b1a6fcacea Mon Sep 17 00:00:00 2001 From: whitequark Date: Thu, 30 Apr 2020 17:05:49 +0000 Subject: [PATCH 03/79] Add support for WASI platform in cmdCheckShellEscape. Since cmdCheckShellEscape doesn't actually report failure in any way, this code simulates a situation where system() never succeeds. --- src/base/cmd/cmdUtils.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/base/cmd/cmdUtils.c b/src/base/cmd/cmdUtils.c index 3409543f15..c10e913411 100644 --- a/src/base/cmd/cmdUtils.c +++ b/src/base/cmd/cmdUtils.c @@ -52,6 +52,9 @@ int cmdCheckShellEscape( Abc_Frame_t * pAbc, int argc, char ** argv) int RetValue; if (argv[0][0] == '!') { +#if defined(__wasm) + RetValue = -1; +#else const int size = 4096; int i; char * buffer = ABC_ALLOC(char, 10000); @@ -70,7 +73,7 @@ int cmdCheckShellEscape( Abc_Frame_t * pAbc, int argc, char ** argv) // the parts, we lose information. So a command like // `!ls "file name"` will be sent to the system as // `ls file name` which is a BUG - +#endif return 1; } else From 70ed4da2acbb3ae61d5fbfbd6e02da94cc38da05 Mon Sep 17 00:00:00 2001 From: whitequark Date: Thu, 30 Apr 2020 17:08:33 +0000 Subject: [PATCH 04/79] Add support for WASI platform in Gia_ManGnuplotShow. --- src/base/cmd/cmd.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/base/cmd/cmd.c b/src/base/cmd/cmd.c index a00424432e..259c9d7804 100644 --- a/src/base/cmd/cmd.c +++ b/src/base/cmd/cmd.c @@ -2175,7 +2175,11 @@ void Gia_ManGnuplotShow( char * pPlotFileName ) { char Command[1000]; sprintf( Command, "%s %s ", pProgNameGnuplot, pPlotFileName ); +#if defined(__wasm) + if ( 1 ) +#else if ( system( Command ) == -1 ) +#endif { fprintf( stdout, "Cannot execute \"%s\".\n", Command ); return; From 9366e4fa68fe73ba0f514e20f589dab0f5489e56 Mon Sep 17 00:00:00 2001 From: whitequark Date: Thu, 30 Apr 2020 17:12:36 +0000 Subject: [PATCH 05/79] Add support for WASI platform in Abc_ShowFile. --- src/base/abc/abcShow.c | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/base/abc/abcShow.c b/src/base/abc/abcShow.c index f91397dffb..7df1e323f9 100644 --- a/src/base/abc/abcShow.c +++ b/src/base/abc/abcShow.c @@ -363,7 +363,11 @@ void Abc_ShowFile( char * FileNameDot ) // generate the PostScript file using DOT sprintf( CommandDot, "%s -Tps -o %s %s", pDotName, FileNamePs, FileNameDot ); +#if defined(__wasm) + RetValue = -1; +#else RetValue = system( CommandDot ); +#endif if ( RetValue == -1 ) { fprintf( stdout, "Command \"%s\" did not succeed.\n", CommandDot ); @@ -401,7 +405,11 @@ void Abc_ShowFile( char * FileNameDot ) char CommandPs[1000]; unlink( FileNameDot ); sprintf( CommandPs, "%s %s &", pGsNameUnix, FileNamePs ); +#if defined(__wasm) + if ( 1 ) +#else if ( system( CommandPs ) == -1 ) +#endif { fprintf( stdout, "Cannot execute \"%s\".\n", CommandPs ); return; From 2db5f19ab6f86a11d131add176222d0260593a57 Mon Sep 17 00:00:00 2001 From: whitequark Date: Thu, 30 Apr 2020 17:12:47 +0000 Subject: [PATCH 06/79] Add support for WASI platform in Util_SignalSystem. --- src/misc/util/utilSignal.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/misc/util/utilSignal.c b/src/misc/util/utilSignal.c index 03af81d13d..137ff54b81 100644 --- a/src/misc/util/utilSignal.c +++ b/src/misc/util/utilSignal.c @@ -43,7 +43,11 @@ ABC_NAMESPACE_IMPL_START int Util_SignalSystem(const char* cmd) { +#if defined(__wasm) + return -1; +#else return system(cmd); +#endif } int tmpFile(const char* prefix, const char* suffix, char** out_name); From fd2c9b1c19216f6b756f88b18f5ca67b759ca128 Mon Sep 17 00:00:00 2001 From: whitequark Date: Thu, 30 Apr 2020 18:41:25 +0000 Subject: [PATCH 07/79] Remove ABC_NO_RLIMIT macro, use defined(__wasm) instead. --- src/base/main/mainReal.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/base/main/mainReal.c b/src/base/main/mainReal.c index 922e052158..68c0c0322d 100644 --- a/src/base/main/mainReal.c +++ b/src/base/main/mainReal.c @@ -132,7 +132,7 @@ int Abc_RealMain( int argc, char * argv[] ) break; case 'm': { -#if !defined(WIN32) && !defined(ABC_NO_RLIMIT) +#if !defined(WIN32) && !defined(__wasm) int maxMb = atoi(globalUtilOptarg); printf("Limiting memory use to %d MB\n", maxMb); struct rlimit limit = { @@ -144,7 +144,7 @@ int Abc_RealMain( int argc, char * argv[] ) break; } case 'l': { -#if !defined(WIN32) && !defined(ABC_NO_RLIMIT) +#if !defined(WIN32) && !defined(__wasm) rlim_t maxTime = atoi(globalUtilOptarg); printf("Limiting time to %d seconds\n", (int)maxTime); struct rlimit limit = { From ef2d917562dadb7efc0780aa717ffe0474a4134f Mon Sep 17 00:00:00 2001 From: whitequark Date: Mon, 22 Jun 2020 02:51:42 +0000 Subject: [PATCH 08/79] Add WASI platform support to main. --- src/base/main/mainReal.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/base/main/mainReal.c b/src/base/main/mainReal.c index 68c0c0322d..a13be5e525 100644 --- a/src/base/main/mainReal.c +++ b/src/base/main/mainReal.c @@ -49,7 +49,9 @@ SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. #include #include #include +#if !defined(__wasm) #include +#endif #include #endif From 81febe5d69ddd98f23ec645465500effdcddd1ae Mon Sep 17 00:00:00 2001 From: whitequark Date: Mon, 22 Jun 2020 02:51:26 +0000 Subject: [PATCH 09/79] Add WASI platform support to glucose. --- src/sat/glucose/IntTypes.h | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/src/sat/glucose/IntTypes.h b/src/sat/glucose/IntTypes.h index 3f75862b1b..5c4176b292 100644 --- a/src/sat/glucose/IntTypes.h +++ b/src/sat/glucose/IntTypes.h @@ -28,20 +28,18 @@ OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWA # include # include -#else +#elif _WIN32 -#define __STDC_LIMIT_MACROS # include "pstdint.h" -//# include -#endif +#else -#include +# define __STDC_LIMIT_MACROS +# include +# include -#ifndef PRIu64 -#define PRIu64 "lu" -#define PRIi64 "ld" #endif + //================================================================================================= #include From 2343da6bf1f41995b41a6c49d8a4993df0f7bcd1 Mon Sep 17 00:00:00 2001 From: Baruch Sterin Date: Thu, 19 Nov 2020 22:27:24 +0200 Subject: [PATCH 10/79] Makefile: support ccache for compiling ABC. Surround $(CC), $(CXX) with double quotes when calling depends.sh, to allow space-delimited compilation tools to be used. --- Makefile | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Makefile b/Makefile index 641cd6ac1f..ee4172f508 100644 --- a/Makefile +++ b/Makefile @@ -186,15 +186,15 @@ DEP := $(OBJ:.o=.d) %.d: %.c @echo "$(MSG_PREFIX)\`\` Generating dependency:" $(LOCAL_PATH)/$< - $(VERBOSE)$(ABCSRC)/depends.sh $(CC) `dirname $*.c` $(OPTFLAGS) $(INCLUDES) $(CFLAGS) $< > $@ + $(VERBOSE)$(ABCSRC)/depends.sh "$(CC)" `dirname $*.c` $(OPTFLAGS) $(INCLUDES) $(CFLAGS) $< > $@ %.d: %.cc @echo "$(MSG_PREFIX)\`\` Generating dependency:" $(LOCAL_PATH)/$< - $(VERBOSE)$(ABCSRC)/depends.sh $(CXX) `dirname $*.cc` $(OPTFLAGS) $(INCLUDES) $(CXXFLAGS) $< > $@ + $(VERBOSE)$(ABCSRC)/depends.sh "$(CXX)" `dirname $*.cc` $(OPTFLAGS) $(INCLUDES) $(CXXFLAGS) $< > $@ %.d: %.cpp @echo "$(MSG_PREFIX)\`\` Generating dependency:" $(LOCAL_PATH)/$< - $(VERBOSE)$(ABCSRC)/depends.sh $(CXX) `dirname $*.cpp` $(OPTFLAGS) $(INCLUDES) $(CXXFLAGS) $< > $@ + $(VERBOSE)$(ABCSRC)/depends.sh "$(CXX)" `dirname $*.cpp` $(OPTFLAGS) $(INCLUDES) $(CXXFLAGS) $< > $@ ifndef ABC_MAKE_NO_DEPS -include $(DEP) From e289a8059eac7d0514f8a20ffe6464fc18db1450 Mon Sep 17 00:00:00 2001 From: whitequark Date: Tue, 29 Dec 2020 13:35:31 +0000 Subject: [PATCH 11/79] Add WASI platform support to bsat2 and glucose. Abort on OOM since there are no C++ exceptions yet. Signed-off-by: Miodrag Milanovic --- src/sat/bsat2/Alloc.h | 8 ++++++++ src/sat/bsat2/Vec.h | 4 ++++ src/sat/bsat2/XAlloc.h | 4 ++++ src/sat/glucose/Alloc.h | 8 ++++++++ src/sat/glucose/Vec.h | 4 ++++ src/sat/glucose/XAlloc.h | 4 ++++ 6 files changed, 32 insertions(+) diff --git a/src/sat/bsat2/Alloc.h b/src/sat/bsat2/Alloc.h index 7f506cb5a9..9a65cf0cd2 100644 --- a/src/sat/bsat2/Alloc.h +++ b/src/sat/bsat2/Alloc.h @@ -97,7 +97,11 @@ void RegionAllocator::capacity(uint32_t min_cap) cap += delta; if (cap <= prev_cap) +#ifdef __wasm + abort(); +#else throw OutOfMemoryException(); +#endif } // printf(" .. (%p) cap = %u\n", this, cap); @@ -119,7 +123,11 @@ RegionAllocator::alloc(int size) // Handle overflow: if (sz < prev_sz) +#ifdef __wasm + abort(); +#else throw OutOfMemoryException(); +#endif return prev_sz; } diff --git a/src/sat/bsat2/Vec.h b/src/sat/bsat2/Vec.h index f0e07d016a..5eea6174b5 100644 --- a/src/sat/bsat2/Vec.h +++ b/src/sat/bsat2/Vec.h @@ -97,7 +97,11 @@ void vec::capacity(int min_cap) { if (cap >= min_cap) return; int add = imax((min_cap - cap + 1) & ~1, ((cap >> 1) + 2) & ~1); // NOTE: grow by approximately 3/2 if (add > INT_MAX - cap || (((data = (T*)::realloc(data, (cap += add) * sizeof(T))) == NULL) && errno == ENOMEM)) +#ifdef __wasm + abort(); +#else throw OutOfMemoryException(); +#endif } diff --git a/src/sat/bsat2/XAlloc.h b/src/sat/bsat2/XAlloc.h index 1da176028d..33741e3329 100644 --- a/src/sat/bsat2/XAlloc.h +++ b/src/sat/bsat2/XAlloc.h @@ -34,7 +34,11 @@ static inline void* xrealloc(void *ptr, size_t size) { void* mem = realloc(ptr, size); if (mem == NULL && errno == ENOMEM){ +#ifdef __wasm + abort(); +#else throw OutOfMemoryException(); +#endif }else return mem; } diff --git a/src/sat/glucose/Alloc.h b/src/sat/glucose/Alloc.h index e56b54414d..a63de032fe 100644 --- a/src/sat/glucose/Alloc.h +++ b/src/sat/glucose/Alloc.h @@ -100,7 +100,11 @@ void RegionAllocator::capacity(uint32_t min_cap) cap += delta; if (cap <= prev_cap) +#ifdef __wasm + abort(); +#else throw OutOfMemoryException(); +#endif } //printf(" .. (%p) cap = %u\n", this, cap); @@ -122,7 +126,11 @@ RegionAllocator::alloc(int size) // Handle overflow: if (sz < prev_sz) +#ifdef __wasm + abort(); +#else throw OutOfMemoryException(); +#endif return prev_sz; } diff --git a/src/sat/glucose/Vec.h b/src/sat/glucose/Vec.h index dd1bc20a1c..d2781635c8 100644 --- a/src/sat/glucose/Vec.h +++ b/src/sat/glucose/Vec.h @@ -100,7 +100,11 @@ void vec::capacity(int min_cap) { if (cap >= min_cap) return; int add = imax((min_cap - cap + 1) & ~1, ((cap >> 1) + 2) & ~1); // NOTE: grow by approximately 3/2 if (add > INT_MAX - cap || (((data = (T*)::realloc(data, (cap += add) * sizeof(T))) == NULL) && errno == ENOMEM)) +#ifdef __wasm + abort(); +#else throw OutOfMemoryException(); +#endif } diff --git a/src/sat/glucose/XAlloc.h b/src/sat/glucose/XAlloc.h index 233f834e02..d1f1062afa 100644 --- a/src/sat/glucose/XAlloc.h +++ b/src/sat/glucose/XAlloc.h @@ -39,7 +39,11 @@ static inline void* xrealloc(void *ptr, size_t size) { void* mem = realloc(ptr, size); if (mem == NULL && errno == ENOMEM){ +#ifdef __wasm + abort(); +#else throw OutOfMemoryException(); +#endif }else { return mem; } From e792072f8a6f016eb5f2c8653f261ba7c88e6392 Mon Sep 17 00:00:00 2001 From: "Mohamed A. Bamakhrama" Date: Mon, 3 Aug 2020 23:19:23 +0200 Subject: [PATCH 12/79] Define S_IREAD|IWRITE macros using IRUSR|IWUSR On platforms such as Android, legacy macros are no longer defined. Hence, we define them in terms of the new POSIX macros if the new ones are defined. Otherwise, we throw an error. Signed-off-by: Mohamed A. Bamakhrama Signed-off-by: Miodrag Milanovic --- src/misc/util/utilFile.c | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/src/misc/util/utilFile.c b/src/misc/util/utilFile.c index b6835daa2a..f64d71c24a 100644 --- a/src/misc/util/utilFile.c +++ b/src/misc/util/utilFile.c @@ -25,6 +25,23 @@ #include #include +// Handle legacy macros +#if !defined(S_IREAD) +#if defined(S_IRUSR) +#define S_IREAD S_IRUSR +#else +#error S_IREAD is undefined +#endif +#endif + +#if !defined(S_IWRITE) +#if defined(S_IWUSR) +#define S_IWRITE S_IWUSR +#else +#error S_IWRITE is undefined +#endif +#endif + #if defined(_MSC_VER) || defined(__MINGW32__) #include #include From f6fa2ddcfc89099726d60386befba874c7ac1e0d Mon Sep 17 00:00:00 2001 From: Miodrag Milanovic Date: Thu, 11 Nov 2021 18:08:50 +0100 Subject: [PATCH 13/79] Add WASI platform support to glucose2. Signed-off-by: Miodrag Milanovic --- src/sat/glucose2/IntTypes.h | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/src/sat/glucose2/IntTypes.h b/src/sat/glucose2/IntTypes.h index 3f75862b1b..5c4176b292 100644 --- a/src/sat/glucose2/IntTypes.h +++ b/src/sat/glucose2/IntTypes.h @@ -28,20 +28,18 @@ OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWA # include # include -#else +#elif _WIN32 -#define __STDC_LIMIT_MACROS # include "pstdint.h" -//# include -#endif +#else -#include +# define __STDC_LIMIT_MACROS +# include +# include -#ifndef PRIu64 -#define PRIu64 "lu" -#define PRIi64 "ld" #endif + //================================================================================================= #include From 87a0a718c9c86b0931777914f5185823f5a96131 Mon Sep 17 00:00:00 2001 From: Miodrag Milanovic Date: Fri, 19 Nov 2021 16:22:50 +0100 Subject: [PATCH 14/79] write_cex - add minimize using algorithm from cexinfo command --- src/base/io/io.c | 40 ++++++++++++++++++++++++++++++++++++---- 1 file changed, 36 insertions(+), 4 deletions(-) diff --git a/src/base/io/io.c b/src/base/io/io.c index 5cf74ef94c..f254181a7e 100644 --- a/src/base/io/io.c +++ b/src/base/io/io.c @@ -2441,8 +2441,12 @@ void Abc_NtkDumpOneCexSpecial( FILE * pFile, Abc_Ntk_t * pNtk, Abc_Cex_t * pCex } +extern Abc_Cex_t * Bmc_CexInnerStates( Gia_Man_t * p, Abc_Cex_t * pCex, Abc_Cex_t ** ppCexImpl, int fVerbose ); +extern Abc_Cex_t * Bmc_CexEssentialBits( Gia_Man_t * p, Abc_Cex_t * pCexState, Abc_Cex_t * pCexCare, int fVerbose ); +extern Abc_Cex_t * Bmc_CexCareBits( Gia_Man_t * p, Abc_Cex_t * pCexState, Abc_Cex_t * pCexImpl, Abc_Cex_t * pCexEss, int fFindAll, int fVerbose ); + void Abc_NtkDumpOneCex( FILE * pFile, Abc_Ntk_t * pNtk, Abc_Cex_t * pCex, - int fPrintFull, int fNames, int fUseFfNames, int fMinimize, int fUseOldMin, + int fPrintFull, int fNames, int fUseFfNames, int fMinimize, int fUseOldMin, int fCexInfo, int fCheckCex, int fUseSatBased, int fHighEffort, int fAiger, int fVerbose ) { Abc_Obj_t * pObj; @@ -2477,6 +2481,29 @@ void Abc_NtkDumpOneCex( FILE * pFile, Abc_Ntk_t * pNtk, Abc_Cex_t * pCex, } else if ( fUseSatBased ) pCare = Bmc_CexCareSatBasedMinimize( pAig, Saig_ManPiNum(pAig), pCex, fHighEffort, fCheckCex, fVerbose ); + else if ( fCexInfo ) + { + Gia_Man_t * p = Gia_ManFromAigSimple( pAig ); + Abc_Cex_t * pCexImpl = NULL; + Abc_Cex_t * pCexStates = Bmc_CexInnerStates( p, pCex, &pCexImpl, fVerbose ); + Abc_Cex_t * pCexCare = Bmc_CexCareBits( p, pCexStates, pCexImpl, NULL, 1, fVerbose ); + Abc_Cex_t * pCexEss; + + if ( fCheckCex && !Bmc_CexVerify( p, pCex, pCexCare ) ) + printf( "Counter-example care-set verification has failed.\n" ); + + pCexEss = Bmc_CexEssentialBits( p, pCexStates, pCexCare, fVerbose ); + + // pCare is pCexMin from Bmc_CexTest + pCare = Bmc_CexCareBits( p, pCexStates, pCexImpl, pCexEss, 0, fVerbose ); + + if ( fCheckCex && !Bmc_CexVerify( p, pCex, pCare ) ) + printf( "Counter-example min-set verification has failed.\n" ); + Abc_CexFreeP( &pCexStates ); + Abc_CexFreeP( &pCexImpl ); + Abc_CexFreeP( &pCexCare ); + Abc_CexFreeP( &pCexEss ); + } else pCare = Bmc_CexCareMinimize( pAig, Saig_ManPiNum(pAig), pCex, 4, fCheckCex, fVerbose ); Aig_ManStop( pAig ); @@ -2581,9 +2608,10 @@ int IoCommandWriteCex( Abc_Frame_t * pAbc, int argc, char **argv ) int fPrintFull = 0; int fUseFfNames = 0; int fVerbose = 0; + int fCexInfo = 0; Extra_UtilGetoptReset(); - while ( ( c = Extra_UtilGetopt( argc, argv, "snmueocafzvh" ) ) != EOF ) + while ( ( c = Extra_UtilGetopt( argc, argv, "snmueocafzvhx" ) ) != EOF ) { switch ( c ) { @@ -2620,6 +2648,9 @@ int IoCommandWriteCex( Abc_Frame_t * pAbc, int argc, char **argv ) case 'v': fVerbose ^= 1; break; + case 'x': + fCexInfo ^= 1; + break; case 'h': goto usage; default: @@ -2668,7 +2699,7 @@ int IoCommandWriteCex( Abc_Frame_t * pAbc, int argc, char **argv ) if ( pAbc->pCex ) { Abc_NtkDumpOneCex( pFile, pNtk, pCex, - fPrintFull, fNames, fUseFfNames, fMinimize, fUseOldMin, + fPrintFull, fNames, fUseFfNames, fMinimize, fUseOldMin, fCexInfo, fCheckCex, fUseSatBased, fHighEffort, fAiger, fVerbose ); } else if ( pAbc->vCexVec ) @@ -2679,7 +2710,7 @@ int IoCommandWriteCex( Abc_Frame_t * pAbc, int argc, char **argv ) continue; fprintf( pFile, "#\n#\n# CEX for output %d\n#\n", i ); Abc_NtkDumpOneCex( pFile, pNtk, pCex, - fPrintFull, fNames, fUseFfNames, fMinimize, fUseOldMin, + fPrintFull, fNames, fUseFfNames, fMinimize, fUseOldMin, fCexInfo, fCheckCex, fUseSatBased, fHighEffort, fAiger, fVerbose ); } } @@ -2724,6 +2755,7 @@ int IoCommandWriteCex( Abc_Frame_t * pAbc, int argc, char **argv ) fprintf( pAbc->Err, "\t-u : use fast SAT-based CEX minimization [default = %s]\n", fUseSatBased? "yes": "no" ); fprintf( pAbc->Err, "\t-e : use high-effort SAT-based CEX minimization [default = %s]\n", fHighEffort? "yes": "no" ); fprintf( pAbc->Err, "\t-o : use old CEX minimization algorithm [default = %s]\n", fUseOldMin? "yes": "no" ); + fprintf( pAbc->Err, "\t-x : minimize using algorithm from cexinfo command [default = %s]\n", fCexInfo? "yes": "no" ); fprintf( pAbc->Err, "\t-c : check generated CEX using ternary simulation [default = %s]\n", fCheckCex? "yes": "no" ); fprintf( pAbc->Err, "\t-a : print cex in AIGER 1.9 format [default = %s]\n", fAiger? "yes": "no" ); fprintf( pAbc->Err, "\t-f : enable printing flop values in each timeframe [default = %s]\n", fPrintFull? "yes": "no" ); From 264dfc7ed414476438e4a858258f5516d7b863c6 Mon Sep 17 00:00:00 2001 From: whitequark Date: Sat, 27 Nov 2021 07:57:15 +0000 Subject: [PATCH 15/79] Extend WASI platform support for glucose2. Abort on OOM since there are no C++ exceptions yet. --- src/sat/glucose2/Alloc.h | 8 ++++++++ src/sat/glucose2/Vec.h | 8 ++++++++ src/sat/glucose2/XAlloc.h | 4 ++++ 3 files changed, 20 insertions(+) diff --git a/src/sat/glucose2/Alloc.h b/src/sat/glucose2/Alloc.h index b7bebacac1..427cd32325 100644 --- a/src/sat/glucose2/Alloc.h +++ b/src/sat/glucose2/Alloc.h @@ -100,7 +100,11 @@ void RegionAllocator::capacity(uint32_t min_cap) cap += delta; if (cap <= prev_cap) +#ifdef __wasm + abort(); +#else throw OutOfMemoryException(); +#endif } //printf(" .. (%p) cap = %u\n", this, cap); @@ -122,7 +126,11 @@ RegionAllocator::alloc(int size) // Handle overflow: if (sz < prev_sz) +#ifdef __wasm + abort(); +#else throw OutOfMemoryException(); +#endif return prev_sz; } diff --git a/src/sat/glucose2/Vec.h b/src/sat/glucose2/Vec.h index eaeed20748..bc9892177d 100644 --- a/src/sat/glucose2/Vec.h +++ b/src/sat/glucose2/Vec.h @@ -102,14 +102,22 @@ void vec::capacity(int min_cap) { if (cap >= min_cap) return; int add = imax((min_cap - cap + 1) & ~1, ((cap >> 1) + 2) & ~1); // NOTE: grow by approximately 3/2 if (add > INT_MAX - cap || (((data = (T*)::realloc(data, (cap += add) * sizeof(T))) == NULL) && errno == ENOMEM)) +#ifdef __wasm + abort(); +#else throw OutOfMemoryException(); +#endif } template void vec::prelocate(int ext_cap) { if (cap >= ext_cap) return; if (ext_cap > INT_MAX || (((data = (T*)::realloc(data, ext_cap * sizeof(T))) == NULL) && errno == ENOMEM)) +#ifdef __wasm + abort(); +#else throw OutOfMemoryException(); +#endif cap = ext_cap; } diff --git a/src/sat/glucose2/XAlloc.h b/src/sat/glucose2/XAlloc.h index 716643ef86..86e65a4947 100644 --- a/src/sat/glucose2/XAlloc.h +++ b/src/sat/glucose2/XAlloc.h @@ -39,7 +39,11 @@ static inline void* xrealloc(void *ptr, size_t size) { void* mem = realloc(ptr, size); if (mem == NULL && errno == ENOMEM){ +#ifdef __wasm + abort(); +#else throw OutOfMemoryException(); +#endif }else { return mem; } From 1aeff0325e20f2f4109284f328e4f32e2035187e Mon Sep 17 00:00:00 2001 From: Claire Xenia Wolf Date: Tue, 15 Feb 2022 16:05:47 +0100 Subject: [PATCH 16/79] Enable writing of minimized Cex in non-names mode Signed-off-by: Claire Xenia Wolf --- src/base/io/io.c | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/src/base/io/io.c b/src/base/io/io.c index f254181a7e..529c444bea 100644 --- a/src/base/io/io.c +++ b/src/base/io/io.c @@ -2449,6 +2449,7 @@ void Abc_NtkDumpOneCex( FILE * pFile, Abc_Ntk_t * pNtk, Abc_Cex_t * pCex, int fPrintFull, int fNames, int fUseFfNames, int fMinimize, int fUseOldMin, int fCexInfo, int fCheckCex, int fUseSatBased, int fHighEffort, int fAiger, int fVerbose ) { + Abc_Cex_t * pCare = NULL; Abc_Obj_t * pObj; int i, f; if ( fPrintFull ) @@ -2464,9 +2465,8 @@ void Abc_NtkDumpOneCex( FILE * pFile, Abc_Ntk_t * pNtk, Abc_Cex_t * pCex, fprintf( pFile, "%s@%d=%c ", Abc_ObjName(pObj), f, '0'+Abc_InfoHasBit(pCexFull->pData, Abc_NtkCiNum(pNtk)*f + i) ); Abc_CexFreeP( &pCexFull ); } - else if ( fNames ) + else if ( fNames || fMinimize ) { - Abc_Cex_t * pCare = NULL; if ( fMinimize ) { extern Aig_Man_t * Abc_NtkToDar( Abc_Ntk_t * pNtk, int fExors, int fRegisters ); @@ -2509,6 +2509,8 @@ void Abc_NtkDumpOneCex( FILE * pFile, Abc_Ntk_t * pNtk, Abc_Cex_t * pCex, Aig_ManStop( pAig ); if(pCare == NULL) printf( "Counter-example minimization has failed.\n" ); + if (!fNames) + goto no_names; } else { @@ -2570,6 +2572,7 @@ void Abc_NtkDumpOneCex( FILE * pFile, Abc_Ntk_t * pNtk, Abc_Cex_t * pCex, } else { + no_names: Abc_NtkForEachLatch( pNtk, pObj, i ) fprintf( pFile, "%c", '0'+!Abc_LatchIsInit0(pObj) ); @@ -2577,8 +2580,9 @@ void Abc_NtkDumpOneCex( FILE * pFile, Abc_Ntk_t * pNtk, Abc_Cex_t * pCex, { if ( fAiger && (i-pCex->nRegs)%pCex->nPis == 0) fprintf( pFile, "\n"); - fprintf( pFile, "%c", '0'+Abc_InfoHasBit(pCex->pData, i) ); + fprintf( pFile, "%c", (pCare && !Abc_InfoHasBit(pCare->pData, i)) ? 'x' : '0'+Abc_InfoHasBit(pCex->pData, i) ); } + Abc_CexFreeP( &pCare ); } } From db7ebfb4349f5c2c52f9cd58ab4d9c239ae25cb4 Mon Sep 17 00:00:00 2001 From: Claire Xenia Wolf Date: Tue, 15 Feb 2022 16:40:56 +0100 Subject: [PATCH 17/79] Cleanups in write_cex output format Signed-off-by: Claire Xenia Wolf --- src/base/io/io.c | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/src/base/io/io.c b/src/base/io/io.c index 529c444bea..65946def9b 100644 --- a/src/base/io/io.c +++ b/src/base/io/io.c @@ -2471,8 +2471,11 @@ void Abc_NtkDumpOneCex( FILE * pFile, Abc_Ntk_t * pNtk, Abc_Cex_t * pCex, { extern Aig_Man_t * Abc_NtkToDar( Abc_Ntk_t * pNtk, int fExors, int fRegisters ); Aig_Man_t * pAig = Abc_NtkToDar( pNtk, 0, 1 ); - fprintf( pFile, "# FALSIFYING OUTPUTS:"); - fprintf( pFile, " %s", Abc_ObjName(Abc_NtkCo(pNtk, pCex->iPo)) ); + if ( fNames ) + { + fprintf( pFile, "# FALSIFYING OUTPUTS:"); + fprintf( pFile, " %s", Abc_ObjName(Abc_NtkCo(pNtk, pCex->iPo)) ); + } if ( fUseOldMin ) { pCare = Saig_ManCbaFindCexCareBits( pAig, pCex, 0, fVerbose ); @@ -2582,6 +2585,8 @@ void Abc_NtkDumpOneCex( FILE * pFile, Abc_Ntk_t * pNtk, Abc_Cex_t * pCex, fprintf( pFile, "\n"); fprintf( pFile, "%c", (pCare && !Abc_InfoHasBit(pCare->pData, i)) ? 'x' : '0'+Abc_InfoHasBit(pCex->pData, i) ); } + if ( fAiger ) + fprintf( pFile, "\n"); Abc_CexFreeP( &pCare ); } } From cea4130350f0334bc4b4f01f6285515da83fb901 Mon Sep 17 00:00:00 2001 From: Claire Xenia Wolf Date: Tue, 15 Feb 2022 17:55:10 +0100 Subject: [PATCH 18/79] Fixes and more cleanups in write_cex output code Signed-off-by: Claire Xenia Wolf --- src/base/io/io.c | 61 +++++++++++++++++++++--------------------------- 1 file changed, 27 insertions(+), 34 deletions(-) diff --git a/src/base/io/io.c b/src/base/io/io.c index 65946def9b..77dbfe2a36 100644 --- a/src/base/io/io.c +++ b/src/base/io/io.c @@ -2465,17 +2465,17 @@ void Abc_NtkDumpOneCex( FILE * pFile, Abc_Ntk_t * pNtk, Abc_Cex_t * pCex, fprintf( pFile, "%s@%d=%c ", Abc_ObjName(pObj), f, '0'+Abc_InfoHasBit(pCexFull->pData, Abc_NtkCiNum(pNtk)*f + i) ); Abc_CexFreeP( &pCexFull ); } - else if ( fNames || fMinimize ) + else { + if ( fNames ) + { + fprintf( pFile, "# FALSIFYING OUTPUTS:"); + fprintf( pFile, " %s", Abc_ObjName(Abc_NtkCo(pNtk, pCex->iPo)) ); + } if ( fMinimize ) { extern Aig_Man_t * Abc_NtkToDar( Abc_Ntk_t * pNtk, int fExors, int fRegisters ); Aig_Man_t * pAig = Abc_NtkToDar( pNtk, 0, 1 ); - if ( fNames ) - { - fprintf( pFile, "# FALSIFYING OUTPUTS:"); - fprintf( pFile, " %s", Abc_ObjName(Abc_NtkCo(pNtk, pCex->iPo)) ); - } if ( fUseOldMin ) { pCare = Saig_ManCbaFindCexCareBits( pAig, pCex, 0, fVerbose ); @@ -2510,19 +2510,15 @@ void Abc_NtkDumpOneCex( FILE * pFile, Abc_Ntk_t * pNtk, Abc_Cex_t * pCex, else pCare = Bmc_CexCareMinimize( pAig, Saig_ManPiNum(pAig), pCex, 4, fCheckCex, fVerbose ); Aig_ManStop( pAig ); - if(pCare == NULL) + if(pCare == NULL) printf( "Counter-example minimization has failed.\n" ); - if (!fNames) - goto no_names; } - else + if (fNames) { - fprintf( pFile, "# FALSIFYING OUTPUTS:"); - fprintf( pFile, " %s", Abc_ObjName(Abc_NtkCo(pNtk, pCex->iPo)) ); + fprintf( pFile, "\n"); + fprintf( pFile, "# COUNTEREXAMPLE LENGTH: %u\n", pCex->iFrame+1); } - fprintf( pFile, "\n"); - fprintf( pFile, "# COUNTEREXAMPLE LENGTH: %u\n", pCex->iFrame+1); - if ( fUseFfNames && Abc_NtkCheckSpecialPi(pNtk) ) + if ( fNames && fUseFfNames && Abc_NtkCheckSpecialPi(pNtk) ) { int * pValues; int nXValues = 0, iFlop = 0, iPivotPi = -1; @@ -2564,29 +2560,26 @@ void Abc_NtkDumpOneCex( FILE * pFile, Abc_Ntk_t * pNtk, Abc_Cex_t * pCex, { // output flop values (unaffected by the minimization) Abc_NtkForEachLatch( pNtk, pObj, i ) - fprintf( pFile, "%s@0=%c\n", Abc_ObjName(Abc_ObjFanout0(pObj)), '0'+!Abc_LatchIsInit0(pObj) ); + if ( fNames ) + fprintf( pFile, "%s@0=%c\n", Abc_ObjName(Abc_ObjFanout0(pObj)), '0'+!Abc_LatchIsInit0(pObj) ); + else + fprintf( pFile, "%c", '0'+!Abc_LatchIsInit0(pObj) ); + if ( !fNames ) + fprintf( pFile, "\n"); // output PI values (while skipping the minimized ones) - for ( f = 0; f <= pCex->iFrame; f++ ) + for ( f = 0; f <= pCex->iFrame; f++ ) { Abc_NtkForEachPi( pNtk, pObj, i ) if ( !pCare || Abc_InfoHasBit(pCare->pData, pCare->nRegs+pCare->nPis*f + i) ) - fprintf( pFile, "%s@%d=%c\n", Abc_ObjName(pObj), f, '0'+Abc_InfoHasBit(pCex->pData, pCex->nRegs+pCex->nPis*f + i) ); - } - Abc_CexFreeP( &pCare ); - } - else - { - no_names: - Abc_NtkForEachLatch( pNtk, pObj, i ) - fprintf( pFile, "%c", '0'+!Abc_LatchIsInit0(pObj) ); - - for ( i = pCex->nRegs; i < pCex->nBits; i++ ) - { - if ( fAiger && (i-pCex->nRegs)%pCex->nPis == 0) - fprintf( pFile, "\n"); - fprintf( pFile, "%c", (pCare && !Abc_InfoHasBit(pCare->pData, i)) ? 'x' : '0'+Abc_InfoHasBit(pCex->pData, i) ); + if ( fNames ) + fprintf( pFile, "%s@%d=%c\n", Abc_ObjName(pObj), f, '0'+Abc_InfoHasBit(pCex->pData, pCex->nRegs+pCex->nPis*f + i) ); + else + fprintf( pFile, "%c", '0'+Abc_InfoHasBit(pCex->pData, pCex->nRegs+pCex->nPis*f + i) ); + else if ( !fNames ) + fprintf( pFile, "x"); + if ( !fNames ) + fprintf( pFile, "\n"); + } } - if ( fAiger ) - fprintf( pFile, "\n"); Abc_CexFreeP( &pCare ); } } From f36724e301a4b7b2a93c3ea9314363c8e91d0e6e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Miodrag=20Milanovi=C4=87?= Date: Fri, 4 Mar 2022 10:55:55 +0100 Subject: [PATCH 19/79] read_cex (#12) Added read_cex command --- src/base/io/io.c | 292 +++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 285 insertions(+), 7 deletions(-) diff --git a/src/base/io/io.c b/src/base/io/io.c index 77dbfe2a36..a83e997838 100644 --- a/src/base/io/io.c +++ b/src/base/io/io.c @@ -44,6 +44,7 @@ static int IoCommandReadBblif ( Abc_Frame_t * pAbc, int argc, char **argv ); static int IoCommandReadBlif ( Abc_Frame_t * pAbc, int argc, char **argv ); static int IoCommandReadBlifMv ( Abc_Frame_t * pAbc, int argc, char **argv ); static int IoCommandReadBench ( Abc_Frame_t * pAbc, int argc, char **argv ); +static int IoCommandReadCex ( Abc_Frame_t * pAbc, int argc, char **argv ); static int IoCommandReadDsd ( Abc_Frame_t * pAbc, int argc, char **argv ); static int IoCommandReadEdif ( Abc_Frame_t * pAbc, int argc, char **argv ); static int IoCommandReadEqn ( Abc_Frame_t * pAbc, int argc, char **argv ); @@ -113,6 +114,7 @@ void Io_Init( Abc_Frame_t * pAbc ) Cmd_CommandAdd( pAbc, "I/O", "read_blif", IoCommandReadBlif, 1 ); Cmd_CommandAdd( pAbc, "I/O", "read_blif_mv", IoCommandReadBlifMv, 1 ); Cmd_CommandAdd( pAbc, "I/O", "read_bench", IoCommandReadBench, 1 ); + Cmd_CommandAdd( pAbc, "I/O", "read_cex", IoCommandReadCex, 1 ); Cmd_CommandAdd( pAbc, "I/O", "read_dsd", IoCommandReadDsd, 1 ); Cmd_CommandAdd( pAbc, "I/O", "read_formula", IoCommandReadDsd, 1 ); // Cmd_CommandAdd( pAbc, "I/O", "read_edif", IoCommandReadEdif, 1 ); @@ -668,6 +670,271 @@ int IoCommandReadBench( Abc_Frame_t * pAbc, int argc, char ** argv ) return 1; } +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Abc_NtkReadCexFile( char * pFileName, Abc_Ntk_t * pNtk, Abc_Cex_t ** ppCex, int * pnFrames, int * fOldFormat ) +{ + FILE * pFile; + Abc_Cex_t * pCex; + Vec_Int_t * vNums; + int c, nRegs = -1, nFrames = -1, Status = 0; + pFile = fopen( pFileName, "r" ); + if ( pFile == NULL ) + { + printf( "Cannot open log file for reading \"%s\".\n" , pFileName ); + return -1; + } + + vNums = Vec_IntAlloc( 100 ); + int usedX = 0; + *fOldFormat = 0; + + char Buffer[1000]; + int state = 0; + int iPo = 0; + nFrames = -1; + int status = 0; + while ( fgets( Buffer, 1000, pFile ) != NULL ) + { + if ( Buffer[0] == '#' ) + continue; + Buffer[strlen(Buffer) - 1] = '\0'; + if (state==0 && strlen(Buffer)>1) { + // old format detected + *fOldFormat = 1; + state = 2; + iPo = 0; + status = 1; + } + if (state==1 && Buffer[0]!='b' && Buffer[0]!='c') { + // old format detected, first line was actually register + *fOldFormat = 1; + state = 3; + Vec_IntPush( vNums, status ); + status = 1; + } + if (Buffer[0] == '.' ) + break; + switch(state) { + case 0 : + { + char c = Buffer[0]; + if ( c == '0' || c == '1' || c == '2' ) { + status = c - '0' ; + state = 1; + } else if ( c == 'x' ) { + // old format with one x state latch + usedX = 1; + // set to 2 so we can Abc_LatchSetInitNone + // acts like 0 when setting bits + Vec_IntPush( vNums, 2 ); + nRegs = Vec_IntSize(vNums); + state = 3; + } else { + printf( "ERROR: Bad aiger status line.\n" ); + return -1; + } + } + break; + case 1 : + iPo = atoi(Buffer+1); + state = 2; + break; + case 2 : + for (int i=0; i= nPoNtk ) + { + printf( "ERROR: PO that failed verification not coresponding to Ntk.\n" ); + Vec_IntFree( vNums ); + return -1; + } + Abc_NtkForEachLatch( pNtk, pObj, i ) { + if ( Vec_IntEntry(vNums, i) ==0) + Abc_LatchSetInit0(pObj); + else if ( Vec_IntEntry(vNums, i) ==2) + Abc_LatchSetInitNone(pObj); + else + Abc_LatchSetInit1(pObj); + } + + pCex = Abc_CexAlloc( nRegs, nPi, iFrameCex + 1 ); + // the zero-based number of PO, for which verification failed + // fails in Bmc_CexVerify if not less than actual number of PO + pCex->iPo = iPo; + // the zero-based number of the time-frame, for which verificaiton failed + pCex->iFrame = iFrameCex; + assert( Vec_IntSize(vNums) == pCex->nBits ); + for ( c = 0; c < pCex->nBits; c++ ) + if ( Vec_IntEntry(vNums, c) == 1) + Abc_InfoSetBit( pCex->pData, c ); + Vec_IntFree( vNums ); + if ( ppCex ) + *ppCex = pCex; + else + ABC_FREE( pCex ); + + if ( pnFrames ) + *pnFrames = nFrames; + return Status; +} + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ + + +int IoCommandReadCex( Abc_Frame_t * pAbc, int argc, char ** argv ) +{ + Abc_Ntk_t * pNtk; + char * pFileName; + FILE * pFile; + int fCheck; + int c; + int fOldFormat = 0; + + fCheck = 1; + Extra_UtilGetoptReset(); + while ( ( c = Extra_UtilGetopt( argc, argv, "ch" ) ) != EOF ) + { + switch ( c ) + { + case 'c': + fCheck ^= 1; + break; + case 'h': + goto usage; + default: + goto usage; + } + } + if ( argc != globalUtilOptind + 1 ) + goto usage; + + // get the input file name + pFileName = argv[globalUtilOptind]; + if ( (pFile = fopen( pFileName, "r" )) == NULL ) + { + fprintf( pAbc->Err, "Cannot open input file \"%s\". \n", pFileName ); + return 1; + } + fclose( pFile ); + + pNtk = pAbc->pNtkCur; + if ( pNtk == NULL ) + { + fprintf( pAbc->Out, "Empty network.\n" ); + return 0; + } + Abc_FrameClearVerifStatus( pAbc ); + pAbc->Status = Abc_NtkReadCexFile( pFileName, pNtk, &pAbc->pCex, &pAbc->nFrames, &fOldFormat ); + + if ( fCheck ) { + extern Aig_Man_t * Abc_NtkToDar( Abc_Ntk_t * pNtk, int fExors, int fRegisters ); + Aig_Man_t * pAig = Abc_NtkToDar( pNtk, 0, 1 ); + Bmc_CexCareVerify( pAig, pAbc->pCex, pAbc->pCex, false ); + Aig_ManStop( pAig ); + } else if (fOldFormat) { + fprintf( pAbc->Err, "Using old aiger format with no checks enabled.\n" ); + return -1; + } + return 0; + +usage: + fprintf( pAbc->Err, "usage: read_cex [-ch] \n" ); + fprintf( pAbc->Err, "\t reads the witness cex\n" ); + fprintf( pAbc->Err, "\t-c : toggle check after reading [default = %s]\n", fCheck? "yes":"no" ); + fprintf( pAbc->Err, "\t-h : prints the command summary\n" ); + fprintf( pAbc->Err, "\tfile : the name of a file to read\n" ); + return 1; +} /**Function************************************************************* Synopsis [] @@ -2447,7 +2714,7 @@ extern Abc_Cex_t * Bmc_CexCareBits( Gia_Man_t * p, Abc_Cex_t * pCexState, Abc_Ce void Abc_NtkDumpOneCex( FILE * pFile, Abc_Ntk_t * pNtk, Abc_Cex_t * pCex, int fPrintFull, int fNames, int fUseFfNames, int fMinimize, int fUseOldMin, int fCexInfo, - int fCheckCex, int fUseSatBased, int fHighEffort, int fAiger, int fVerbose ) + int fCheckCex, int fUseSatBased, int fHighEffort, int fAiger, int fVerbose, int fExtended ) { Abc_Cex_t * pCare = NULL; Abc_Obj_t * pObj; @@ -2558,13 +2825,17 @@ void Abc_NtkDumpOneCex( FILE * pFile, Abc_Ntk_t * pNtk, Abc_Cex_t * pCex, } else { + if (fExtended && fAiger && !fNames) { + fprintf( pFile, "1\n"); + fprintf( pFile, "b%d\n", pCex->iPo); + } // output flop values (unaffected by the minimization) Abc_NtkForEachLatch( pNtk, pObj, i ) if ( fNames ) fprintf( pFile, "%s@0=%c\n", Abc_ObjName(Abc_ObjFanout0(pObj)), '0'+!Abc_LatchIsInit0(pObj) ); else fprintf( pFile, "%c", '0'+!Abc_LatchIsInit0(pObj) ); - if ( !fNames ) + if ( !fNames && fAiger) fprintf( pFile, "\n"); // output PI values (while skipping the minimized ones) for ( f = 0; f <= pCex->iFrame; f++ ) { @@ -2576,9 +2847,11 @@ void Abc_NtkDumpOneCex( FILE * pFile, Abc_Ntk_t * pNtk, Abc_Cex_t * pCex, fprintf( pFile, "%c", '0'+Abc_InfoHasBit(pCex->pData, pCex->nRegs+pCex->nPis*f + i) ); else if ( !fNames ) fprintf( pFile, "x"); - if ( !fNames ) + if ( !fNames && fAiger) fprintf( pFile, "\n"); } + if (fExtended && fAiger && !fNames) + fprintf( pFile, ".\n"); } Abc_CexFreeP( &pCare ); } @@ -2611,9 +2884,10 @@ int IoCommandWriteCex( Abc_Frame_t * pAbc, int argc, char **argv ) int fUseFfNames = 0; int fVerbose = 0; int fCexInfo = 0; + int fExtended = 0; Extra_UtilGetoptReset(); - while ( ( c = Extra_UtilGetopt( argc, argv, "snmueocafzvhx" ) ) != EOF ) + while ( ( c = Extra_UtilGetopt( argc, argv, "snmueocafzvhxt" ) ) != EOF ) { switch ( c ) { @@ -2653,6 +2927,9 @@ int IoCommandWriteCex( Abc_Frame_t * pAbc, int argc, char **argv ) case 'x': fCexInfo ^= 1; break; + case 't': + fExtended ^= 1; + break; case 'h': goto usage; default: @@ -2702,7 +2979,7 @@ int IoCommandWriteCex( Abc_Frame_t * pAbc, int argc, char **argv ) { Abc_NtkDumpOneCex( pFile, pNtk, pCex, fPrintFull, fNames, fUseFfNames, fMinimize, fUseOldMin, fCexInfo, - fCheckCex, fUseSatBased, fHighEffort, fAiger, fVerbose ); + fCheckCex, fUseSatBased, fHighEffort, fAiger, fVerbose, fExtended ); } else if ( pAbc->vCexVec ) { @@ -2710,10 +2987,10 @@ int IoCommandWriteCex( Abc_Frame_t * pAbc, int argc, char **argv ) { if ( pCex == NULL ) continue; - fprintf( pFile, "#\n#\n# CEX for output %d\n#\n", i ); + fprintf( pFile, "#\n#\n# CEX for output %d\n#\n", i ); Abc_NtkDumpOneCex( pFile, pNtk, pCex, fPrintFull, fNames, fUseFfNames, fMinimize, fUseOldMin, fCexInfo, - fCheckCex, fUseSatBased, fHighEffort, fAiger, fVerbose ); + fCheckCex, fUseSatBased, fHighEffort, fAiger, fVerbose, fExtended ); } } fprintf( pFile, "# DONE\n" ); @@ -2760,6 +3037,7 @@ int IoCommandWriteCex( Abc_Frame_t * pAbc, int argc, char **argv ) fprintf( pAbc->Err, "\t-x : minimize using algorithm from cexinfo command [default = %s]\n", fCexInfo? "yes": "no" ); fprintf( pAbc->Err, "\t-c : check generated CEX using ternary simulation [default = %s]\n", fCheckCex? "yes": "no" ); fprintf( pAbc->Err, "\t-a : print cex in AIGER 1.9 format [default = %s]\n", fAiger? "yes": "no" ); + fprintf( pAbc->Err, "\t-t : extended header info when cex in AIGER 1.9 format [default = %s]\n", fAiger? "yes": "no" ); fprintf( pAbc->Err, "\t-f : enable printing flop values in each timeframe [default = %s]\n", fPrintFull? "yes": "no" ); fprintf( pAbc->Err, "\t-z : toggle using saved flop names [default = %s]\n", fUseFfNames? "yes": "no" ); fprintf( pAbc->Err, "\t-v : enable verbose output [default = %s]\n", fVerbose? "yes": "no" ); From d7ecb23eeee9c9b4924182ce570c2e33eb18abff Mon Sep 17 00:00:00 2001 From: Miodrag Milanovic Date: Fri, 4 Mar 2022 11:25:56 +0100 Subject: [PATCH 20/79] gcc 4.8 fix --- src/base/io/io.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/base/io/io.c b/src/base/io/io.c index a83e997838..8d07702da4 100644 --- a/src/base/io/io.c +++ b/src/base/io/io.c @@ -703,6 +703,7 @@ int Abc_NtkReadCexFile( char * pFileName, Abc_Ntk_t * pNtk, Abc_Cex_t ** ppCex, int iPo = 0; nFrames = -1; int status = 0; + int i; while ( fgets( Buffer, 1000, pFile ) != NULL ) { if ( Buffer[0] == '#' ) @@ -750,7 +751,7 @@ int Abc_NtkReadCexFile( char * pFileName, Abc_Ntk_t * pNtk, Abc_Cex_t ** ppCex, state = 2; break; case 2 : - for (int i=0; i Date: Tue, 22 Mar 2022 18:45:10 +0100 Subject: [PATCH 21/79] fix buffer error --- src/base/io/io.c | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/base/io/io.c b/src/base/io/io.c index 8d07702da4..3874a6cffb 100644 --- a/src/base/io/io.c +++ b/src/base/io/io.c @@ -698,13 +698,16 @@ int Abc_NtkReadCexFile( char * pFileName, Abc_Ntk_t * pNtk, Abc_Cex_t ** ppCex, int usedX = 0; *fOldFormat = 0; - char Buffer[1000]; + int MaxLine = 1000000; + char *Buffer; int state = 0; int iPo = 0; nFrames = -1; int status = 0; int i; - while ( fgets( Buffer, 1000, pFile ) != NULL ) + + Buffer = ABC_ALLOC( char, MaxLine ); + while ( fgets( Buffer, MaxLine, pFile ) != NULL ) { if ( Buffer[0] == '#' ) continue; From b29e8a777bd2b16e891da323485e7473d9843039 Mon Sep 17 00:00:00 2001 From: Miodrag Milanovic Date: Wed, 13 Apr 2022 18:54:55 +0200 Subject: [PATCH 22/79] Make read_cex able to append if some latches are missing --- src/base/io/io.c | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/src/base/io/io.c b/src/base/io/io.c index 3874a6cffb..03443e9588 100644 --- a/src/base/io/io.c +++ b/src/base/io/io.c @@ -705,6 +705,9 @@ int Abc_NtkReadCexFile( char * pFileName, Abc_Ntk_t * pNtk, Abc_Cex_t ** ppCex, nFrames = -1; int status = 0; int i; + int nRegsNtk = 0; + Abc_Obj_t * pObj; + Abc_NtkForEachLatch( pNtk, pObj, i ) nRegsNtk++; Buffer = ABC_ALLOC( char, MaxLine ); while ( fgets( Buffer, MaxLine, pFile ) != NULL ) @@ -766,6 +769,14 @@ int Abc_NtkReadCexFile( char * pFileName, Abc_Ntk_t * pNtk, Abc_Cex_t ** ppCex, } } nRegs = Vec_IntSize(vNums); + if ( nRegs < nRegsNtk ) + { + printf( "WARNING: Register number is smaller then in Ntk. Appending.\n" ); + for (i=0; i Date: Fri, 15 Apr 2022 11:42:56 +0200 Subject: [PATCH 23/79] Fix for unhandled aiw file commands --- src/base/io/io.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/base/io/io.c b/src/base/io/io.c index 03443e9588..2774266499 100644 --- a/src/base/io/io.c +++ b/src/base/io/io.c @@ -712,7 +712,7 @@ int Abc_NtkReadCexFile( char * pFileName, Abc_Ntk_t * pNtk, Abc_Cex_t ** ppCex, Buffer = ABC_ALLOC( char, MaxLine ); while ( fgets( Buffer, MaxLine, pFile ) != NULL ) { - if ( Buffer[0] == '#' ) + if ( Buffer[0] == '#' || Buffer[0] == 'c' || Buffer[0] == 'f' || Buffer[0] == 'u' ) continue; Buffer[strlen(Buffer) - 1] = '\0'; if (state==0 && strlen(Buffer)>1) { @@ -722,7 +722,7 @@ int Abc_NtkReadCexFile( char * pFileName, Abc_Ntk_t * pNtk, Abc_Cex_t ** ppCex, iPo = 0; status = 1; } - if (state==1 && Buffer[0]!='b' && Buffer[0]!='c') { + if (state==1 && Buffer[0]!='b' && Buffer[0]!='j') { // old format detected, first line was actually register *fOldFormat = 1; state = 3; From c84323b5a5c6df273404b5d5ace70ae61a06bdb0 Mon Sep 17 00:00:00 2001 From: Yuri Victorovich Date: Wed, 29 Dec 2021 12:57:23 -0800 Subject: [PATCH 24/79] Add missing class names in FreeBSD-ifdefed code. --- src/sat/glucose/System.cpp | 2 +- src/sat/glucose2/System2.cpp | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/sat/glucose/System.cpp b/src/sat/glucose/System.cpp index 18f2d65648..276eddb88d 100644 --- a/src/sat/glucose/System.cpp +++ b/src/sat/glucose/System.cpp @@ -86,7 +86,7 @@ double Gluco::memUsed(void) { struct rusage ru; getrusage(RUSAGE_SELF, &ru); return (double)ru.ru_maxrss / 1024; } -double memUsedPeak(void) { return memUsed(); } +double Gluco::memUsedPeak(void) { return memUsed(); } ABC_NAMESPACE_IMPL_END diff --git a/src/sat/glucose2/System2.cpp b/src/sat/glucose2/System2.cpp index 844220a0b8..bf16dcd179 100644 --- a/src/sat/glucose2/System2.cpp +++ b/src/sat/glucose2/System2.cpp @@ -86,7 +86,7 @@ double Gluco2::memUsed(void) { struct rusage ru; getrusage(RUSAGE_SELF, &ru); return (double)ru.ru_maxrss / 1024; } -double memUsedPeak(void) { return memUsed(); } +double Gluco2::memUsedPeak(void) { return memUsed(); } ABC_NAMESPACE_IMPL_END From 6234e18df79ebd2484eb7d088dffaad61cbfe8ef Mon Sep 17 00:00:00 2001 From: Miodrag Milanovic Date: Fri, 6 May 2022 15:41:34 +0200 Subject: [PATCH 25/79] Give more reasonable error on read_cex and handle status --- src/base/io/io.c | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/src/base/io/io.c b/src/base/io/io.c index 2774266499..7ad057dfb7 100644 --- a/src/base/io/io.c +++ b/src/base/io/io.c @@ -686,7 +686,7 @@ int Abc_NtkReadCexFile( char * pFileName, Abc_Ntk_t * pNtk, Abc_Cex_t ** ppCex, FILE * pFile; Abc_Cex_t * pCex; Vec_Int_t * vNums; - int c, nRegs = -1, nFrames = -1, Status = 0; + int c, nRegs = -1, nFrames = -1; pFile = fopen( pFileName, "r" ); if ( pFile == NULL ) { @@ -804,7 +804,10 @@ int Abc_NtkReadCexFile( char * pFileName, Abc_Ntk_t * pNtk, Abc_Cex_t ** ppCex, Abc_NtkForEachPo(pNtk, pObj, i ) nPoNtk++; if ( nRegs < 0 ) { - printf( "ERROR: Cannot read register number.\n" ); + if (status == 1) + printf( "ERROR: Cannot read register number.\n" ); + else + printf( "Counter-example is not available.\n" ); Vec_IntFree( vNums ); return -1; } @@ -866,7 +869,7 @@ int Abc_NtkReadCexFile( char * pFileName, Abc_Ntk_t * pNtk, Abc_Cex_t ** ppCex, if ( pnFrames ) *pnFrames = nFrames; - return Status; + return status; } /**Function************************************************************* @@ -927,7 +930,7 @@ int IoCommandReadCex( Abc_Frame_t * pAbc, int argc, char ** argv ) Abc_FrameClearVerifStatus( pAbc ); pAbc->Status = Abc_NtkReadCexFile( pFileName, pNtk, &pAbc->pCex, &pAbc->nFrames, &fOldFormat ); - if ( fCheck ) { + if ( fCheck && pAbc->Status==1) { extern Aig_Man_t * Abc_NtkToDar( Abc_Ntk_t * pNtk, int fExors, int fRegisters ); Aig_Man_t * pAig = Abc_NtkToDar( pNtk, 0, 1 ); Bmc_CexCareVerify( pAig, pAbc->pCex, pAbc->pCex, false ); From 09a7e6dac739133a927ae7064d319068ab927f90 Mon Sep 17 00:00:00 2001 From: Miodrag Milanovic Date: Fri, 6 May 2022 15:49:42 +0200 Subject: [PATCH 26/79] distinquish between old and new format as well --- src/base/io/io.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/base/io/io.c b/src/base/io/io.c index 7ad057dfb7..a55a30642c 100644 --- a/src/base/io/io.c +++ b/src/base/io/io.c @@ -804,10 +804,10 @@ int Abc_NtkReadCexFile( char * pFileName, Abc_Ntk_t * pNtk, Abc_Cex_t ** ppCex, Abc_NtkForEachPo(pNtk, pObj, i ) nPoNtk++; if ( nRegs < 0 ) { - if (status == 1) - printf( "ERROR: Cannot read register number.\n" ); - else + if (status == 0 || *fOldFormat == 0) printf( "Counter-example is not available.\n" ); + else + printf( "ERROR: Cannot read register number.\n" ); Vec_IntFree( vNums ); return -1; } From 69ffaa09129e3cdec2200f505d4d60c9e271b8d9 Mon Sep 17 00:00:00 2001 From: Jannis Harder Date: Fri, 1 Jul 2022 16:00:05 +0200 Subject: [PATCH 27/79] read_cex: Allow reading cex that has extra registers --- src/base/io/io.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/base/io/io.c b/src/base/io/io.c index a55a30642c..5408ccbaeb 100644 --- a/src/base/io/io.c +++ b/src/base/io/io.c @@ -777,6 +777,12 @@ int Abc_NtkReadCexFile( char * pFileName, Abc_Ntk_t * pNtk, Abc_Cex_t ** ppCex, } nRegs = Vec_IntSize(vNums); } + else if ( nRegs > nRegsNtk ) + { + printf( "WARNING: Register number is larger then in Ntk. Truncating.\n" ); + Vec_IntShrink( vNums, nRegsNtk ); + nRegs = nRegsNtk; + } state = 3; break; default: From f159bef6c3d4e170929a216ded5496154c46e3c2 Mon Sep 17 00:00:00 2001 From: Miodrag Milanovic Date: Mon, 4 Jul 2022 16:11:23 +0200 Subject: [PATCH 28/79] Prevent types from stdint to be defined under abc namespace --- src/misc/util/abc_global.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/misc/util/abc_global.h b/src/misc/util/abc_global.h index d1a9b4d311..2217bb87e4 100644 --- a/src/misc/util/abc_global.h +++ b/src/misc/util/abc_global.h @@ -94,8 +94,6 @@ /// PARAMETERS /// //////////////////////////////////////////////////////////////////////// -ABC_NAMESPACE_HEADER_START - //////////////////////////////////////////////////////////////////////// /// BASIC TYPES /// //////////////////////////////////////////////////////////////////////// @@ -143,6 +141,8 @@ ABC_NAMESPACE_HEADER_START #endif +ABC_NAMESPACE_HEADER_START + /** * Pointer difference type; replacement for ptrdiff_t. * This is a signed integral type that is the same size as a pointer. From 5f40c4704f6f2ab027b12c3f61765d8a2090c92a Mon Sep 17 00:00:00 2001 From: Catherine Date: Thu, 7 Jul 2022 08:28:00 +0000 Subject: [PATCH 29/79] Add support for WASI platform in Wln_ConvertToRtl. --- src/base/wln/wlnRtl.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/base/wln/wlnRtl.c b/src/base/wln/wlnRtl.c index fa0f0cd5c0..8a8e0a324c 100644 --- a/src/base/wln/wlnRtl.c +++ b/src/base/wln/wlnRtl.c @@ -121,6 +121,9 @@ char * Wln_GetYosysName() } int Wln_ConvertToRtl( char * pCommand, char * pFileTemp ) { +#if defined(__wasm) + return 0; +#else FILE * pFile; if ( system( pCommand ) == -1 ) { @@ -134,6 +137,7 @@ int Wln_ConvertToRtl( char * pCommand, char * pFileTemp ) } fclose( pFile ); return 1; +#endif } Rtl_Lib_t * Wln_ReadSystemVerilog( char * pFileName, char * pTopModule, int fCollapse, int fVerbose ) { From c95d9499d9401b572a21f7c631a9ee7c7d4800c5 Mon Sep 17 00:00:00 2001 From: Miodrag Milanovic Date: Fri, 15 Jul 2022 12:46:01 +0200 Subject: [PATCH 30/79] Revert "Remove ABC_NO_RLIMIT macro, use defined(__wasm) instead." This reverts commit fd2c9b1c19216f6b756f88b18f5ca67b759ca128. --- src/base/main/mainReal.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/base/main/mainReal.c b/src/base/main/mainReal.c index a13be5e525..420f2cf101 100644 --- a/src/base/main/mainReal.c +++ b/src/base/main/mainReal.c @@ -134,7 +134,7 @@ int Abc_RealMain( int argc, char * argv[] ) break; case 'm': { -#if !defined(WIN32) && !defined(__wasm) +#if !defined(WIN32) && !defined(ABC_NO_RLIMIT) int maxMb = atoi(globalUtilOptarg); printf("Limiting memory use to %d MB\n", maxMb); struct rlimit limit = { @@ -146,7 +146,7 @@ int Abc_RealMain( int argc, char * argv[] ) break; } case 'l': { -#if !defined(WIN32) && !defined(__wasm) +#if !defined(WIN32) && !defined(ABC_NO_RLIMIT) rlim_t maxTime = atoi(globalUtilOptarg); printf("Limiting time to %d seconds\n", (int)maxTime); struct rlimit limit = { From b6c0b36c8aa0a181a541a8cc4320ebde938a0fe0 Mon Sep 17 00:00:00 2001 From: Josuah Demangeon Date: Fri, 8 Jul 2022 14:35:40 +0200 Subject: [PATCH 31/79] do not include -lrt or -ldl on platform that do not support them Some platforms were already listed, this includes OpenBSD to the list and makes it easier to add more. --- Makefile | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Makefile b/Makefile index 27ede6ac37..032f3a8d6e 100644 --- a/Makefile +++ b/Makefile @@ -137,11 +137,11 @@ endif # LIBS := -ldl -lrt LIBS += -lm -ifneq ($(OS), FreeBSD) +ifneq ($(OS), $(filter $(OS), FreeBSD OpenBSD)) LIBS += -ldl endif -ifneq ($(findstring Darwin, $(shell uname)), Darwin) +ifneq ($(OS), $(filter $(OS), FreeBSD OpenBSD Darwin)) LIBS += -lrt endif From 4e89fc7ccb32086a55c2fbc567755c96e319d2f8 Mon Sep 17 00:00:00 2001 From: Miodrag Milanovic Date: Mon, 25 Jul 2022 11:54:23 +0200 Subject: [PATCH 32/79] Export version --- .gitattributes | 1 + .gitcommit | 1 + 2 files changed, 2 insertions(+) create mode 100644 .gitcommit diff --git a/.gitattributes b/.gitattributes index 400b529f0b..93b5c8649a 100644 --- a/.gitattributes +++ b/.gitattributes @@ -1,3 +1,4 @@ +/.gitcommit export-subst * text=auto *.c text diff --git a/.gitcommit b/.gitcommit new file mode 100644 index 0000000000..46b7856fbc --- /dev/null +++ b/.gitcommit @@ -0,0 +1 @@ +$Format:%h$ From 8c923ad4929870d4e819b84f62c7f9177b0d0d17 Mon Sep 17 00:00:00 2001 From: Jannis Harder Date: Fri, 5 Aug 2022 13:21:59 +0200 Subject: [PATCH 33/79] Add '-p' option to 'constr' to allow fully removing constraints Invoking 'constr -r' converts constraints into POs but does not fully remove them. Now 'constr -pr' can be used to completely remove them, leaving the set of non-constraint POs unchanged. --- src/base/abci/abc.c | 31 +++++++++++++++++++++++++++---- 1 file changed, 27 insertions(+), 4 deletions(-) diff --git a/src/base/abci/abc.c b/src/base/abci/abc.c index 33a8315ca2..bb64a3f30c 100644 --- a/src/base/abci/abc.c +++ b/src/base/abci/abc.c @@ -27974,17 +27974,20 @@ int Abc_CommandInduction( Abc_Frame_t * pAbc, int argc, char ** argv ) int Abc_CommandConstr( Abc_Frame_t * pAbc, int argc, char ** argv ) { Abc_Ntk_t * pNtk; + Abc_Ntk_t * pNtkRes; int c; int nFrames; int nConfs; int nProps; int fRemove; + int fPurge; int fStruct; int fInvert; int fOldAlgo; int fVerbose; int nConstrs; extern void Abc_NtkDarConstr( Abc_Ntk_t * pNtk, int nFrames, int nConfs, int nProps, int fStruct, int fOldAlgo, int fVerbose ); + extern Abc_Ntk_t * Abc_NtkMakeOnePo( Abc_Ntk_t * pNtk, int Output, int nRange ); pNtk = Abc_FrameReadNtk(pAbc); // set defaults @@ -27992,13 +27995,14 @@ int Abc_CommandConstr( Abc_Frame_t * pAbc, int argc, char ** argv ) nConfs = 1000; nProps = 1000; fRemove = 0; + fPurge = 0; fStruct = 0; fInvert = 0; fOldAlgo = 0; fVerbose = 0; nConstrs = -1; Extra_UtilGetoptReset(); - while ( ( c = Extra_UtilGetopt( argc, argv, "FCPNrsiavh" ) ) != EOF ) + while ( ( c = Extra_UtilGetopt( argc, argv, "FCPNrpsiavh" ) ) != EOF ) { switch ( c ) { @@ -28049,6 +28053,9 @@ int Abc_CommandConstr( Abc_Frame_t * pAbc, int argc, char ** argv ) case 'r': fRemove ^= 1; break; + case 'p': + fPurge ^= 1; + break; case 's': fStruct ^= 1; break; @@ -28084,7 +28091,22 @@ int Abc_CommandConstr( Abc_Frame_t * pAbc, int argc, char ** argv ) Abc_Print( -1, "Constraints are not defined.\n" ); return 0; } - Abc_Print( 1, "Constraints are converted to be primary outputs.\n" ); + + if ( fPurge ) + { + Abc_Print( 1, "Constraints are removed.\n" ); + pNtkRes = Abc_NtkMakeOnePo( pNtk, 0, Abc_NtkPoNum(pNtk) - Abc_NtkConstrNum(pNtk) ); + if ( pNtkRes == NULL ) + { + Abc_Print( 1,"Transformation has failed.\n" ); + return 1; + } + // replace the current network + Abc_FrameReplaceCurrentNetwork( pAbc, pNtkRes ); + pNtk = Abc_FrameReadNtk(pAbc); + } + else + Abc_Print( 1, "Constraints are converted to be primary outputs.\n" ); pNtk->nConstrs = 0; return 0; } @@ -28129,7 +28151,7 @@ int Abc_CommandConstr( Abc_Frame_t * pAbc, int argc, char ** argv ) Abc_NtkDarConstr( pNtk, nFrames, nConfs, nProps, fStruct, fOldAlgo, fVerbose ); return 0; usage: - Abc_Print( -2, "usage: constr [-FCPN num] [-risavh]\n" ); + Abc_Print( -2, "usage: constr [-FCPN num] [-rpisavh]\n" ); Abc_Print( -2, "\t a toolkit for constraint manipulation\n" ); Abc_Print( -2, "\t if constraints are absent, detect them functionally\n" ); Abc_Print( -2, "\t if constraints are present, profiles them using random simulation\n" ); @@ -28138,7 +28160,8 @@ int Abc_CommandConstr( Abc_Frame_t * pAbc, int argc, char ** argv ) Abc_Print( -2, "\t-C num : the max number of conflicts in SAT solving [default = %d]\n", nConfs ); Abc_Print( -2, "\t-P num : the max number of propagations in SAT solving [default = %d]\n", nProps ); Abc_Print( -2, "\t-N num : manually set the last POs to be constraints [default = %d]\n", nConstrs ); - Abc_Print( -2, "\t-r : manually remove the constraints [default = %s]\n", fRemove? "yes": "no" ); + Abc_Print( -2, "\t-r : manually remove the constraints, converting them to POs [default = %s]\n", fRemove? "yes": "no" ); + Abc_Print( -2, "\t-p : remove constraints instead of converting them to POs [default = %s]\n", fPurge? "yes": "no" ); Abc_Print( -2, "\t-i : toggle inverting already defined constraints [default = %s]\n", fInvert? "yes": "no" ); Abc_Print( -2, "\t-s : toggle using structural detection methods [default = %s]\n", fStruct? "yes": "no" ); Abc_Print( -2, "\t-a : toggle fast implication detection [default = %s]\n", !fOldAlgo? "yes": "no" ); From feedbc744955df2b142d6a0b9dc62814567f5fc2 Mon Sep 17 00:00:00 2001 From: Jannis Harder Date: Fri, 5 Aug 2022 14:28:13 +0200 Subject: [PATCH 34/79] read_cex: Faster parsing and care bits for verification --- src/base/io/io.c | 106 ++++++++++++++++++++++++++------------ src/sat/bmc/bmc.h | 4 +- src/sat/bmc/bmcCexCare.c | 38 +++++++++++++- src/sat/bmc/bmcCexTools.c | 47 +++++++++++++++++ 4 files changed, 159 insertions(+), 36 deletions(-) diff --git a/src/base/io/io.c b/src/base/io/io.c index b8d1d7db86..a8a5b61871 100644 --- a/src/base/io/io.c +++ b/src/base/io/io.c @@ -683,10 +683,11 @@ int IoCommandReadBench( Abc_Frame_t * pAbc, int argc, char ** argv ) SeeAlso [] ***********************************************************************/ -int Abc_NtkReadCexFile( char * pFileName, Abc_Ntk_t * pNtk, Abc_Cex_t ** ppCex, int * pnFrames, int * fOldFormat ) +int Abc_NtkReadCexFile( char * pFileName, Abc_Ntk_t * pNtk, Abc_Cex_t ** ppCex, Abc_Cex_t ** ppCexCare, int * pnFrames, int * fOldFormat, int xMode ) { FILE * pFile; Abc_Cex_t * pCex; + Abc_Cex_t * pCexCare; Vec_Int_t * vNums; int c, nRegs = -1, nFrames = -1; pFile = fopen( pFileName, "r" ); @@ -702,6 +703,7 @@ int Abc_NtkReadCexFile( char * pFileName, Abc_Ntk_t * pNtk, Abc_Cex_t ** ppCex, int MaxLine = 1000000; char *Buffer; + int BufferLen = 0; int state = 0; int iPo = 0; nFrames = -1; @@ -716,8 +718,9 @@ int Abc_NtkReadCexFile( char * pFileName, Abc_Ntk_t * pNtk, Abc_Cex_t ** ppCex, { if ( Buffer[0] == '#' || Buffer[0] == 'c' || Buffer[0] == 'f' || Buffer[0] == 'u' ) continue; - Buffer[strlen(Buffer) - 1] = '\0'; - if (state==0 && strlen(Buffer)>1) { + BufferLen = strlen(Buffer) - 1; + Buffer[BufferLen] = '\0'; + if (state==0 && BufferLen>1) { // old format detected *fOldFormat = 1; state = 2; @@ -759,7 +762,7 @@ int Abc_NtkReadCexFile( char * pFileName, Abc_Ntk_t * pNtk, Abc_Cex_t ** ppCex, state = 2; break; case 2 : - for (i=0; i nRegsNtk ) { - printf( "WARNING: Register number is larger then in Ntk. Truncating.\n" ); + printf( "WARNING: Register number is larger than in Ntk. Truncating.\n" ); Vec_IntShrink( vNums, nRegsNtk ); nRegs = nRegsNtk; } state = 3; break; default: - for (i=0; i= nPoNtk ) + if ( iPo >= Abc_NtkPoNum(pNtk) ) { - printf( "ERROR: PO that failed verification not coresponding to Ntk.\n" ); - Vec_IntFree( vNums ); - return -1; + printf( "WARNING: PO that failed verification not coresponding to Ntk, using first PO instead.\n" ); + iPo = 0; } Abc_NtkForEachLatch( pNtk, pObj, i ) { - if ( Vec_IntEntry(vNums, i) ==0) - Abc_LatchSetInit0(pObj); - else if ( Vec_IntEntry(vNums, i) ==2) + if ( Vec_IntEntry(vNums, i) == 1 ) + Abc_LatchSetInit1(pObj); + else if ( Vec_IntEntry(vNums, i) == 2 && xMode ) Abc_LatchSetInitNone(pObj); else - Abc_LatchSetInit1(pObj); + Abc_LatchSetInit0(pObj); } pCex = Abc_CexAlloc( nRegs, nPi, iFrameCex + 1 ); + pCexCare = Abc_CexAlloc( nRegs, nPi, iFrameCex + 1); // the zero-based number of PO, for which verification failed // fails in Bmc_CexVerify if not less than actual number of PO pCex->iPo = iPo; + pCexCare->iPo = iPo; // the zero-based number of the time-frame, for which verificaiton failed pCex->iFrame = iFrameCex; + pCexCare->iFrame = iFrameCex; assert( Vec_IntSize(vNums) == pCex->nBits ); - for ( c = 0; c < pCex->nBits; c++ ) + for ( c = 0; c < pCex->nBits; c++ ) { if ( Vec_IntEntry(vNums, c) == 1) + { Abc_InfoSetBit( pCex->pData, c ); + Abc_InfoSetBit( pCexCare->pData, c ); + } + else if ( Vec_IntEntry(vNums, c) == 2 && xMode ) + { + // nothing to set + } + else + Abc_InfoSetBit( pCexCare->pData, c ); + } + Vec_IntFree( vNums ); + Abc_CexFreeP( ppCex ); if ( ppCex ) *ppCex = pCex; else - ABC_FREE( pCex ); + Abc_CexFree( pCex ); + Abc_CexFreeP( ppCexCare ); + if ( ppCexCare ) + *ppCexCare = pCexCare; + else + Abc_CexFree( pCexCare ); if ( pnFrames ) *pnFrames = nFrames; @@ -896,21 +914,27 @@ int Abc_NtkReadCexFile( char * pFileName, Abc_Ntk_t * pNtk, Abc_Cex_t ** ppCex, int IoCommandReadCex( Abc_Frame_t * pAbc, int argc, char ** argv ) { Abc_Ntk_t * pNtk; + Abc_Cex_t * pCex = NULL; + Abc_Cex_t * pCexCare = NULL; char * pFileName; FILE * pFile; - int fCheck; + int fCheck = 1; + int fXMode = 0; int c; int fOldFormat = 0; + int verified; - fCheck = 1; Extra_UtilGetoptReset(); - while ( ( c = Extra_UtilGetopt( argc, argv, "ch" ) ) != EOF ) + while ( ( c = Extra_UtilGetopt( argc, argv, "cxh" ) ) != EOF ) { switch ( c ) { case 'c': fCheck ^= 1; break; + case 'x': + fXMode ^= 1; + break; case 'h': goto usage; default: @@ -936,16 +960,31 @@ int IoCommandReadCex( Abc_Frame_t * pAbc, int argc, char ** argv ) return 0; } Abc_FrameClearVerifStatus( pAbc ); - pAbc->Status = Abc_NtkReadCexFile( pFileName, pNtk, &pAbc->pCex, &pAbc->nFrames, &fOldFormat ); + pAbc->Status = Abc_NtkReadCexFile( pFileName, pNtk, &pCex, &pCexCare, &pAbc->nFrames, &fOldFormat, fXMode); + if ( fOldFormat && !fCheck ) + printf( "WARNING: Old witness format detected and checking is disabled. Reading might have failed.\n" ); if ( fCheck && pAbc->Status==1) { extern Aig_Man_t * Abc_NtkToDar( Abc_Ntk_t * pNtk, int fExors, int fRegisters ); Aig_Man_t * pAig = Abc_NtkToDar( pNtk, 0, 1 ); - Bmc_CexCareVerify( pAig, pAbc->pCex, pAbc->pCex, false ); - Aig_ManStop( pAig ); - } else if (fOldFormat) { - fprintf( pAbc->Err, "Using old aiger format with no checks enabled.\n" ); - return -1; + + verified = Bmc_CexCareVerify( pAig, pCex, pCexCare, false ); + if (!verified) + { + printf( "Checking CEX for any PO.\n" ); + int verified = Bmc_CexCareVerifyAnyPo( pAig, pCex, pCexCare, false ); + Aig_ManStop( pAig ); + if (verified < 0) + { + Abc_CexFreeP(&pCex); + Abc_CexFreeP(&pCexCare); + return 1; + } + pAbc->pCex->iPo = verified; + } + + Abc_CexFreeP(&pCexCare); + Abc_FrameReplaceCex( pAbc, &pCex ); } return 0; @@ -953,6 +992,7 @@ int IoCommandReadCex( Abc_Frame_t * pAbc, int argc, char ** argv ) fprintf( pAbc->Err, "usage: read_cex [-ch] \n" ); fprintf( pAbc->Err, "\t reads the witness cex\n" ); fprintf( pAbc->Err, "\t-c : toggle check after reading [default = %s]\n", fCheck? "yes":"no" ); + fprintf( pAbc->Err, "\t-x : read x bits for verification [default = %s]\n", fXMode? "yes":"no" ); fprintf( pAbc->Err, "\t-h : prints the command summary\n" ); fprintf( pAbc->Err, "\tfile : the name of a file to read\n" ); return 1; diff --git a/src/sat/bmc/bmc.h b/src/sat/bmc/bmc.h index 12439faa17..2c843392c1 100644 --- a/src/sat/bmc/bmc.h +++ b/src/sat/bmc/bmc.h @@ -219,7 +219,8 @@ extern int Gia_ManBmcPerform( Gia_Man_t * p, Bmc_AndPar_t * pPars extern Abc_Cex_t * Bmc_CexCareExtendToObjects( Gia_Man_t * p, Abc_Cex_t * pCex, Abc_Cex_t * pCexCare ); extern Abc_Cex_t * Bmc_CexCareMinimize( Aig_Man_t * p, int nRealPis, Abc_Cex_t * pCex, int nTryCexes, int fCheck, int fVerbose ); extern Abc_Cex_t * Bmc_CexCareMinimizeAig( Gia_Man_t * p, int nRealPis, Abc_Cex_t * pCex, int nTryCexes, int fCheck, int fVerbose ); -extern void Bmc_CexCareVerify( Aig_Man_t * p, Abc_Cex_t * pCex, Abc_Cex_t * pCexMin, int fVerbose ); +extern int Bmc_CexCareVerify( Aig_Man_t * p, Abc_Cex_t * pCex, Abc_Cex_t * pCexMin, int fVerbose ); +extern int Bmc_CexCareVerifyAnyPo( Aig_Man_t * p, Abc_Cex_t * pCex, Abc_Cex_t * pCexMin, int fVerbose ); extern Abc_Cex_t * Bmc_CexCareSatBasedMinimize( Aig_Man_t * p, int nRealPis, Abc_Cex_t * pCex, int fHighEffort, int fCheck, int fVerbose ); extern Abc_Cex_t * Bmc_CexCareSatBasedMinimizeAig( Gia_Man_t * p, Abc_Cex_t * pCex, int fHighEffort, int fVerbose ); /*=== bmcCexCut.c ==========================================================*/ @@ -230,6 +231,7 @@ extern Abc_Cex_t * Saig_ManCexMinPerform( Aig_Man_t * pAig, Abc_Cex_t * pC /*=== bmcCexTool.c ==========================================================*/ extern void Bmc_CexPrint( Abc_Cex_t * pCex, int nRealPis, int fVerbose ); extern int Bmc_CexVerify( Gia_Man_t * p, Abc_Cex_t * pCex, Abc_Cex_t * pCexCare ); +extern int Bmc_CexVerifyAnyPo( Gia_Man_t * p, Abc_Cex_t * pCex, Abc_Cex_t * pCexCare ); /*=== bmcICheck.c ==========================================================*/ extern void Bmc_PerformICheck( Gia_Man_t * p, int nFramesMax, int nTimeOut, int fEmpty, int fVerbose ); extern Vec_Int_t * Bmc_PerformISearch( Gia_Man_t * p, int nFramesMax, int nTimeOut, int fReverse, int fBackTopo, int fDump, int fVerbose ); diff --git a/src/sat/bmc/bmcCexCare.c b/src/sat/bmc/bmcCexCare.c index c274b04cbf..d9a677c356 100644 --- a/src/sat/bmc/bmcCexCare.c +++ b/src/sat/bmc/bmcCexCare.c @@ -455,8 +455,9 @@ Abc_Cex_t * Bmc_CexCareSatBasedMinimize( Aig_Man_t * p, int nRealPis, Abc_Cex_t SeeAlso [] ***********************************************************************/ -void Bmc_CexCareVerify( Aig_Man_t * p, Abc_Cex_t * pCex, Abc_Cex_t * pCexMin, int fVerbose ) +int Bmc_CexCareVerify( Aig_Man_t * p, Abc_Cex_t * pCex, Abc_Cex_t * pCexMin, int fVerbose ) { + int result; Gia_Man_t * pGia = Gia_ManFromAigSimple( p ); if ( fVerbose ) { @@ -465,11 +466,13 @@ void Bmc_CexCareVerify( Aig_Man_t * p, Abc_Cex_t * pCex, Abc_Cex_t * pCexMin, in printf( "Minimized: " ); Bmc_CexPrint( pCexMin, Gia_ManPiNum(pGia), 0 ); } - if ( !Bmc_CexVerify( pGia, pCex, pCexMin ) ) + result = Bmc_CexVerify( pGia, pCex, pCexMin ); + if ( !result ) printf( "Counter-example verification has failed.\n" ); else printf( "Counter-example verification succeeded.\n" ); Gia_ManStop( pGia ); + return result; } /* { @@ -480,6 +483,37 @@ void Bmc_CexCareVerify( Aig_Man_t * p, Abc_Cex_t * pCex, Abc_Cex_t * pCexMin, in } */ +/**Function************************************************************* + + Synopsis [Verifies the care set of the counter-example.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Bmc_CexCareVerifyAnyPo( Aig_Man_t * p, Abc_Cex_t * pCex, Abc_Cex_t * pCexMin, int fVerbose ) +{ + int iPo; + Gia_Man_t * pGia = Gia_ManFromAigSimple( p ); + if ( fVerbose ) + { + printf( "Original : " ); + Bmc_CexPrint( pCex, Gia_ManPiNum(pGia), 0 ); + printf( "Minimized: " ); + Bmc_CexPrint( pCexMin, Gia_ManPiNum(pGia), 0 ); + } + iPo = Bmc_CexVerifyAnyPo( pGia, pCex, pCexMin ); + if ( iPo < 0 ) + printf( "Counter-example verification has failed.\n" ); + else + printf( "Counter-example verification succeeded.\n" ); + Gia_ManStop( pGia ); + return iPo; +} + //////////////////////////////////////////////////////////////////////// /// END OF FILE /// //////////////////////////////////////////////////////////////////////// diff --git a/src/sat/bmc/bmcCexTools.c b/src/sat/bmc/bmcCexTools.c index 6cc298577a..6bea6fc5f9 100644 --- a/src/sat/bmc/bmcCexTools.c +++ b/src/sat/bmc/bmcCexTools.c @@ -374,6 +374,53 @@ int Bmc_CexVerify( Gia_Man_t * p, Abc_Cex_t * pCex, Abc_Cex_t * pCexCare ) return Gia_ObjTerSimGet1(pObj); } +/**Function************************************************************* + + Synopsis [Verifies the care set of the counter-example for an arbitrary PO.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Bmc_CexVerifyAnyPo( Gia_Man_t * p, Abc_Cex_t * pCex, Abc_Cex_t * pCexCare ) +{ + Gia_Obj_t * pObj; + int i, k; +// assert( pCex->nRegs > 0 ); +// assert( pCexCare->nRegs == 0 ); + Gia_ObjTerSimSet0( Gia_ManConst0(p) ); + Gia_ManForEachRi( p, pObj, k ) + Gia_ObjTerSimSet0( pObj ); + for ( i = 0; i <= pCex->iFrame; i++ ) + { + Gia_ManForEachPi( p, pObj, k ) + { + if ( !Abc_InfoHasBit( pCexCare->pData, pCexCare->nRegs + i * pCexCare->nPis + k ) ) + Gia_ObjTerSimSetX( pObj ); + else if ( Abc_InfoHasBit( pCex->pData, pCex->nRegs + i * pCex->nPis + k ) ) + Gia_ObjTerSimSet1( pObj ); + else + Gia_ObjTerSimSet0( pObj ); + } + Gia_ManForEachRo( p, pObj, k ) + Gia_ObjTerSimRo( p, pObj ); + Gia_ManForEachAnd( p, pObj, k ) + Gia_ObjTerSimAnd( pObj ); + Gia_ManForEachCo( p, pObj, k ) + Gia_ObjTerSimCo( pObj ); + } + for (i = 0; i < Gia_ManPoNum(p) - Gia_ManConstrNum(p); i++) + { + pObj = Gia_ManPo( p, i ); + if (Gia_ObjTerSimGet1(pObj)) + return i; + } + return -1; +} + /**Function************************************************************* Synopsis [Computes internal states of the CEX.] From 20f970f569d014420413c475dc87265d1ab35f02 Mon Sep 17 00:00:00 2001 From: Jannis Harder Date: Fri, 5 Aug 2022 14:44:12 +0200 Subject: [PATCH 35/79] write_cex: Check for unsupported multi-PO SAT based minimization Running SAT-based CEX minimization with multiple POs runs into an assertion. This makes it produce an error message instead. --- src/base/io/io.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/base/io/io.c b/src/base/io/io.c index a8a5b61871..46f2641ff5 100644 --- a/src/base/io/io.c +++ b/src/base/io/io.c @@ -2820,7 +2820,12 @@ void Abc_NtkDumpOneCex( FILE * pFile, Abc_Ntk_t * pNtk, Abc_Cex_t * pCex, Bmc_CexCareVerify( pAig, pCex, pCare, fVerbose ); } else if ( fUseSatBased ) - pCare = Bmc_CexCareSatBasedMinimize( pAig, Saig_ManPiNum(pAig), pCex, fHighEffort, fCheckCex, fVerbose ); + { + if ( Abc_NtkPoNum( pNtk ) == 1 ) + pCare = Bmc_CexCareSatBasedMinimize( pAig, Saig_ManPiNum(pAig), pCex, fHighEffort, fCheckCex, fVerbose ); + else + printf( "SAT-based CEX minimization requires having a single PO.\n" ); + } else if ( fCexInfo ) { Gia_Man_t * p = Gia_ManFromAigSimple( pAig ); From aa21961c24c14c1e8f3c350552d6a1ae15656e21 Mon Sep 17 00:00:00 2001 From: Miodrag Milanovic Date: Wed, 7 Sep 2022 11:42:59 +0200 Subject: [PATCH 36/79] Support using large liberty files --- src/map/scl/sclLiberty.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/map/scl/sclLiberty.c b/src/map/scl/sclLiberty.c index 49b5c2370b..b448885dcd 100644 --- a/src/map/scl/sclLiberty.c +++ b/src/map/scl/sclLiberty.c @@ -63,7 +63,7 @@ struct Scl_Tree_t_ { char * pFileName; // input Liberty file name char * pContents; // file contents - int nContents; // file size + long nContents; // file size int nLines; // line counter int nItems; // number of items int nItermAlloc; // number of items allocated @@ -506,10 +506,10 @@ void Scl_LibertyFixFileName( char * pFileName ) if ( *pHead == '>' ) *pHead = '\\'; } -int Scl_LibertyFileSize( char * pFileName ) +long Scl_LibertyFileSize( char * pFileName ) { FILE * pFile; - int nFileSize; + long nFileSize; pFile = fopen( pFileName, "rb" ); if ( pFile == NULL ) { @@ -521,7 +521,7 @@ int Scl_LibertyFileSize( char * pFileName ) fclose( pFile ); return nFileSize; } -char * Scl_LibertyFileContents( char * pFileName, int nContents ) +char * Scl_LibertyFileContents( char * pFileName, long nContents ) { FILE * pFile = fopen( pFileName, "rb" ); char * pContents = ABC_ALLOC( char, nContents+1 ); @@ -558,7 +558,7 @@ void Scl_LibertyStringDump( char * pFileName, Vec_Str_t * vStr ) Scl_Tree_t * Scl_LibertyStart( char * pFileName ) { Scl_Tree_t * p; - int RetValue; + long RetValue; // read the file into the buffer Scl_LibertyFixFileName( pFileName ); RetValue = Scl_LibertyFileSize( pFileName ); From ab5b16ede2ff3a4ab5209df24db2c76700899684 Mon Sep 17 00:00:00 2001 From: Miodrag Milanovic Date: Thu, 8 Sep 2022 16:04:24 +0200 Subject: [PATCH 37/79] Additional fix for large liberty files --- src/map/scl/sclLiberty.c | 52 ++++++++++++++++++++-------------------- 1 file changed, 26 insertions(+), 26 deletions(-) diff --git a/src/map/scl/sclLiberty.c b/src/map/scl/sclLiberty.c index b448885dcd..60aaded2ed 100644 --- a/src/map/scl/sclLiberty.c +++ b/src/map/scl/sclLiberty.c @@ -42,20 +42,20 @@ typedef enum { typedef struct Scl_Pair_t_ Scl_Pair_t; struct Scl_Pair_t_ { - int Beg; // item beginning - int End; // item end + long Beg; // item beginning + long End; // item end }; typedef struct Scl_Item_t_ Scl_Item_t; struct Scl_Item_t_ { int Type; // Scl_LibertyType_t - int iLine; // file line where the item's spec begins + long iLine; // file line where the item's spec begins Scl_Pair_t Key; // key part Scl_Pair_t Head; // head part Scl_Pair_t Body; // body part - int Next; // next item in the list - int Child; // first child item + long Next; // next item in the list + long Child; // first child item }; typedef struct Scl_Tree_t_ Scl_Tree_t; @@ -64,9 +64,9 @@ struct Scl_Tree_t_ char * pFileName; // input Liberty file name char * pContents; // file contents long nContents; // file size - int nLines; // line counter - int nItems; // number of items - int nItermAlloc; // number of items allocated + long nLines; // line counter + long nItems; // number of items + long nItermAlloc; // number of items allocated Scl_Item_t * pItems; // the items char * pError; // the error string abctime clkStart; // beginning time @@ -74,11 +74,11 @@ struct Scl_Tree_t_ }; static inline Scl_Item_t * Scl_LibertyRoot( Scl_Tree_t * p ) { return p->pItems; } -static inline Scl_Item_t * Scl_LibertyItem( Scl_Tree_t * p, int v ) { assert( v < p->nItems ); return v < 0 ? NULL : p->pItems + v; } -static inline int Scl_LibertyCompare( Scl_Tree_t * p, Scl_Pair_t Pair, char * pStr ) { return strncmp( p->pContents+Pair.Beg, pStr, Pair.End-Pair.Beg ) || ((int)strlen(pStr) != Pair.End-Pair.Beg); } +static inline Scl_Item_t * Scl_LibertyItem( Scl_Tree_t * p, long v ) { assert( v < p->nItems ); return v < 0 ? NULL : p->pItems + v; } +static inline long Scl_LibertyCompare( Scl_Tree_t * p, Scl_Pair_t Pair, char * pStr ) { return strncmp( p->pContents+Pair.Beg, pStr, Pair.End-Pair.Beg ) || ((long)strlen(pStr) != Pair.End-Pair.Beg); } static inline void Scl_PrintWord( FILE * pFile, Scl_Tree_t * p, Scl_Pair_t Pair ) { char * pBeg = p->pContents+Pair.Beg, * pEnd = p->pContents+Pair.End; while ( pBeg < pEnd ) fputc( *pBeg++, pFile ); } -static inline void Scl_PrintSpace( FILE * pFile, int nOffset ) { int i; for ( i = 0; i < nOffset; i++ ) fputc(' ', pFile); } -static inline int Scl_LibertyItemId( Scl_Tree_t * p, Scl_Item_t * pItem ) { return pItem - p->pItems; } +static inline void Scl_PrintSpace( FILE * pFile, long nOffset ) { long i; for ( i = 0; i < nOffset; i++ ) fputc(' ', pFile); } +static inline long Scl_LibertyItemId( Scl_Tree_t * p, Scl_Item_t * pItem ) { return pItem - p->pItems; } #define Scl_ItemForEachChild( p, pItem, pChild ) \ for ( pChild = Scl_LibertyItem(p, pItem->Child); pChild; pChild = Scl_LibertyItem(p, pChild->Next) ) @@ -166,9 +166,9 @@ int Scl_LibertyParseDump( Scl_Tree_t * p, char * pFileName ) SeeAlso [] ***********************************************************************/ -int Scl_LibertyCountItems( char * pBeg, char * pEnd ) +long Scl_LibertyCountItems( char * pBeg, char * pEnd ) { - int Counter = 0; + long Counter = 0; for ( ; pBeg < pEnd; pBeg++ ) Counter += (*pBeg == '(' || *pBeg == ':'); return Counter; @@ -213,11 +213,11 @@ void Scl_LibertyWipeOutComments( char * pBeg, char * pEnd ) } } } -static inline int Scl_LibertyCharIsSpace( char c ) +static inline long Scl_LibertyCharIsSpace( char c ) { return c == ' ' || c == '\t' || c == '\r' || c == '\n' || c == '\\'; } -static inline int Scl_LibertySkipSpaces( Scl_Tree_t * p, char ** ppPos, char * pEnd, int fStopAtNewLine ) +static inline long Scl_LibertySkipSpaces( Scl_Tree_t * p, char ** ppPos, char * pEnd, int fStopAtNewLine ) { char * pPos = *ppPos; for ( ; pPos < pEnd; pPos++ ) @@ -235,7 +235,7 @@ static inline int Scl_LibertySkipSpaces( Scl_Tree_t * p, char ** ppPos, char * p return pPos == pEnd; } // skips entry delimited by " :;(){}" and returns 1 if reached the end -static inline int Scl_LibertySkipEntry( char ** ppPos, char * pEnd ) +static inline long Scl_LibertySkipEntry( char ** ppPos, char * pEnd ) { char * pPos = *ppPos; if ( *pPos == '\"' ) @@ -262,7 +262,7 @@ static inline int Scl_LibertySkipEntry( char ** ppPos, char * pEnd ) // finds the matching closing symbol static inline char * Scl_LibertyFindMatch( char * pPos, char * pEnd ) { - int Counter = 0; + long Counter = 0; assert( *pPos == '(' || *pPos == '{' ); if ( *pPos == '(' ) { @@ -356,10 +356,10 @@ char * Scl_LibertyReadString( Scl_Tree_t * p, Scl_Pair_t Pair ) Buffer[Pair.End-Pair.Beg] = 0; return Buffer; } -int Scl_LibertyItemNum( Scl_Tree_t * p, Scl_Item_t * pRoot, char * pName ) +long Scl_LibertyItemNum( Scl_Tree_t * p, Scl_Item_t * pRoot, char * pName ) { Scl_Item_t * pItem; - int Counter = 0; + long Counter = 0; Scl_ItemForEachChildName( p, pRoot, pItem, pName ) Counter++; return Counter; @@ -376,7 +376,7 @@ int Scl_LibertyItemNum( Scl_Tree_t * p, Scl_Item_t * pRoot, char * pName ) SeeAlso [] ***********************************************************************/ -int Scl_LibertyBuildItem( Scl_Tree_t * p, char ** ppPos, char * pEnd ) +long Scl_LibertyBuildItem( Scl_Tree_t * p, char ** ppPos, char * pEnd ) { Scl_Item_t * pItem; Scl_Pair_t Key, Head, Body; @@ -482,7 +482,7 @@ int Scl_LibertyBuildItem( Scl_Tree_t * p, char ** ppPos, char * pEnd ) if ( p->pError == NULL ) { p->pError = ABC_ALLOC( char, 1000 ); - sprintf( p->pError, "File \"%s\". Line %6d. Failed to parse entry \"%s\".\n", + sprintf( p->pError, "File \"%s\". Line %6ld. Failed to parse entry \"%s\".\n", p->pFileName, p->nLines, Scl_LibertyReadString(p, Key) ); } return -1; @@ -525,7 +525,7 @@ char * Scl_LibertyFileContents( char * pFileName, long nContents ) { FILE * pFile = fopen( pFileName, "rb" ); char * pContents = ABC_ALLOC( char, nContents+1 ); - int RetValue = 0; + long RetValue = 0; RetValue = fread( pContents, nContents, 1, pFile ); fclose( pFile ); pContents[nContents] = 0; @@ -534,7 +534,7 @@ char * Scl_LibertyFileContents( char * pFileName, long nContents ) void Scl_LibertyStringDump( char * pFileName, Vec_Str_t * vStr ) { FILE * pFile = fopen( pFileName, "wb" ); - int RetValue = 0; + long RetValue = 0; if ( pFile == NULL ) { printf( "Scl_LibertyStringDump(): The output file is unavailable.\n" ); @@ -682,10 +682,10 @@ int Scl_LibertyReadCellIsThreeState( Scl_Tree_t * p, Scl_Item_t * pCell ) return 1; return 0; } -int Scl_LibertyReadCellOutputNum( Scl_Tree_t * p, Scl_Item_t * pCell ) +long Scl_LibertyReadCellOutputNum( Scl_Tree_t * p, Scl_Item_t * pCell ) { Scl_Item_t * pPin; - int Counter = 0; + long Counter = 0; Scl_ItemForEachChildName( p, pCell, pPin, "pin" ) if ( Scl_LibertyReadPinFormula(p, pPin) ) Counter++; From 18b9c612f43d865e4dec0ef996f617571f9047d9 Mon Sep 17 00:00:00 2001 From: Catherine Date: Sat, 4 Feb 2023 03:14:44 +0000 Subject: [PATCH 38/79] Add WASI support in Exa4_ManSolve. --- src/sat/bmc/bmcMaj.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/sat/bmc/bmcMaj.c b/src/sat/bmc/bmcMaj.c index 2525f943c8..1059a4ae3a 100644 --- a/src/sat/bmc/bmcMaj.c +++ b/src/sat/bmc/bmcMaj.c @@ -1650,7 +1650,11 @@ Vec_Int_t * Exa4_ManSolve( char * pFileNameIn, char * pFileNameOut, int TimeOut, sprintf( pCommand, "%s --time=%d %s %s > %s", pKissat, TimeOut, fVerboseSolver ? "": "-q", pFileNameIn, pFileNameOut ); else sprintf( pCommand, "%s %s %s > %s", pKissat, fVerboseSolver ? "": "-q", pFileNameIn, pFileNameOut ); +#ifdef __wasm + if ( 1 ) +#else if ( system( pCommand ) == -1 ) +#endif { fprintf( stdout, "Command \"%s\" did not succeed.\n", pCommand ); return 0; From 3f9b46591c7134bf546c14e90a29c643b8189c6e Mon Sep 17 00:00:00 2001 From: Sean Cross Date: Wed, 15 Feb 2023 22:58:24 +0800 Subject: [PATCH 39/79] casts: add casts for unsigned -> signed int When compiling on Darwin ARM64 hardware using the Conda clang compiler, compilation fails due to these casts going from `unsigned` to `int`. In these cases, a cast appears to be the correct approach. Add a cast to make the compiler happy. Signed-off-by: Sean Cross --- src/aig/gia/giaIso3.c | 2 +- src/base/acb/acbTest.c | 12 ++++++------ 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/src/aig/gia/giaIso3.c b/src/aig/gia/giaIso3.c index 8bea4bbfae..bcd73c36fd 100644 --- a/src/aig/gia/giaIso3.c +++ b/src/aig/gia/giaIso3.c @@ -177,7 +177,7 @@ Vec_Wec_t * Gia_Iso4Gia( Gia_Man_t * p ) Vec_WecForEachLevel( vLevs, vLevel, l ) { Gia_Obj_t * pObj; int i; - int RandC[2] = { Abc_Random(0), Abc_Random(0) }; + int RandC[2] = { (int)Abc_Random(0), (int)Abc_Random(0) }; if ( l == 0 ) { Gia_ManForEachObjVec( vLevel, p, pObj, i ) diff --git a/src/base/acb/acbTest.c b/src/base/acb/acbTest.c index 6290b88ea3..c664eadafc 100644 --- a/src/base/acb/acbTest.c +++ b/src/base/acb/acbTest.c @@ -421,8 +421,8 @@ Gia_Man_t * Acb_NtkGiaDeriveMiter( Gia_Man_t * pOne, Gia_Man_t * pTwo, int Type { for ( i = 0; i < Gia_ManCoNum(pOne); i += 2 ) { - int pLitsF[2] = { Gia_ManCo(pOne, i)->Value, Gia_ManCo(pOne, i+1)->Value }; - int pLitsS[2] = { Gia_ManCo(pTwo, i)->Value, Gia_ManCo(pTwo, i+1)->Value }; + int pLitsF[2] = { (int)Gia_ManCo(pOne, i)->Value, (int)Gia_ManCo(pOne, i+1)->Value }; + int pLitsS[2] = { (int)Gia_ManCo(pTwo, i)->Value, (int)Gia_ManCo(pTwo, i+1)->Value }; Gia_ManAppendCo( pNew, pLitsF[0] ); Gia_ManAppendCo( pNew, pLitsS[0] ); } @@ -431,8 +431,8 @@ Gia_Man_t * Acb_NtkGiaDeriveMiter( Gia_Man_t * pOne, Gia_Man_t * pTwo, int Type { for ( i = 0; i < Gia_ManCoNum(pOne); i += 2 ) { - int pLitsF[2] = { Gia_ManCo(pOne, i)->Value, Gia_ManCo(pOne, i+1)->Value }; - int pLitsS[2] = { Gia_ManCo(pTwo, i)->Value, Gia_ManCo(pTwo, i+1)->Value }; + int pLitsF[2] = { (int)Gia_ManCo(pOne, i)->Value, (int)Gia_ManCo(pOne, i+1)->Value }; + int pLitsS[2] = { (int)Gia_ManCo(pTwo, i)->Value, (int)Gia_ManCo(pTwo, i+1)->Value }; Gia_ManAppendCo( pNew, pLitsF[1] ); Gia_ManAppendCo( pNew, pLitsS[1] ); } @@ -441,8 +441,8 @@ Gia_Man_t * Acb_NtkGiaDeriveMiter( Gia_Man_t * pOne, Gia_Man_t * pTwo, int Type { for ( i = 0; i < Gia_ManCoNum(pOne); i += 2 ) { - int pLitsF[2] = { Gia_ManCo(pOne, i)->Value, Gia_ManCo(pOne, i+1)->Value }; - int pLitsS[2] = { Gia_ManCo(pTwo, i)->Value, Gia_ManCo(pTwo, i+1)->Value }; + int pLitsF[2] = { (int)Gia_ManCo(pOne, i)->Value, (int)Gia_ManCo(pOne, i+1)->Value }; + int pLitsS[2] = { (int)Gia_ManCo(pTwo, i)->Value, (int)Gia_ManCo(pTwo, i+1)->Value }; Gia_ManAppendCo( pNew, Gia_ManDualCompare( pNew, pLitsF, pLitsS ) ); } } From f89df8087d76ec666b6b0509614958e3a5f7d4cd Mon Sep 17 00:00:00 2001 From: Catherine Date: Thu, 23 Feb 2023 01:14:48 +0000 Subject: [PATCH 40/79] Add WASI support in Abc_Clock. --- src/misc/util/abc_global.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/misc/util/abc_global.h b/src/misc/util/abc_global.h index 2217bb87e4..02dfb679bd 100644 --- a/src/misc/util/abc_global.h +++ b/src/misc/util/abc_global.h @@ -334,7 +334,7 @@ static inline abctime Abc_Clock() #else #define APPLE_MACH 0 #endif -#if (defined(LIN) || defined(LIN64)) && !APPLE_MACH && !defined(__MINGW32__) +#if (defined(LIN) || defined(LIN64)) && !APPLE_MACH && !defined(__MINGW32__) && !defined(__wasm) struct timespec ts; if ( clock_gettime(CLOCK_THREAD_CPUTIME_ID, &ts) < 0 ) return (abctime)-1; From 1de4eafb0da0639199bd97f2fa98471e76327a6b Mon Sep 17 00:00:00 2001 From: Miodrag Milanovic Date: Tue, 6 Jun 2023 13:59:30 +0200 Subject: [PATCH 41/79] fix segfault --- src/base/io/ioWriteVerilog.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/base/io/ioWriteVerilog.c b/src/base/io/ioWriteVerilog.c index 48b15b559b..3d9f8fa389 100644 --- a/src/base/io/ioWriteVerilog.c +++ b/src/base/io/ioWriteVerilog.c @@ -561,7 +561,7 @@ void Io_WriteVerilogObjects( FILE * pFile, Abc_Ntk_t * pNtk, int fOnlyAnds ) vLevels = Vec_VecAlloc( 10 ); Abc_NtkForEachNode( pNtk, pObj, i ) { - if ( Abc_ObjFaninNum(pObj) == 1 || Abc_ObjIsCo(Abc_ObjFanout0(Abc_ObjFanout0(pObj))) ) + if ( Abc_ObjFaninNum(pObj) == 1 && Abc_ObjIsCo(Abc_ObjFanout0(Abc_ObjFanout0(pObj))) ) { int iLit = Abc_Var2Lit( Abc_ObjId( Abc_ObjFanin0(Abc_ObjFanin0(pObj)) ), Abc_NodeIsInv(pObj) ); int iObj = Vec_IntEntry( vMap, iLit ); From 1bd088d027262bc66c6ec2f1ca65c338c71dd168 Mon Sep 17 00:00:00 2001 From: Jannis Harder Date: Wed, 7 Jun 2023 13:58:51 +0200 Subject: [PATCH 42/79] fold: Option (-s) to make sequential cleanup optional --- src/aig/saig/saig.h | 2 +- src/aig/saig/saigConstr2.c | 5 +++-- src/base/abci/abc.c | 12 +++++++++--- src/base/abci/abcDar.c | 4 ++-- src/base/wlc/wlcMem.c | 2 +- 5 files changed, 16 insertions(+), 9 deletions(-) diff --git a/src/aig/saig/saig.h b/src/aig/saig/saig.h index 16e7fb4421..4744b8f86d 100644 --- a/src/aig/saig/saig.h +++ b/src/aig/saig/saig.h @@ -114,7 +114,7 @@ extern Aig_Man_t * Saig_ManDupFoldConstrs( Aig_Man_t * pAig, Vec_Int_t * v extern int Saig_ManDetectConstrTest( Aig_Man_t * p ); extern void Saig_ManDetectConstrFuncTest( Aig_Man_t * p, int nFrames, int nConfs, int nProps, int fOldAlgo, int fVerbose ); /*=== saigConstr2.c ==========================================================*/ -extern Aig_Man_t * Saig_ManDupFoldConstrsFunc( Aig_Man_t * pAig, int fCompl, int fVerbose ); +extern Aig_Man_t * Saig_ManDupFoldConstrsFunc( Aig_Man_t * pAig, int fCompl, int fVerbose, int fSeqCleanup ); extern Aig_Man_t * Saig_ManDupUnfoldConstrsFunc( Aig_Man_t * pAig, int nFrames, int nConfs, int nProps, int fOldAlgo, int fVerbose ); // -- jlong -- begin extern Aig_Man_t * Saig_ManDupFoldConstrsFunc2( Aig_Man_t * pAig, int fCompl, int fVerbose, int typeII_cnt ); diff --git a/src/aig/saig/saigConstr2.c b/src/aig/saig/saigConstr2.c index 6e560b59e4..79f557616e 100644 --- a/src/aig/saig/saigConstr2.c +++ b/src/aig/saig/saigConstr2.c @@ -939,7 +939,7 @@ Aig_Man_t * Saig_ManDupUnfoldConstrsFunc( Aig_Man_t * pAig, int nFrames, int nCo SeeAlso [] ***********************************************************************/ -Aig_Man_t * Saig_ManDupFoldConstrsFunc( Aig_Man_t * pAig, int fCompl, int fVerbose ) +Aig_Man_t * Saig_ManDupFoldConstrsFunc( Aig_Man_t * pAig, int fCompl, int fVerbose, int fSeqCleanup ) { Aig_Man_t * pAigNew; Aig_Obj_t * pMiter, * pFlopOut, * pFlopIn, * pObj; @@ -1000,7 +1000,8 @@ Aig_Man_t * Saig_ManDupFoldConstrsFunc( Aig_Man_t * pAig, int fCompl, int fVerbo // perform cleanup Aig_ManCleanup( pAigNew ); - Aig_ManSeqCleanup( pAigNew ); + if ( fSeqCleanup ) + Aig_ManSeqCleanup( pAigNew ); return pAigNew; } diff --git a/src/base/abci/abc.c b/src/base/abci/abc.c index 5007415fea..2acd485d67 100644 --- a/src/base/abci/abc.c +++ b/src/base/abci/abc.c @@ -28530,14 +28530,16 @@ int Abc_CommandFold( Abc_Frame_t * pAbc, int argc, char ** argv ) Abc_Ntk_t * pNtk, * pNtkRes; int fCompl; int fVerbose; + int fSeqCleanup; int c; - extern Abc_Ntk_t * Abc_NtkDarFold( Abc_Ntk_t * pNtk, int fCompl, int fVerbose ); + extern Abc_Ntk_t * Abc_NtkDarFold( Abc_Ntk_t * pNtk, int fCompl, int fVerbose, int fSeqCleanup ); pNtk = Abc_FrameReadNtk(pAbc); // set defaults fCompl = 0; fVerbose = 0; + fSeqCleanup = 1; Extra_UtilGetoptReset(); - while ( ( c = Extra_UtilGetopt( argc, argv, "cvh" ) ) != EOF ) + while ( ( c = Extra_UtilGetopt( argc, argv, "cvsh" ) ) != EOF ) { switch ( c ) { @@ -28547,6 +28549,9 @@ int Abc_CommandFold( Abc_Frame_t * pAbc, int argc, char ** argv ) case 'v': fVerbose ^= 1; break; + case 's': + fSeqCleanup ^= 1; + break; case 'h': goto usage; default: @@ -28576,7 +28581,7 @@ int Abc_CommandFold( Abc_Frame_t * pAbc, int argc, char ** argv ) if ( Abc_NtkIsComb(pNtk) ) Abc_Print( 0, "The network is combinational.\n" ); // modify the current network - pNtkRes = Abc_NtkDarFold( pNtk, fCompl, fVerbose ); + pNtkRes = Abc_NtkDarFold( pNtk, fCompl, fVerbose, fSeqCleanup ); if ( pNtkRes == NULL ) { Abc_Print( 1,"Transformation has failed.\n" ); @@ -28591,6 +28596,7 @@ int Abc_CommandFold( Abc_Frame_t * pAbc, int argc, char ** argv ) Abc_Print( -2, "\t (constraints fail when any of them becomes 1 in any timeframe)\n" ); Abc_Print( -2, "\t-c : toggle complementing constraints while folding [default = %s]\n", fCompl? "yes": "no" ); Abc_Print( -2, "\t-v : toggle printing verbose information [default = %s]\n", fVerbose? "yes": "no" ); + Abc_Print( -2, "\t-s : toggle performing sequential cleanup [default = %s]\n", fSeqCleanup? "yes": "no" ); Abc_Print( -2, "\t-h : print the command usage\n"); return 1; } diff --git a/src/base/abci/abcDar.c b/src/base/abci/abcDar.c index 76c7cf5473..a209143f35 100644 --- a/src/base/abci/abcDar.c +++ b/src/base/abci/abcDar.c @@ -4682,7 +4682,7 @@ Abc_Ntk_t * Abc_NtkDarUnfold( Abc_Ntk_t * pNtk, int nFrames, int nConfs, int nPr SeeAlso [] ***********************************************************************/ -Abc_Ntk_t * Abc_NtkDarFold( Abc_Ntk_t * pNtk, int fCompl, int fVerbose ) +Abc_Ntk_t * Abc_NtkDarFold( Abc_Ntk_t * pNtk, int fCompl, int fVerbose, int fSeqCleanup ) { Abc_Ntk_t * pNtkAig; Aig_Man_t * pMan, * pTemp; @@ -4690,7 +4690,7 @@ Abc_Ntk_t * Abc_NtkDarFold( Abc_Ntk_t * pNtk, int fCompl, int fVerbose ) pMan = Abc_NtkToDar( pNtk, 0, 1 ); if ( pMan == NULL ) return NULL; - pMan = Saig_ManDupFoldConstrsFunc( pTemp = pMan, fCompl, fVerbose ); + pMan = Saig_ManDupFoldConstrsFunc( pTemp = pMan, fCompl, fVerbose, fSeqCleanup ); Aig_ManStop( pTemp ); pNtkAig = Abc_NtkFromAigPhase( pMan ); pNtkAig->pName = Extra_UtilStrsav(pMan->pName); diff --git a/src/base/wlc/wlcMem.c b/src/base/wlc/wlcMem.c index b2d5c5ee08..1f35f324e2 100644 --- a/src/base/wlc/wlcMem.c +++ b/src/base/wlc/wlcMem.c @@ -1024,7 +1024,7 @@ int Wlc_NtkMemAbstract( Wlc_Ntk_t * p, int nIterMax, int fDumpAbs, int fPdrVerbo pAig = Gia_ManToAigSimple( pAbs ); Gia_ManStop( pAbs ); pAig->nConstrs = 1; - pAig = Saig_ManDupFoldConstrsFunc( pTempAig = pAig, 0, 0 ); + pAig = Saig_ManDupFoldConstrsFunc( pTempAig = pAig, 0, 0, 1 ); Aig_ManStop( pTempAig ); pAbs = Gia_ManFromAigSimple( pAig ); Aig_ManStop( pAig ); From 6b66b81722eca588b7b3e17f5a95312726ed4049 Mon Sep 17 00:00:00 2001 From: Catherine Date: Thu, 23 Feb 2023 01:14:48 +0000 Subject: [PATCH 43/79] Add WASI support in Abc_ThreadClock. --- src/misc/util/abc_global.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/misc/util/abc_global.h b/src/misc/util/abc_global.h index 0b4da33906..9090460823 100644 --- a/src/misc/util/abc_global.h +++ b/src/misc/util/abc_global.h @@ -353,7 +353,7 @@ static inline abctime Abc_ThreadClock() #else #define APPLE_MACH 0 #endif -#if (defined(LIN) || defined(LIN64)) && !APPLE_MACH && !defined(__MINGW32__) +#if (defined(LIN) || defined(LIN64)) && !APPLE_MACH && !defined(__MINGW32__) && !defined(__wasm) struct timespec ts; if ( clock_gettime(CLOCK_THREAD_CPUTIME_ID, &ts) < 0 ) return (abctime)-1; From 89a5395fc694c5bd5061cf81b62a2849f155dca7 Mon Sep 17 00:00:00 2001 From: Catherine Date: Wed, 13 Sep 2023 11:50:17 +0000 Subject: [PATCH 44/79] Add WASI support in Cnf_RunSolverOnce and Cnf_SplitCnfFile. --- src/sat/cnf/cnfUtil.c | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/sat/cnf/cnfUtil.c b/src/sat/cnf/cnfUtil.c index e67328171e..3a47ae796f 100644 --- a/src/sat/cnf/cnfUtil.c +++ b/src/sat/cnf/cnfUtil.c @@ -82,7 +82,11 @@ Vec_Int_t *Cnf_RunSolverOnce(int Id, int Rand, int TimeOut, int fVerbose) FILE * pFile = fopen(FileNameIn, "rb"); if ( pFile != NULL ) { fclose( pFile ); +#if defined(__wasm) + if ( 1 ) { +#else if (system(pCommand) == -1) { +#endif fprintf(stdout, "Command \"%s\" did not succeed.\n", pCommand); return 0; } @@ -764,7 +768,11 @@ void Cnf_SplitCnfFile(char * pFileName, int nParts, int iVarBeg, int iVarEnd, in char Command[1000]; sprintf(Command, "satelite --verbosity=0 -pre temp.cnf %s", FileName); Cnf_DataWriteIntoFile(pCnf, "temp.cnf", 0, NULL, NULL); +#if defined(__wasm) + if ( 1 ) { +#else if (system(Command) == -1) { +#endif fprintf(stdout, "Command \"%s\" did not succeed. Preprocessing skipped.\n", Command); Cnf_DataWriteIntoFile(pCnf, FileName, 0, NULL, NULL); } From daad9ede0137dc58487a0abc126253e671a85b14 Mon Sep 17 00:00:00 2001 From: Catherine Date: Wed, 13 Sep 2023 13:57:48 +0000 Subject: [PATCH 45/79] Add WASI support in Gia_StochProcessOne. --- src/aig/gia/giaStoch.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/aig/gia/giaStoch.c b/src/aig/gia/giaStoch.c index 78fe2cb677..b6c3c101f0 100644 --- a/src/aig/gia/giaStoch.c +++ b/src/aig/gia/giaStoch.c @@ -67,7 +67,11 @@ Gia_Man_t * Gia_StochProcessOne( Gia_Man_t * p, char * pScript, int Rand, int Ti sprintf( FileName, "%06x.aig", Rand ); Gia_AigerWrite( p, FileName, 0, 0, 0 ); sprintf( Command, "./abc -q \"&read %s; %s; &write %s\"", FileName, pScript, FileName ); +#if defined(__wasm) + if ( 1 ) +#else if ( system( (char *)Command ) ) +#endif { fprintf( stderr, "The following command has returned non-zero exit status:\n" ); fprintf( stderr, "\"%s\"\n", (char *)Command ); From 896e5e7dedf9b9b1459fa019f1fa8aa8101fdf43 Mon Sep 17 00:00:00 2001 From: Miodrag Milanovic Date: Fri, 13 Oct 2023 14:28:47 +0200 Subject: [PATCH 46/79] disable command history --- src/base/cmd/cmdHist.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/base/cmd/cmdHist.c b/src/base/cmd/cmdHist.c index 0aaf6d5cf0..109c8e6825 100644 --- a/src/base/cmd/cmdHist.c +++ b/src/base/cmd/cmdHist.c @@ -30,7 +30,7 @@ ABC_NAMESPACE_IMPL_START /// DECLARATIONS /// //////////////////////////////////////////////////////////////////////// -#define ABC_USE_HISTORY 1 +// #define ABC_USE_HISTORY 1 //////////////////////////////////////////////////////////////////////// /// FUNCTION DEFINITIONS /// From f63471bdf5d64c44e0b107fbe9e2629f969030b6 Mon Sep 17 00:00:00 2001 From: Jannis Harder Date: Fri, 2 Feb 2024 16:47:38 +0100 Subject: [PATCH 47/79] pdr -X to write CEXes immediately --- src/base/abci/abc.c | 14 ++++++++++++-- src/proof/pdr/pdr.h | 2 ++ src/proof/pdr/pdrCore.c | 41 +++++++++++++++++++++++++++++++++++++++-- src/proof/pdr/pdrIncr.c | 4 +++- 4 files changed, 56 insertions(+), 5 deletions(-) diff --git a/src/base/abci/abc.c b/src/base/abci/abc.c index 26f49799f1..e79dcaa873 100644 --- a/src/base/abci/abc.c +++ b/src/base/abci/abc.c @@ -29275,7 +29275,7 @@ int Abc_CommandPdr( Abc_Frame_t * pAbc, int argc, char ** argv ) int c; Pdr_ManSetDefaultParams( pPars ); Extra_UtilGetoptReset(); - while ( ( c = Extra_UtilGetopt( argc, argv, "MFCDQTHGSLIaxrmuyfqipdegjonctkvwzh" ) ) != EOF ) + while ( ( c = Extra_UtilGetopt( argc, argv, "MFCDQTHGSLIXaxrmuyfqipdegjonctkvwzh" ) ) != EOF ) { switch ( c ) { @@ -29396,6 +29396,15 @@ int Abc_CommandPdr( Abc_Frame_t * pAbc, int argc, char ** argv ) pPars->pInvFileName = argv[globalUtilOptind]; globalUtilOptind++; break; + case 'X': + if ( globalUtilOptind >= argc ) + { + Abc_Print( -1, "Command line switch \"-X\" should be followed by a directory name.\n" ); + goto usage; + } + pPars->pCexFilePrefix = argv[globalUtilOptind]; + globalUtilOptind++; + break; case 'a': pPars->fSolveAll ^= 1; break; @@ -29503,7 +29512,7 @@ int Abc_CommandPdr( Abc_Frame_t * pAbc, int argc, char ** argv ) return 0; usage: - Abc_Print( -2, "usage: pdr [-MFCDQTHGS ] [-LI ] [-axrmuyfqipdegjonctkvwzh]\n" ); + Abc_Print( -2, "usage: pdr [-MFCDQTHGS ] [-LI ] [-X ] [-axrmuyfqipdegjonctkvwzh]\n" ); Abc_Print( -2, "\t model checking using property directed reachability (aka IC3)\n" ); Abc_Print( -2, "\t pioneered by Aaron R. Bradley (http://theory.stanford.edu/~arbrad/)\n" ); Abc_Print( -2, "\t with improvements by Niklas Een (http://een.se/niklas/)\n" ); @@ -29518,6 +29527,7 @@ int Abc_CommandPdr( Abc_Frame_t * pAbc, int argc, char ** argv ) Abc_Print( -2, "\t-S num : * value to seed the SAT solver with [default = %d]\n", pPars->nRandomSeed ); Abc_Print( -2, "\t-L file: the log file name [default = %s]\n", pLogFileName ? pLogFileName : "no logging" ); Abc_Print( -2, "\t-I file: the invariant file name [default = %s]\n", pPars->pInvFileName ? pPars->pInvFileName : "default name" ); + Abc_Print( -2, "\t-X pref: when solving all outputs, store CEXes immediately as *.aiw [default = %s]\n", pPars->pCexFilePrefix ? pPars->pCexFilePrefix : "disabled"); Abc_Print( -2, "\t-a : toggle solving all outputs even if one of them is SAT [default = %s]\n", pPars->fSolveAll? "yes": "no" ); Abc_Print( -2, "\t-x : toggle storing CEXes when solving all outputs [default = %s]\n", pPars->fStoreCex? "yes": "no" ); Abc_Print( -2, "\t-r : toggle using more effort in generalization [default = %s]\n", pPars->fTwoRounds? "yes": "no" ); diff --git a/src/proof/pdr/pdr.h b/src/proof/pdr/pdr.h index d8a548d5c1..00d15a8bc8 100644 --- a/src/proof/pdr/pdr.h +++ b/src/proof/pdr/pdr.h @@ -84,6 +84,7 @@ struct Pdr_Par_t_ abctime timeLastSolved; // the time when the last output was solved Vec_Int_t * vOutMap; // in the multi-output mode, contains status for each PO (0 = sat; 1 = unsat; negative = undecided) char * pInvFileName; // invariable file name + char * pCexFilePrefix; // CEX output prefix }; //////////////////////////////////////////////////////////////////////// @@ -95,6 +96,7 @@ struct Pdr_Par_t_ //////////////////////////////////////////////////////////////////////// /*=== pdrCore.c ==========================================================*/ +extern void Pdr_OutputCexToDir( Pdr_Par_t * pPars, Abc_Cex_t * pCex ); extern void Pdr_ManSetDefaultParams( Pdr_Par_t * pPars ); extern int Pdr_ManSolve( Aig_Man_t * p, Pdr_Par_t * pPars ); diff --git a/src/proof/pdr/pdrCore.c b/src/proof/pdr/pdrCore.c index d77fd4e17d..8937c8c19f 100644 --- a/src/proof/pdr/pdrCore.c +++ b/src/proof/pdr/pdrCore.c @@ -80,6 +80,7 @@ void Pdr_ManSetDefaultParams( Pdr_Par_t * pPars ) pPars->nDropOuts = 0; // the number of timed out outputs pPars->timeLastSolved = 0; // last one solved pPars->pInvFileName = NULL; // invariant file name + pPars->pCexFilePrefix = NULL; // CEX output prefix } /**Function************************************************************* @@ -1026,6 +1027,38 @@ int Pdr_ManBlockCube( Pdr_Man_t * p, Pdr_Set_t * pCube ) return 1; } +void Pdr_OutputCexToDir( Pdr_Par_t * pPars, Abc_Cex_t * pCex ) +{ + int i, f, iBit; + size_t iCexPathSize; + char * pCexPath; + FILE * pCexFile; + + iCexPathSize = snprintf( NULL, 0, "%s%d.aiw", pPars->pCexFilePrefix, pCex->iPo ) + 1; + pCexPath = malloc( iCexPathSize ); + snprintf( pCexPath, iCexPathSize, "%s%d.aiw", pPars->pCexFilePrefix, pCex->iPo ); + Abc_Print( 1, "Writing CEX for output %d to %s\n", pCex->iPo, pCexPath ); + pCexFile = fopen( pCexPath, "w" ); + free( pCexPath ); + + fprintf( pCexFile, "1\n"); + fprintf( pCexFile, "b%d\n", pCex->iPo); + + iBit = 0; + for ( i = 0; i < pCex->nRegs; i++, iBit++ ) + putc( '0' + Abc_InfoHasBit(pCex->pData, iBit), pCexFile ); + putc( '\n', pCexFile ); + + for ( f = 0; f <= pCex->iFrame; f++ ) + { + for ( i = 0; i < pCex->nPis; i++, iBit++ ) + putc( '0' + Abc_InfoHasBit(pCex->pData, iBit), pCexFile ); + putc( '\n', pCexFile ); + } + fprintf( pCexFile, ".\n"); + fclose( pCexFile ); +} + /**Function************************************************************* Synopsis [] @@ -1100,7 +1133,7 @@ int Pdr_ManSolveInt( Pdr_Man_t * p ) p->pAig->pSeqModel = pCexNew; return 0; // SAT } - pCexNew = (p->pPars->fUseBridge || p->pPars->fStoreCex) ? Abc_CexMakeTriv( Aig_ManRegNum(p->pAig), Saig_ManPiNum(p->pAig), Saig_ManPoNum(p->pAig), iFrame*Saig_ManPoNum(p->pAig)+p->iOutCur ) : (Abc_Cex_t *)(ABC_PTRINT_T)1; + pCexNew = (p->pPars->fUseBridge || p->pPars->fStoreCex || p->pPars->pCexFilePrefix) ? Abc_CexMakeTriv( Aig_ManRegNum(p->pAig), Saig_ManPiNum(p->pAig), Saig_ManPoNum(p->pAig), iFrame*Saig_ManPoNum(p->pAig)+p->iOutCur ) : (Abc_Cex_t *)(ABC_PTRINT_T)1; p->pPars->nFailOuts++; if ( p->pPars->vOutMap ) Vec_IntWriteEntry( p->pPars->vOutMap, p->iOutCur, 0 ); if ( !p->pPars->fNotVerbose ) @@ -1109,6 +1142,8 @@ int Pdr_ManSolveInt( Pdr_Man_t * p ) assert( Vec_PtrEntry(p->vCexes, p->iOutCur) == NULL ); if ( p->pPars->fUseBridge ) Gia_ManToBridgeResult( stdout, 0, pCexNew, pCexNew->iPo ); + if ( p->pPars->pCexFilePrefix ) + Pdr_OutputCexToDir( p->pPars, pCexNew ); Vec_PtrWriteEntry( p->vCexes, p->iOutCur, pCexNew ); if ( p->pPars->pFuncOnFail && p->pPars->pFuncOnFail(p->iOutCur, p->pPars->fStoreCex ? (Abc_Cex_t *)Vec_PtrEntry(p->vCexes, p->iOutCur) : NULL) ) { @@ -1217,11 +1252,13 @@ int Pdr_ManSolveInt( Pdr_Man_t * p ) return 0; // SAT } p->pPars->nFailOuts++; - pCexNew = (p->pPars->fUseBridge || p->pPars->fStoreCex) ? Pdr_ManDeriveCex(p) : (Abc_Cex_t *)(ABC_PTRINT_T)1; + pCexNew = (p->pPars->fUseBridge || p->pPars->fStoreCex || p->pPars->pCexFilePrefix) ? Pdr_ManDeriveCex(p) : (Abc_Cex_t *)(ABC_PTRINT_T)1; if ( p->pPars->vOutMap ) Vec_IntWriteEntry( p->pPars->vOutMap, p->iOutCur, 0 ); assert( Vec_PtrEntry(p->vCexes, p->iOutCur) == NULL ); if ( p->pPars->fUseBridge ) Gia_ManToBridgeResult( stdout, 0, pCexNew, pCexNew->iPo ); + if ( p->pPars->pCexFilePrefix ) + Pdr_OutputCexToDir( p->pPars, pCexNew ); Vec_PtrWriteEntry( p->vCexes, p->iOutCur, pCexNew ); if ( p->pPars->pFuncOnFail && p->pPars->pFuncOnFail(p->iOutCur, p->pPars->fStoreCex ? (Abc_Cex_t *)Vec_PtrEntry(p->vCexes, p->iOutCur) : NULL) ) { diff --git a/src/proof/pdr/pdrIncr.c b/src/proof/pdr/pdrIncr.c index 9ca72ba69f..6ba68db992 100644 --- a/src/proof/pdr/pdrIncr.c +++ b/src/proof/pdr/pdrIncr.c @@ -484,7 +484,7 @@ int IPdr_ManSolveInt( Pdr_Man_t * p, int fCheckClauses, int fPushClauses ) p->pAig->pSeqModel = pCexNew; return 0; // SAT } - pCexNew = (p->pPars->fUseBridge || p->pPars->fStoreCex) ? Abc_CexMakeTriv( Aig_ManRegNum(p->pAig), Saig_ManPiNum(p->pAig), Saig_ManPoNum(p->pAig), iFrame*Saig_ManPoNum(p->pAig)+p->iOutCur ) : (Abc_Cex_t *)(ABC_PTRINT_T)1; + pCexNew = (p->pPars->fUseBridge || p->pPars->fStoreCex || p->pPars->pCexFilePrefix) ? Abc_CexMakeTriv( Aig_ManRegNum(p->pAig), Saig_ManPiNum(p->pAig), Saig_ManPoNum(p->pAig), iFrame*Saig_ManPoNum(p->pAig)+p->iOutCur ) : (Abc_Cex_t *)(ABC_PTRINT_T)1; p->pPars->nFailOuts++; if ( p->pPars->vOutMap ) Vec_IntWriteEntry( p->pPars->vOutMap, p->iOutCur, 0 ); if ( !p->pPars->fNotVerbose ) @@ -493,6 +493,8 @@ int IPdr_ManSolveInt( Pdr_Man_t * p, int fCheckClauses, int fPushClauses ) assert( Vec_PtrEntry(p->vCexes, p->iOutCur) == NULL ); if ( p->pPars->fUseBridge ) Gia_ManToBridgeResult( stdout, 0, pCexNew, pCexNew->iPo ); + if ( p->pPars->pCexFilePrefix ) + Pdr_OutputCexToDir( p->pPars, pCexNew ); Vec_PtrWriteEntry( p->vCexes, p->iOutCur, pCexNew ); if ( p->pPars->pFuncOnFail && p->pPars->pFuncOnFail(p->iOutCur, p->pPars->fStoreCex ? (Abc_Cex_t *)Vec_PtrEntry(p->vCexes, p->iOutCur) : NULL) ) { From acbe1b1f03c4df825698c6843d1f69f0f1316379 Mon Sep 17 00:00:00 2001 From: Jannis Harder Date: Wed, 7 Feb 2024 18:37:53 +0100 Subject: [PATCH 48/79] Fix pdr timing output --- src/proof/pdr/pdrCore.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/proof/pdr/pdrCore.c b/src/proof/pdr/pdrCore.c index 8937c8c19f..3742237e0a 100644 --- a/src/proof/pdr/pdrCore.c +++ b/src/proof/pdr/pdrCore.c @@ -698,7 +698,7 @@ int Pdr_ManGeneralize( Pdr_Man_t * p, int k, Pdr_Set_t * pCube, Pdr_Set_t ** ppP return -1; if ( RetValue == 0 ) { - p->tGeneral += clock() - clk; + p->tGeneral += Abc_Clock() - clk; return 0; } @@ -1035,7 +1035,7 @@ void Pdr_OutputCexToDir( Pdr_Par_t * pPars, Abc_Cex_t * pCex ) FILE * pCexFile; iCexPathSize = snprintf( NULL, 0, "%s%d.aiw", pPars->pCexFilePrefix, pCex->iPo ) + 1; - pCexPath = malloc( iCexPathSize ); + pCexPath = (char *)malloc( iCexPathSize ); snprintf( pCexPath, iCexPathSize, "%s%d.aiw", pPars->pCexFilePrefix, pCex->iPo ); Abc_Print( 1, "Writing CEX for output %d to %s\n", pCex->iPo, pCexPath ); pCexFile = fopen( pCexPath, "w" ); From c832967200f1b5578cf4a057cb9b5b5acf816844 Mon Sep 17 00:00:00 2001 From: Jannis Harder Date: Tue, 27 Feb 2024 10:35:32 +0100 Subject: [PATCH 49/79] Improved anytime pdr --- src/base/abci/abc.c | 6 +- src/proof/pdr/pdr.h | 1 + src/proof/pdr/pdrCore.c | 333 ++++++++++++++++++++++++++++++++++++---- src/proof/pdr/pdrIncr.c | 4 +- src/proof/pdr/pdrInt.h | 8 + src/proof/pdr/pdrInv.c | 77 +++++++--- src/proof/pdr/pdrMan.c | 3 +- src/proof/pdr/pdrSat.c | 3 +- src/proof/pdr/pdrUtil.c | 39 +++++ 9 files changed, 423 insertions(+), 51 deletions(-) diff --git a/src/base/abci/abc.c b/src/base/abci/abc.c index e79dcaa873..cf5b341f31 100644 --- a/src/base/abci/abc.c +++ b/src/base/abci/abc.c @@ -29275,7 +29275,7 @@ int Abc_CommandPdr( Abc_Frame_t * pAbc, int argc, char ** argv ) int c; Pdr_ManSetDefaultParams( pPars ); Extra_UtilGetoptReset(); - while ( ( c = Extra_UtilGetopt( argc, argv, "MFCDQTHGSLIXaxrmuyfqipdegjonctkvwzh" ) ) != EOF ) + while ( ( c = Extra_UtilGetopt( argc, argv, "MFCDQTHGSLIXalxrmuyfqipdegjonctkvwzh" ) ) != EOF ) { switch ( c ) { @@ -29408,6 +29408,9 @@ int Abc_CommandPdr( Abc_Frame_t * pAbc, int argc, char ** argv ) case 'a': pPars->fSolveAll ^= 1; break; + case 'l': + pPars->fAnytime ^= 1; + break; case 'x': pPars->fStoreCex ^= 1; break; @@ -29529,6 +29532,7 @@ int Abc_CommandPdr( Abc_Frame_t * pAbc, int argc, char ** argv ) Abc_Print( -2, "\t-I file: the invariant file name [default = %s]\n", pPars->pInvFileName ? pPars->pInvFileName : "default name" ); Abc_Print( -2, "\t-X pref: when solving all outputs, store CEXes immediately as *.aiw [default = %s]\n", pPars->pCexFilePrefix ? pPars->pCexFilePrefix : "disabled"); Abc_Print( -2, "\t-a : toggle solving all outputs even if one of them is SAT [default = %s]\n", pPars->fSolveAll? "yes": "no" ); + Abc_Print( -2, "\t-l : toggle anytime schedule when solving all outputs [default = %s]\n", pPars->fAnytime? "yes": "no" ); Abc_Print( -2, "\t-x : toggle storing CEXes when solving all outputs [default = %s]\n", pPars->fStoreCex? "yes": "no" ); Abc_Print( -2, "\t-r : toggle using more effort in generalization [default = %s]\n", pPars->fTwoRounds? "yes": "no" ); Abc_Print( -2, "\t-m : toggle using monolythic CNF computation [default = %s]\n", pPars->fMonoCnf? "yes": "no" ); diff --git a/src/proof/pdr/pdr.h b/src/proof/pdr/pdr.h index 00d15a8bc8..8f62e9b693 100644 --- a/src/proof/pdr/pdr.h +++ b/src/proof/pdr/pdr.h @@ -72,6 +72,7 @@ struct Pdr_Par_t_ int fSilent; // totally silent execution int fSolveAll; // do not stop when found a SAT output int fStoreCex; // enable storing counter-examples in MO mode + int fAnytime; // enable anytime scheduling int fUseBridge; // use bridge interface int fUsePropOut; // use property output int nFailOuts; // the number of failed outputs diff --git a/src/proof/pdr/pdrCore.c b/src/proof/pdr/pdrCore.c index 3742237e0a..14bf89970e 100644 --- a/src/proof/pdr/pdrCore.c +++ b/src/proof/pdr/pdrCore.c @@ -136,6 +136,150 @@ Pdr_Set_t * Pdr_ManReduceClause( Pdr_Man_t * p, int k, Pdr_Set_t * pCube ) return pCubeMin; } +void Pdr_ManCompactNullClauses( Pdr_Man_t * p, int k ) +{ + Pdr_Set_t * pCubeK; + Vec_Ptr_t * vArrayK; + int j; + vArrayK = Vec_VecEntry( p->vClauses, k ); + Vec_PtrForEachEntry( Pdr_Set_t *, vArrayK, pCubeK, j ) + { + if ( pCubeK != NULL ) + continue; + Vec_PtrWriteEntry( vArrayK, j, Vec_PtrEntryLast(vArrayK) ); + Vec_PtrPop(vArrayK); + j--; + } +} + +int Pdr_ManPushInfAndRecycledClauses( Pdr_Man_t * p ) +{ + Pdr_Set_t * pTemp, * pCubeK; + Vec_Ptr_t * vArrayK, * vArrayK1; + Aig_Obj_t * pObj; + int j, k, m, RetValue2, fReduce = p->fNewInfClauses; + + p->fNewInfClauses = 0; + + k = Vec_PtrSize(p->vSolvers) - 2; + assert (k >= 0); + + if ( fReduce && p->pPars->fVeryVerbose ) + Abc_Print( 1, "Reducing inf and recycled clauses for frame %d.\n", k ); + + vArrayK = Vec_VecEntry( p->vClauses, k ); + vArrayK1 = Vec_VecEntry( p->vClauses, k + 1 ); + + Vec_PtrSort( vArrayK, (int (*)(const void *, const void *))Pdr_SetBoundSizeLextCompare ); + + Vec_PtrForEachEntry( Pdr_Set_t *, vArrayK, pCubeK, j ) + { + if ( pCubeK == NULL || pCubeK->iBound != PDR_INF_BOUND ) + continue; + + if (fReduce) + { + // remove cubes in the same frame that are contained by pCubeK + Vec_PtrForEachEntryStart( Pdr_Set_t *, vArrayK, pTemp, m, j+1 ) + { + if ( pTemp == NULL ) + continue; + // This is not needed here due to the sort order we're using + // if ( pTemp->iBound > pCubeK->iBound ) + // continue; + if ( !Pdr_SetContains( pTemp, pCubeK ) ) // pCubeK contains pTemp + continue; + pTemp->iBound = 0; + Pdr_SetDeref( pTemp ); + Vec_PtrWriteEntry( vArrayK, m, NULL ); + } + } + + // Is it already implied by other clauses? + RetValue2 = fReduce ? Pdr_ManCheckCubeCs( p, k+1, pCubeK ) : 0; + if ( RetValue2 == -1 ) + { + Pdr_ManCompactNullClauses( p, k ); + return -1; + } + if ( RetValue2 == 1 ) + { + pCubeK->iBound = 0; + p->nInfClauses--; + Pdr_SetDeref( pCubeK ); + Vec_PtrWriteEntry( vArrayK, j, NULL ); + continue; + } + + Pdr_ManSolverAddClause( p, k+1, pCubeK ); + Vec_PtrWriteEntry( vArrayK, j, NULL ); + Vec_PtrPush( vArrayK1, pCubeK ); + } + + if ( (p->pPars->fAnytime || p->pPars->fSolveAll) && fReduce ) + { + Saig_ManForEachPo( p->pAig, pObj, p->iOutCur ) + { + if ( p->pPars->vOutMap && Vec_IntEntry( p->pPars->vOutMap, p->iOutCur ) >= 0 ) + continue; + RetValue2 = Pdr_ManCheckCube( p, k+1, NULL, NULL, p->pPars->nConfLimit, 0, 1 ); + if ( RetValue2 == -1 ) + { + Pdr_ManCompactNullClauses( p, k ); + return -1; + } + if ( RetValue2 == 1 ) + { + // if the output is already in timeout we need to adjust the stats! + if ( Vec_IntEntry( p->pPars->vOutMap, p->iOutCur ) == -1 ) + p->pPars->nDropOuts--; + Abc_Print( 1, "Proved output %d in frame %d.\n", p->iOutCur, k ); + p->pPars->nProveOuts++; + Vec_IntWriteEntry( p->pPars->vOutMap, p->iOutCur, 1 ); + } + } + } + + // Extra debug checks + + // Vec_PtrForEachEntry( Pdr_Set_t *, vArrayK1, pCubeK1, j ) + // { + // assert( pCubeK1->iBound == PDR_INF_BOUND ); + // assert( Pdr_ManCheckCubeCs( p, k+1, pCubeK1 ) != 0 ); + // assert( Pdr_ManCheckCube( p, k, pCubeK1, NULL, 0, 0, 1 ) != 0 ); + // } + + Vec_PtrForEachEntry( Pdr_Set_t *, vArrayK, pCubeK, j ) + { + if ( pCubeK == NULL || pCubeK->iBound <= k ) + continue; + + // Is it already implied by other clauses? + RetValue2 = fReduce ? Pdr_ManCheckCubeCs( p, k+1, pCubeK ) : 0; + if ( RetValue2 == -1 ) + { + Pdr_ManCompactNullClauses( p, k ); + return -1; + } + if ( RetValue2 == 1 ) + { + Pdr_SetDeref( pCubeK ); + Vec_PtrWriteEntry( vArrayK, j, NULL ); + continue; + } + + Pdr_ManSolverAddClause( p, k+1, pCubeK ); + Vec_PtrWriteEntry( vArrayK, j, NULL ); + Vec_PtrPush( vArrayK1, pCubeK ); + } + + // Compact NULL entries + Pdr_ManCompactNullClauses( p, k ); + + return 0; +} + + /**Function************************************************************* Synopsis [Returns 1 if the state could be blocked.] @@ -153,7 +297,6 @@ int Pdr_ManPushClauses( Pdr_Man_t * p ) Vec_Ptr_t * vArrayK, * vArrayK1; int i, j, k, m, RetValue = 0, RetValue2, kMax = Vec_PtrSize(p->vSolvers)-1; int iStartFrame = p->pPars->fShiftStart ? p->iUseFrame : 1; - int Counter = 0; abctime clk = Abc_Clock(); assert( p->iUseFrame > 0 ); Vec_VecForEachLevelStartStop( p->vClauses, vArrayK, k, iStartFrame, kMax ) @@ -162,11 +305,11 @@ int Pdr_ManPushClauses( Pdr_Man_t * p ) vArrayK1 = Vec_VecEntry( p->vClauses, k+1 ); Vec_PtrForEachEntry( Pdr_Set_t *, vArrayK, pCubeK, j ) { - Counter++; - // remove cubes in the same frame that are contained by pCubeK Vec_PtrForEachEntryStart( Pdr_Set_t *, vArrayK, pTemp, m, j+1 ) { + if ( pTemp->iBound > pCubeK->iBound ) + continue; if ( !Pdr_SetContains( pTemp, pCubeK ) ) // pCubeK contains pTemp continue; Pdr_SetDeref( pTemp ); @@ -198,6 +341,9 @@ int Pdr_ManPushClauses( Pdr_Man_t * p ) // check if the clause subsumes others Vec_PtrForEachEntry( Pdr_Set_t *, vArrayK1, pCubeK1, i ) { + // Subsuming a clause of the invariant with a non-invariant clause could break the invariant + if ( pCubeK1->iBound > pCubeK->iBound ) + continue; if ( !Pdr_SetContains( pCubeK1, pCubeK ) ) // pCubeK contains pCubeK1 continue; Pdr_SetDeref( pCubeK1 ); @@ -206,6 +352,7 @@ int Pdr_ManPushClauses( Pdr_Man_t * p ) i--; } // add the last clause + pCubeK->iBound = k+1; Vec_PtrPush( vArrayK1, pCubeK ); Vec_PtrWriteEntry( vArrayK, j, Vec_PtrEntryLast(vArrayK) ); Vec_PtrPop(vArrayK); @@ -223,6 +370,8 @@ int Pdr_ManPushClauses( Pdr_Man_t * p ) // remove cubes in the same frame that are contained by pCubeK Vec_PtrForEachEntryStart( Pdr_Set_t *, vArrayK, pTemp, m, j+1 ) { + if ( pTemp->iBound > pCubeK->iBound ) + continue; if ( !Pdr_SetContains( pTemp, pCubeK ) ) // pCubeK contains pTemp continue; /* @@ -908,6 +1057,9 @@ int Pdr_ManBlockCube( Pdr_Man_t * p, Pdr_Set_t * pCube ) while ( !Pdr_QueueIsEmpty(p) ) { Counter++; + if (Counter % 100 == 0) { + Pdr_ManPrintProgress( p, 1, Abc_Clock() - p->tStart ); + } pThis = Pdr_QueueHead( p ); if ( pThis->iFrame == 0 || (p->pPars->fUseAbs && Pdr_SetIsInit(pThis->pState, -1)) ) return 0; // SAT @@ -987,6 +1139,7 @@ int Pdr_ManBlockCube( Pdr_Man_t * p, Pdr_Set_t * pCube ) p->nAbsFlops++; Vec_IntAddToEntry( p->vPrio, pCubeMin->Lits[i] / 2, 1 << p->nPrioShift ); } + pCubeMin->iBound = k; Vec_VecPush( p->vClauses, k, pCubeMin ); // consume ref p->nCubes++; // add clause @@ -1076,24 +1229,36 @@ int Pdr_ManSolveInt( Pdr_Man_t * p ) Pdr_Set_t * pCube = NULL; Aig_Obj_t * pObj; Abc_Cex_t * pCexNew; - int iFrame, RetValue = -1; + int iFrame, i, RetValue = -1, SomeActive = 1, ClausesAdded = 1; int nOutDigits = Abc_Base10Log( Saig_ManPoNum(p->pAig) ); abctime clkStart = Abc_Clock(), clkOne = 0; + p->tStart = clkStart; p->timeToStop = p->pPars->nTimeOut ? p->pPars->nTimeOut * CLOCKS_PER_SEC + Abc_Clock(): 0; assert( Vec_PtrSize(p->vSolvers) == 0 ); // in the multi-output mode, mark trivial POs (those fed by const0) as solved - if ( p->pPars->fSolveAll ) - Saig_ManForEachPo( p->pAig, pObj, iFrame ) - if ( Aig_ObjChild0(pObj) == Aig_ManConst0(p->pAig) ) + if ( p->pPars->fSolveAll || p->pPars->fAnytime ) + Saig_ManForEachPo( p->pAig, pObj, i ) + if ( Aig_ObjChild0(pObj) == Aig_ManConst0(p->pAig) && Vec_IntEntry( p->pPars->vOutMap, i ) == -2) { - Vec_IntWriteEntry( p->pPars->vOutMap, iFrame, 1 ); // unsat + Vec_IntWriteEntry( p->pPars->vOutMap, i, 1 ); // unsat + Abc_Print( 1, "Proved output %d in frame 0 (trivial).\n", i ); p->pPars->nProveOuts++; if ( p->pPars->fUseBridge ) - Gia_ManToBridgeResult( stdout, 1, NULL, iFrame ); + Gia_ManToBridgeResult( stdout, 1, NULL, i ); } // create the first timeframe p->pPars->timeLastSolved = Abc_Clock(); Pdr_ManCreateSolver( p, (iFrame = 0) ); + if ( p->vInfCubes != NULL ) + { + Vec_PtrForEachEntry( Pdr_Set_t *, p->vInfCubes, pCube, i ) + { + Pdr_ManSolverAddClause( p, 0, pCube ); + Vec_VecPush( p->vClauses, 0, pCube ); + } + pCube = NULL; + } + while ( 1 ) { int fRefined = 0; @@ -1113,11 +1278,15 @@ int Pdr_ManSolveInt( Pdr_Man_t * p ) p->nFrames = iFrame; assert( iFrame == Vec_PtrSize(p->vSolvers)-1 ); p->iUseFrame = Abc_MaxInt(iFrame, 1); + SomeActive = 0; Saig_ManForEachPo( p->pAig, pObj, p->iOutCur ) { // skip disproved outputs if ( p->vCexes && Vec_PtrEntry(p->vCexes, p->iOutCur) ) continue; + // skip otuput that was already solved + if ( p->pPars->vOutMap && Vec_IntEntry( p->pPars->vOutMap, p->iOutCur ) == 1 ) + continue; // skip output whose time has run out if ( p->pTime4Outs && p->pTime4Outs[p->iOutCur] == 0 ) continue; @@ -1159,6 +1328,7 @@ int Pdr_ManSolveInt( Pdr_Man_t * p ) p->pPars->timeLastSolved = Abc_Clock(); continue; } + SomeActive += 1; // try to solve this output if ( p->pTime4Outs ) { @@ -1203,6 +1373,7 @@ int Pdr_ManSolveInt( Pdr_Man_t * p ) } if ( RetValue == 0 ) { + ClausesAdded = 1; RetValue = Pdr_ManBlockCube( p, pCube ); if ( RetValue == -1 ) { @@ -1276,6 +1447,7 @@ int Pdr_ManSolveInt( Pdr_Man_t * p ) return 0; // all SAT Pdr_QueueClean( p ); pCube = NULL; + SomeActive--; break; // keep solving } if ( p->pPars->fVerbose ) @@ -1289,13 +1461,19 @@ int Pdr_ManSolveInt( Pdr_Man_t * p ) abctime timeSince = Abc_Clock() - clkOne; assert( p->pTime4Outs[p->iOutCur] > 0 ); p->pTime4Outs[p->iOutCur] = (p->pTime4Outs[p->iOutCur] > timeSince) ? p->pTime4Outs[p->iOutCur] - timeSince : 0; - if ( p->pTime4Outs[p->iOutCur] == 0 && Vec_PtrEntry(p->vCexes, p->iOutCur) == NULL ) // undecided + if ( p->pTime4Outs[p->iOutCur] == 0 && (p->vCexes == NULL || Vec_PtrEntry(p->vCexes, p->iOutCur) == NULL) ) // undecided { + SomeActive--; p->pPars->nDropOuts++; if ( p->pPars->vOutMap ) Vec_IntWriteEntry( p->pPars->vOutMap, p->iOutCur, -1 ); if ( !p->pPars->fNotVerbose ) - Abc_Print( 1, "Timing out on output %*d in frame %d.\n", nOutDigits, p->iOutCur, iFrame ); + { + if ( p->pPars->fAnytime ) + Abc_Print( 1, "Timing out on output %*d in frame %d (retrying in next anytime pass).\n", nOutDigits, p->iOutCur, iFrame ); + else + Abc_Print( 1, "Timing out on output %*d in frame %d.\n", nOutDigits, p->iOutCur, iFrame ); + } } p->timeToStopOne = 0; } @@ -1316,6 +1494,10 @@ int Pdr_ManSolveInt( Pdr_Man_t * p ) // open a new timeframe p->nQueLim = p->pPars->nRestLimit; assert( pCube == NULL ); + + if ( p->pPars->fAnytime && ClausesAdded ) + Vec_IntFree( Pdr_ManDeriveInfinityClauses( p, 1 ) ); + Pdr_ManSetPropertyOutput( p, iFrame ); Pdr_ManCreateSolver( p, ++iFrame ); if ( fPrintClauses ) @@ -1324,14 +1506,23 @@ int Pdr_ManSolveInt( Pdr_Man_t * p ) Pdr_ManPrintClauses( p, 0 ); } // push clauses into this timeframe - RetValue = Pdr_ManPushClauses( p ); - if ( RetValue == -1 ) + RetValue = 0; + if ( p->pPars->fAnytime ) + { + RetValue = Pdr_ManPushInfAndRecycledClauses( p ); + ClausesAdded = 0; + } + + RetValue = RetValue == -1 ? -1 : Pdr_ManPushClauses( p ); + if ( RetValue == -1 || !SomeActive ) { if ( p->pPars->fVerbose ) Pdr_ManPrintProgress( p, 1, Abc_Clock() - clkStart ); if ( !p->pPars->fSilent ) { - if ( p->timeToStop && Abc_Clock() > p->timeToStop ) + if ( !SomeActive ) + Abc_Print( 1, "All outputs solved or timed out in frame %d.\n", iFrame ); + else if ( p->timeToStop && Abc_Clock() > p->timeToStop ) Abc_Print( 1, "Reached timeout (%d seconds) in frame %d.\n", p->pPars->nTimeOut, iFrame ); else Abc_Print( 1, "Reached conflict limit (%d) in frame %d.\n", p->pPars->nConfLimit, iFrame ); @@ -1343,22 +1534,34 @@ int Pdr_ManSolveInt( Pdr_Man_t * p ) { if ( p->pPars->fVerbose ) Pdr_ManPrintProgress( p, 1, Abc_Clock() - clkStart ); - if ( !p->pPars->fSilent ) - Pdr_ManReportInvariant( p ); - if ( !p->pPars->fSilent ) - Pdr_ManVerifyInvariant( p ); + if ( !p->pPars->fAnytime ) + { + if ( !p->pPars->fSilent ) + Pdr_ManReportInvariant( p ); + if ( !p->pPars->fSilent ) + Pdr_ManVerifyInvariant( p ); + } p->pPars->iFrame = iFrame; - // count the number of UNSAT outputs - p->pPars->nProveOuts = Saig_ManPoNum(p->pAig) - p->pPars->nFailOuts - p->pPars->nDropOuts; // convert previously 'unknown' into 'unsat' if ( p->pPars->vOutMap ) - for ( iFrame = 0; iFrame < Saig_ManPoNum(p->pAig); iFrame++ ) - if ( Vec_IntEntry(p->pPars->vOutMap, iFrame) == -2 ) // unknown + { + for ( i = 0; i < Saig_ManPoNum(p->pAig); i++ ) + if ( Vec_IntEntry(p->pPars->vOutMap, i) == -2 ) // unknown { - Vec_IntWriteEntry( p->pPars->vOutMap, iFrame, 1 ); // unsat + Vec_IntWriteEntry( p->pPars->vOutMap, i, 1 ); // unsat + Abc_Print( 1, "Proved output %d in frame %d (converged).\n", i ); + p->pPars->nProveOuts++; if ( p->pPars->fUseBridge ) - Gia_ManToBridgeResult( stdout, 1, NULL, iFrame ); + Gia_ManToBridgeResult( stdout, 1, NULL, i ); } + if ( p->pPars->nDropOuts > 0 ) + return -1; + } + else + { + // count the number of UNSAT outputs + p->pPars->nProveOuts = Saig_ManPoNum(p->pAig) - p->pPars->nFailOuts - p->pPars->nDropOuts; + } if ( p->pPars->nProveOuts == Saig_ManPoNum(p->pAig) ) return 1; // UNSAT if ( p->pPars->nFailOuts > 0 ) @@ -1416,6 +1619,61 @@ int Pdr_ManSolveInt( Pdr_Man_t * p ) return -1; } +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Pdr_ManResetReuseInvariant( Pdr_Man_t * p ) +{ + int i, k, nTimeOutU; + Pdr_Set_t * pCla; + sat_solver * pSat; + + Vec_IntFree( Pdr_ManDeriveInfinityClauses( p, 1 ) ); + + Vec_PtrClear( p->vInfCubes ); + Vec_VecForEachEntry( Pdr_Set_t *, p->vClauses, pCla, i, k ) + Vec_PtrPush( p->vInfCubes, pCla ); + + Vec_PtrForEachEntry( sat_solver *, p->vSolvers, pSat, i ) + sat_solver_delete( pSat ); + Vec_PtrFree( p->vSolvers ); + Vec_VecFree( p->vClauses ); + Pdr_QueueStop( p ); + Vec_IntFree( p->vActVars ); + + p->vSolvers = Vec_PtrAlloc( 0 ); + p->vClauses = Vec_VecAlloc( 0 ); + p->pQueue = NULL; + p->vActVars = Vec_IntAlloc( 256 ); + + + Vec_IntFill (p->vPrio, Vec_IntSize(p->vPrio), 0); + p->nAbsFlops = 0; + + for ( i = 0; i < Saig_ManPoNum(p->pAig); i++ ) + { + p->pTime4Outs[i] = p->pPars->nTimeOutOne * CLOCKS_PER_SEC / 1000 + 1; + + if ( Vec_IntEntry(p->pPars->vOutMap, i) == -1 ) // timeout + Vec_IntWriteEntry( p->pPars->vOutMap, i, -2 ); // unknown + } + + p->pPars->nDropOuts = 0; + p->pPars->nTimeOutOne *= 2; + nTimeOutU = p->pPars->nTimeOutOne * (Saig_ManPoNum(p->pAig) - p->pPars->nProveOuts - p->pPars->nFailOuts); + p->pPars->nTimeOut = (nTimeOutU + 999) / 10; // TODO XXX + + Abc_Print( 1, "Starting new anytime pass, reusing clauses.\n" ); +} + /**Function************************************************************* Synopsis [] @@ -1430,12 +1688,21 @@ int Pdr_ManSolveInt( Pdr_Man_t * p ) int Pdr_ManSolve( Aig_Man_t * pAig, Pdr_Par_t * pPars ) { Pdr_Man_t * p; - int k, RetValue; + int k, RetValue, nTimeOutU; abctime clk = Abc_Clock(); - if ( pPars->nTimeOutOne && !pPars->fSolveAll ) + if ( pPars->fAnytime ) + { + if ( pPars->nTimeOutOne == 0 ) + pPars->nTimeOutOne = 200; + } + if ( pPars->nTimeOutOne && !(pPars->fSolveAll || pPars->fAnytime) ) pPars->nTimeOutOne = 0; if ( pPars->nTimeOutOne && pPars->nTimeOut == 0 ) - pPars->nTimeOut = pPars->nTimeOutOne * Saig_ManPoNum(pAig) / 1000 + (int)((pPars->nTimeOutOne * Saig_ManPoNum(pAig) % 1000) > 0); + { + nTimeOutU = pPars->nTimeOutOne * (Saig_ManPoNum(pAig) - pPars->nProveOuts - pPars->nFailOuts); + pPars->nTimeOut = (nTimeOutU + 999) / 10; // TODO XXX + } + if ( pPars->fVerbose ) { // Abc_Print( 1, "Running PDR by Niklas Een (aka IC3 by Aaron Bradley) with these parameters:\n" ); @@ -1451,7 +1718,17 @@ int Pdr_ManSolve( Aig_Man_t * pAig, Pdr_Par_t * pPars ) } ABC_FREE( pAig->pSeqModel ); p = Pdr_ManStart( pAig, pPars, NULL ); - RetValue = Pdr_ManSolveInt( p ); + + while (1) { + + RetValue = Pdr_ManSolveInt( p ); + if ( RetValue == -1 && Saig_ManPoNum(p->pAig) == pPars->nProveOuts + pPars->nFailOuts ) + RetValue = pPars->nFailOuts == 0; + if ( RetValue == -1 && pPars->fAnytime ) + Pdr_ManResetReuseInvariant( p ); + else + break; + } if ( RetValue == 0 ) assert( pAig->pSeqModel != NULL || p->vCexes != NULL ); if ( p->vCexes ) diff --git a/src/proof/pdr/pdrIncr.c b/src/proof/pdr/pdrIncr.c index 6ba68db992..28cf491e4f 100644 --- a/src/proof/pdr/pdrIncr.c +++ b/src/proof/pdr/pdrIncr.c @@ -379,6 +379,7 @@ int IPdr_ManSolveInt( Pdr_Man_t * p, int fCheckClauses, int fPushClauses ) int iFrame, RetValue = -1; int nOutDigits = Abc_Base10Log( Saig_ManPoNum(p->pAig) ); abctime clkStart = Abc_Clock(), clkOne = 0; + p->tStart = clkStart; p->timeToStop = p->pPars->nTimeOut ? p->pPars->nTimeOut * CLOCKS_PER_SEC + Abc_Clock(): 0; // assert( Vec_PtrSize(p->vSolvers) == 0 ); // in the multi-output mode, mark trivial POs (those fed by const0) as solved @@ -678,7 +679,8 @@ int IPdr_ManSolveInt( Pdr_Man_t * p, int fCheckClauses, int fPushClauses ) //if ( p->pPars->fUseAbs && p->vAbsFlops ) // printf( "Finished frame %d with %d (%d) flops.\n", iFrame, Vec_IntCountPositive(p->vAbsFlops), Vec_IntCountPositive(p->vPrio) ); // open a new timeframe - p->nQueLim = p->pPars->nRestLimit; + p->nQueLim = iFrame + p->pPars->nRestLimit; + p->nQueLimStep = 1; assert( pCube == NULL ); Pdr_ManSetPropertyOutput( p, iFrame ); Pdr_ManCreateSolver( p, ++iFrame ); diff --git a/src/proof/pdr/pdrInt.h b/src/proof/pdr/pdrInt.h index 4a96b071b1..b1375e1bea 100644 --- a/src/proof/pdr/pdrInt.h +++ b/src/proof/pdr/pdrInt.h @@ -58,6 +58,8 @@ #define sat_solver_compress(s) #endif +#define PDR_INF_BOUND ((int)((~((unsigned int)0)) >> 1)) + ABC_NAMESPACE_HEADER_START //////////////////////////////////////////////////////////////////////// @@ -76,6 +78,7 @@ struct Pdr_Set_t_ { word Sign; // signature int nRefs; // ref counter + int iBound; // known to hold up to this frame, INT_MAX = inf int nTotal; // total literals int nLits; // num flop literals int Lits[0]; @@ -156,8 +159,11 @@ struct Pdr_Man_t_ int nQueCur; int nQueMax; int nQueLim; + int nQueLimStep; int nXsimRuns; int nXsimLits; + int nInfClauses; + int fNewInfClauses; // runtime abctime timeToStop; abctime timeToStopOne; @@ -172,6 +178,7 @@ struct Pdr_Man_t_ abctime tCnf; abctime tAbs; abctime tTotal; + abctime tStart; }; //////////////////////////////////////////////////////////////////////// @@ -253,6 +260,7 @@ extern void Pdr_SetPrint( FILE * pFile, Pdr_Set_t * p, int nRegs, Vec extern void ZPdr_SetPrint( Pdr_Set_t * p ); extern void Pdr_SetPrintStr( Vec_Str_t * vStr, Pdr_Set_t * p, int nRegs, Vec_Int_t * vFlopCounts ); extern int Pdr_SetCompare( Pdr_Set_t ** pp1, Pdr_Set_t ** pp2 ); +extern int Pdr_SetBoundSizeLextCompare( Pdr_Set_t ** pp1, Pdr_Set_t ** pp2 ); extern Pdr_Obl_t * Pdr_OblStart( int k, int prio, Pdr_Set_t * pState, Pdr_Obl_t * pNext ); extern Pdr_Obl_t * Pdr_OblRef( Pdr_Obl_t * p ); extern void Pdr_OblDeref( Pdr_Obl_t * p ); diff --git a/src/proof/pdr/pdrInv.c b/src/proof/pdr/pdrInv.c index ae61ce2c06..d1ae5d9d11 100644 --- a/src/proof/pdr/pdrInv.c +++ b/src/proof/pdr/pdrInv.c @@ -48,14 +48,15 @@ ABC_NAMESPACE_IMPL_START void Pdr_ManPrintProgress( Pdr_Man_t * p, int fClose, abctime Time ) { Vec_Ptr_t * vVec; - int i, ThisSize, Length, LengthStart; + int i, ThisSize, Length, LengthStart, kLast, Value, Width; if ( Vec_PtrSize(p->vSolvers) < 2 ) { Abc_Print(1, "Frame " ); Abc_Print(1, "Clauses " ); Abc_Print(1, "Max Queue " ); Abc_Print(1, "Flops " ); - Abc_Print(1, "Cex " ); + if ( p->pPars->fUseAbs ) + Abc_Print(1, "Cex " ); Abc_Print(1, "Time" ); Abc_Print(1, "\n" ); return; @@ -64,8 +65,12 @@ void Pdr_ManPrintProgress( Pdr_Man_t * p, int fClose, abctime Time ) return; // count the total length of the printout Length = 0; + kLast = Vec_VecSize( p->vClauses ) - 1; + Vec_VecForEachLevel( p->vClauses, vVec, i ) - Length += 1 + Abc_Base10Log(Vec_PtrSize(vVec)+1); + Length += 1 + Abc_Base10Log(Vec_PtrSize(vVec)+1 - (i == kLast ? p->nInfClauses : 0) ); + if ( p->vInfCubes != NULL && Vec_PtrSize(p->vInfCubes) > 0 ) + Length += 2 + Abc_Base10Log(Vec_PtrSize(p->vInfCubes)+1); // determine the starting point LengthStart = Abc_MaxInt( 0, Length - 60 ); Abc_Print( 1, "%3d :", Vec_PtrSize(p->vSolvers)-1 ); @@ -78,26 +83,37 @@ void Pdr_ManPrintProgress( Pdr_Man_t * p, int fClose, abctime Time ) Length = 0; Vec_VecForEachLevel( p->vClauses, vVec, i ) { + Value = Vec_PtrSize(vVec) - (i == kLast ? p->nInfClauses : 0); + Width = 1 + Abc_Base10Log(Value+1); if ( Length < LengthStart ) { - Length += 1 + Abc_Base10Log(Vec_PtrSize(vVec)+1); + Length += Width; continue; } - Abc_Print( 1, " %d", Vec_PtrSize(vVec) ); - Length += 1 + Abc_Base10Log(Vec_PtrSize(vVec)+1); - ThisSize += 1 + Abc_Base10Log(Vec_PtrSize(vVec)+1); + Abc_Print( 1, " %d", Value ); + Length += Width; + ThisSize += Width; + } + if ( p->vInfCubes != NULL && Vec_PtrSize(p->vInfCubes) > 0 ) + { + Abc_Print( 1, " ~%d", Vec_PtrSize(p->vInfCubes) ); + Length += 1 + Abc_Base10Log(Vec_PtrSize(p->vInfCubes)+2); + ThisSize += 1 + Abc_Base10Log(Vec_PtrSize(p->vInfCubes)+2); } for ( i = ThisSize; i < 70; i++ ) Abc_Print( 1, " " ); Abc_Print( 1, "%5d", p->nQueMax ); Abc_Print( 1, "%6d", p->vAbsFlops ? Vec_IntCountPositive(p->vAbsFlops) : p->nAbsFlops ); if ( p->pPars->fUseAbs ) - Abc_Print( 1, "%4d", p->nCexes ); + Abc_Print( 1, "%4d", p->nCexes ); Abc_Print( 1, "%10.2f sec", 1.0*Time/CLOCKS_PER_SEC ); if ( p->pPars->fSolveAll ) Abc_Print( 1, " CEX =%4d", p->pPars->nFailOuts ); if ( p->pPars->nTimeOutOne ) Abc_Print( 1, " T/O =%3d", p->pPars->nDropOuts ); + if ( p->pPars->fAnytime ) + Abc_Print( 1, " PRV =%3d", p->pPars->nProveOuts ); + Abc_Print( 1, "%s", fClose ? "\n":"\r" ); if ( fClose ) p->nQueMax = 0, p->nCexes = 0; @@ -123,7 +139,7 @@ Vec_Int_t * Pdr_ManCountFlops( Pdr_Man_t * p, Vec_Ptr_t * vCubes ) vFlopCount = Vec_IntStart( Aig_ManRegNum(p->pAig) ); Vec_PtrForEachEntry( Pdr_Set_t *, vCubes, pCube, i ) { - if ( pCube->nRefs == -1 ) + if ( pCube->iBound != PDR_INF_BOUND ) continue; for ( n = 0; n < pCube->nLits; n++ ) { @@ -376,7 +392,7 @@ void Pdr_ManDumpClauses( Pdr_Man_t * p, char * pFileName, int fProved ) Count = 0; Vec_PtrForEachEntry( Pdr_Set_t *, vCubes, pCube, i ) { - if ( pCube->nRefs == -1 ) + if ( pCube->iBound != PDR_INF_BOUND ) continue; Count++; } @@ -406,7 +422,7 @@ void Pdr_ManDumpClauses( Pdr_Man_t * p, char * pFileName, int fProved ) // output cubes Vec_PtrForEachEntry( Pdr_Set_t *, vCubes, pCube, i ) { - if ( pCube->nRefs == -1 ) + if ( pCube->iBound != PDR_INF_BOUND ) continue; Pdr_SetPrint( pFile, pCube, Aig_ManRegNum(p->pAig), vFlopCounts ); fprintf( pFile, " 1\n" ); @@ -452,7 +468,7 @@ Vec_Str_t * Pdr_ManDumpString( Pdr_Man_t * p ) // output cubes Vec_PtrForEachEntry( Pdr_Set_t *, vCubes, pCube, i ) { - if ( pCube->nRefs == -1 ) + if ( pCube->iBound != PDR_INF_BOUND ) continue; Pdr_SetPrintStr( vStr, pCube, Aig_ManRegNum(p->pAig), vFlopCounts ); } @@ -566,7 +582,7 @@ int Pdr_ManDeriveMarkNonInductive( Pdr_Man_t * p, Vec_Ptr_t * vCubes ) // add the clauses Vec_PtrForEachEntry( Pdr_Set_t *, vCubes, pCube, i ) { - if ( pCube->nRefs == -1 ) // skip non-inductive + if ( pCube->iBound >= 0 && pCube->iBound != PDR_INF_BOUND ) // skip known non-inf continue; vLits = Pdr_ManCubeToLits( p, kThis, pCube, 1, 0 ); RetValue = sat_solver_addclause( pSat, Vec_IntArray(vLits), Vec_IntArray(vLits) + Vec_IntSize(vLits) ); @@ -576,31 +592,45 @@ int Pdr_ManDeriveMarkNonInductive( Pdr_Man_t * p, Vec_Ptr_t * vCubes ) // check each clause Vec_PtrForEachEntry( Pdr_Set_t *, vCubes, pCube, i ) { - if ( pCube->nRefs == -1 ) // skip non-inductive + if ( pCube->iBound >= 0 ) // skip clauses with known inf-ness continue; vLits = Pdr_ManCubeToLits( p, kThis, pCube, 0, 1 ); RetValue = sat_solver_solve( pSat, Vec_IntArray(vLits), Vec_IntArray(vLits) + Vec_IntSize(vLits), 0, 0, 0, 0 ); - if ( RetValue != l_False ) // mark as non-inductive + if ( RetValue != l_False ) // mark as non-inf { - pCube->nRefs = -1; + pCube->iBound = ~pCube->iBound; fChanges = 1; } else Counter++; } + + + Vec_Ptr_t *vLevel; + sat_solver_delete( (sat_solver *)Vec_PtrPop( p->vSolvers ) ); + vLevel = (Vec_Ptr_t *)Vec_PtrPop( (Vec_Ptr_t *)p->vClauses ); + assert (Vec_PtrSize( vLevel ) == 0); + Vec_PtrFree( vLevel ); + Vec_IntPop( p->vActVars ); //Abc_Print(1, "Clauses = %d.\n", Counter ); //sat_solver_delete( pSat ); return fChanges; } + Vec_Int_t * Pdr_ManDeriveInfinityClauses( Pdr_Man_t * p, int fReduce ) { Vec_Int_t * vResult; Vec_Ptr_t * vCubes; Pdr_Set_t * pCube; - int i, v, kStart; + int i, v, kStart, kMax; // collect cubes used in the inductive invariant + kMax = Vec_PtrSize( p->vSolvers ); kStart = Pdr_ManFindInvariantStart( p ); vCubes = Pdr_ManCollectCubes( p, kStart ); + // mark all non-inf clauses as candidates + Vec_PtrForEachEntry( Pdr_Set_t *, vCubes, pCube, i ) + if (pCube->iBound != PDR_INF_BOUND) + pCube->iBound = ~pCube->iBound; // refine as long as there are changes if ( fReduce ) while ( Pdr_ManDeriveMarkNonInductive(p, vCubes) ); @@ -609,14 +639,23 @@ Vec_Int_t * Pdr_ManDeriveInfinityClauses( Pdr_Man_t * p, int fReduce ) Vec_IntPush( vResult, 0 ); Vec_PtrForEachEntry( Pdr_Set_t *, vCubes, pCube, i ) { - if ( pCube->nRefs == -1 ) // skip non-inductive + if ( pCube->iBound >= 0 && pCube->iBound != PDR_INF_BOUND ) { + Vec_PtrWriteEntry( vCubes, i, Vec_PtrEntryLast( vCubes ) ); + Vec_PtrPop( vCubes ); + i--; continue; + } + if (pCube->iBound != PDR_INF_BOUND) + { + p->nInfClauses++; + p->fNewInfClauses = 1; + pCube->iBound = PDR_INF_BOUND; + } Vec_IntAddToEntry( vResult, 0, 1 ); Vec_IntPush( vResult, pCube->nLits ); for ( v = 0; v < pCube->nLits; v++ ) Vec_IntPush( vResult, pCube->Lits[v] ); } - //Vec_PtrFree( vCubes ); Vec_PtrFreeP( &p->vInfCubes ); p->vInfCubes = vCubes; Vec_IntPush( vResult, Aig_ManRegNum(p->pAig) ); diff --git a/src/proof/pdr/pdrMan.c b/src/proof/pdr/pdrMan.c index 7686ec03b1..052dd731eb 100644 --- a/src/proof/pdr/pdrMan.c +++ b/src/proof/pdr/pdrMan.c @@ -296,8 +296,9 @@ Pdr_Man_t * Pdr_ManStart( Aig_Man_t * pAig, Pdr_Par_t * pPars, Vec_Int_t * vPrio p->pTime4Outs[i] = pPars->nTimeOutOne * CLOCKS_PER_SEC / 1000 + 1; } if ( pPars->fSolveAll ) - { p->vCexes = Vec_PtrStart( Saig_ManPoNum(p->pAig) ); + if ( pPars->fSolveAll || p->pPars->fAnytime ) + { p->pPars->vOutMap = Vec_IntAlloc( Saig_ManPoNum(pAig) ); Vec_IntFill( p->pPars->vOutMap, Saig_ManPoNum(pAig), -2 ); } diff --git a/src/proof/pdr/pdrSat.c b/src/proof/pdr/pdrSat.c index b1a5b66c45..becf713fd8 100644 --- a/src/proof/pdr/pdrSat.c +++ b/src/proof/pdr/pdrSat.c @@ -99,7 +99,8 @@ sat_solver * Pdr_ManFetchSolver( Pdr_Man_t * p, int k ) // add the clauses Vec_VecForEachLevelStart( p->vClauses, vArrayK, i, k ) Vec_PtrForEachEntry( Pdr_Set_t *, vArrayK, pCube, j ) - Pdr_ManSolverAddClause( p, k, pCube ); + if ( pCube != NULL ) + Pdr_ManSolverAddClause( p, k, pCube ); return pSat; } diff --git a/src/proof/pdr/pdrUtil.c b/src/proof/pdr/pdrUtil.c index b2eaa2c767..e4b9fd18d9 100644 --- a/src/proof/pdr/pdrUtil.c +++ b/src/proof/pdr/pdrUtil.c @@ -71,6 +71,7 @@ Pdr_Set_t * Pdr_SetCreate( Vec_Int_t * vLits, Vec_Int_t * vPiLits ) p->nLits = Vec_IntSize(vLits); p->nTotal = Vec_IntSize(vLits) + Vec_IntSize(vPiLits); p->nRefs = 1; + p->iBound = 0; p->Sign = 0; for ( i = 0; i < p->nLits; i++ ) { @@ -104,6 +105,7 @@ Pdr_Set_t * Pdr_SetCreateFrom( Pdr_Set_t * pSet, int iRemove ) p->nLits = pSet->nLits - 1; p->nTotal = pSet->nTotal - 1; p->nRefs = 1; + p->iBound = 0; p->Sign = 0; for ( i = 0; i < pSet->nTotal; i++ ) { @@ -138,6 +140,7 @@ Pdr_Set_t * Pdr_SetCreateSubset( Pdr_Set_t * pSet, int * pLits, int nLits ) p->nLits = nLits; p->nTotal = nLits + pSet->nTotal - pSet->nLits; p->nRefs = 1; + p->iBound = 0; p->Sign = 0; for ( i = 0; i < nLits; i++ ) { @@ -171,6 +174,7 @@ Pdr_Set_t * Pdr_SetDup( Pdr_Set_t * pSet ) p->nLits = pSet->nLits; p->nTotal = pSet->nTotal; p->nRefs = 1; + p->iBound = 0; p->Sign = pSet->Sign; for ( i = 0; i < pSet->nTotal; i++ ) p->Lits[i] = pSet->Lits[i]; @@ -501,6 +505,41 @@ int Pdr_SetCompare( Pdr_Set_t ** pp1, Pdr_Set_t ** pp2 ) return 0; } +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Pdr_SetBoundSizeLextCompare( Pdr_Set_t ** pp1, Pdr_Set_t ** pp2 ) +{ + Pdr_Set_t * p1 = *pp1; + Pdr_Set_t * p2 = *pp2; + if (p1->iBound > p2->iBound) + return -1; + if (p1->iBound < p2->iBound) + return 1; + if (p1->nLits < p2->nLits) + return -1; + if (p1->nLits > p2->nLits) + return 1; + + int i; + for ( i = 0; i < p1->nLits && i < p2->nLits; i++ ) + { + if ( p1->Lits[i] > p2->Lits[i] ) + return -1; + if ( p1->Lits[i] < p2->Lits[i] ) + return 1; + } + return 0; +} + /**Function************************************************************* Synopsis [] From ecce27ce1d8d8ff1f5ccd89be65700f0da98eaf4 Mon Sep 17 00:00:00 2001 From: Jason Thorpe Date: Sun, 3 Mar 2024 08:02:41 -0800 Subject: [PATCH 50/79] Skip -ldl on NetBSD; it does not exist. Skip -lrt on NetBSD; it is not required. Same treatment as FreeBSD. --- Makefile | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Makefile b/Makefile index 032f3a8d6e..8b38e71537 100644 --- a/Makefile +++ b/Makefile @@ -137,11 +137,11 @@ endif # LIBS := -ldl -lrt LIBS += -lm -ifneq ($(OS), $(filter $(OS), FreeBSD OpenBSD)) +ifneq ($(OS), $(filter $(OS), FreeBSD OpenBSD NetBSD)) LIBS += -ldl endif -ifneq ($(OS), $(filter $(OS), FreeBSD OpenBSD Darwin)) +ifneq ($(OS), $(filter $(OS), FreeBSD OpenBSD NetBSD Darwin)) LIBS += -lrt endif From b83985c25bf7845986f88335178068f99d500fe3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20Povi=C5=A1er?= Date: Fri, 22 Mar 2024 21:42:12 +0100 Subject: [PATCH 51/79] Add `&mfs -r` for re-import testing --- src/aig/gia/giaMfs.c | 3 +++ src/base/abci/abc.c | 6 +++++- src/opt/sfm/sfm.h | 1 + src/opt/sfm/sfmCore.c | 1 + 4 files changed, 10 insertions(+), 1 deletion(-) diff --git a/src/aig/gia/giaMfs.c b/src/aig/gia/giaMfs.c index a0e6caed4f..b1e657f735 100644 --- a/src/aig/gia/giaMfs.c +++ b/src/aig/gia/giaMfs.c @@ -517,6 +517,8 @@ Gia_Man_t * Gia_ManPerformMfs( Gia_Man_t * p, Sfm_Par_t * pPars ) } // collect information pNtk = Gia_ManExtractMfs( p ); + if (pPars->fTestReimport) + goto reimport; // perform optimization nNodes = Sfm_NtkPerform( pNtk, pPars ); if ( nNodes == 0 ) @@ -529,6 +531,7 @@ Gia_Man_t * Gia_ManPerformMfs( Gia_Man_t * p, Sfm_Par_t * pPars ) } else { + reimport: pNew = Gia_ManInsertMfs( p, pNtk, pPars->fAllBoxes ); if( pPars->fVerbose ) Abc_Print( 1, "The network has %d nodes changed by \"&mfs\".\n", nNodes ); diff --git a/src/base/abci/abc.c b/src/base/abci/abc.c index cf5b341f31..f0fa728c2f 100644 --- a/src/base/abci/abc.c +++ b/src/base/abci/abc.c @@ -49096,7 +49096,7 @@ int Abc_CommandAbc9Mfs( Abc_Frame_t * pAbc, int argc, char ** argv ) pPars->nDepthMax = 100; pPars->nWinSizeMax = 2000; Extra_UtilGetoptReset(); - while ( ( c = Extra_UtilGetopt( argc, argv, "WFDMLCNdaeblvwh" ) ) != EOF ) + while ( ( c = Extra_UtilGetopt( argc, argv, "WFDMLCNdaeblvwhr" ) ) != EOF ) { switch ( c ) { @@ -49192,6 +49192,9 @@ int Abc_CommandAbc9Mfs( Abc_Frame_t * pAbc, int argc, char ** argv ) case 'l': pPars->fUseDcs ^= 1; break; + case 'r': + pPars->fTestReimport ^= 1; + break; case 'v': pPars->fVerbose ^= 1; break; @@ -49261,6 +49264,7 @@ int Abc_CommandAbc9Mfs( Abc_Frame_t * pAbc, int argc, char ** argv ) Abc_Print( -2, "\t-l : toggle deriving don't-cares [default = %s]\n", pPars->fUseDcs? "yes": "no" ); Abc_Print( -2, "\t-v : toggle printing optimization summary [default = %s]\n", pPars->fVerbose? "yes": "no" ); Abc_Print( -2, "\t-w : toggle printing detailed stats for each node [default = %s]\n", pPars->fVeryVerbose? "yes": "no" ); + Abc_Print( -2, "\t-r : toggle testing re-importing the network unchanged [default = %s]\n", pPars->fTestReimport? "yes": "no" ); Abc_Print( -2, "\t-h : print the command usage\n"); return 1; } diff --git a/src/opt/sfm/sfm.h b/src/opt/sfm/sfm.h index f9c95c83eb..9cdf1bc670 100644 --- a/src/opt/sfm/sfm.h +++ b/src/opt/sfm/sfm.h @@ -73,6 +73,7 @@ struct Sfm_Par_t_ int fDelayVerbose; // enable delay stats int fVerbose; // enable basic stats int fVeryVerbose; // enable detailed stats + int fTestReimport; // enable testing of re-import }; //////////////////////////////////////////////////////////////////////// diff --git a/src/opt/sfm/sfmCore.c b/src/opt/sfm/sfmCore.c index f363e1cb0e..c0dc70dd50 100644 --- a/src/opt/sfm/sfmCore.c +++ b/src/opt/sfm/sfmCore.c @@ -58,6 +58,7 @@ void Sfm_ParSetDefault( Sfm_Par_t * pPars ) pPars->fAllBoxes = 0; // enable preserving all boxes pPars->fVerbose = 0; // enable basic stats pPars->fVeryVerbose = 0; // enable detailed stats + pPars->fTestReimport = 0; // enable testing of re-import } /**Function************************************************************* From fda490235eace499ccee78501d0fd1c268e64b52 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20Povi=C5=A1er?= Date: Fri, 22 Mar 2024 22:01:08 +0100 Subject: [PATCH 52/79] &mfs: Fix issues with traversal when re-importing network The former implementation of `Sfm_NtkDfs` was trying to serialize the network while ordering all box inputs ahead of the box outputs. This is sometimes impossible, leaving the result unordered, which led to crashes in the `&mfs` code when it was reintegrating the result into the GIA structure: ABC: Assertion failed: iLitNew >= 0 (src/aig/gia/giaMfs.c: Gia_ManInsertMfs: 388) With a small change to `Gia_ManInsertMfs` which does the reintegration we don't really need the ordering to see through boxes, ordering on the paths between boxes is sufficient. Relaxing the ordering requirement, we make `Sfm_NtkDfs` robust. --- src/aig/gia/giaMfs.c | 15 ++++++++---- src/opt/sfm/sfmWin.c | 56 ++++++++++++++++++++++++++++++-------------- 2 files changed, 48 insertions(+), 23 deletions(-) diff --git a/src/aig/gia/giaMfs.c b/src/aig/gia/giaMfs.c index b1e657f735..e5d7c37eea 100644 --- a/src/aig/gia/giaMfs.c +++ b/src/aig/gia/giaMfs.c @@ -381,6 +381,15 @@ Gia_Man_t * Gia_ManInsertMfs( Gia_Man_t * p, Sfm_Ntk_t * pNtk, int fAllBoxes ) continue; } Vec_IntClear( vLeaves ); + + // handle internal CIs first, before we go looking for vLeaves + if ( iGroup != -1 && Abc_LitIsCompl(iGroup) ) + { + //Dau_DsdPrintFromTruth( pTruth, Vec_IntSize(vLeaves) ); + iLitNew = Gia_ManAppendCi( pNew ); + goto done; + } + Vec_IntForEachEntry( vArray, Fanin, k ) { iLitNew = Vec_IntEntry( vMfs2Gia, Fanin ); assert( iLitNew >= 0 ); @@ -405,17 +414,13 @@ Gia_Man_t * Gia_ManInsertMfs( Gia_Man_t * p, Sfm_Ntk_t * pNtk, int fAllBoxes ) else iLitNew = Gia_ManFromIfLogicCreateLut( pNew, pTruth, vLeaves, vCover, vMapping, vMapping2 ); } - else if ( Abc_LitIsCompl(iGroup) ) // internal CI - { - //Dau_DsdPrintFromTruth( pTruth, Vec_IntSize(vLeaves) ); - iLitNew = Gia_ManAppendCi( pNew ); - } else // internal CO { assert( pTruth[0] == uTruthVar || pTruth[0] == ~uTruthVar ); iLitNew = Gia_ManAppendCo( pNew, Abc_LitNotCond(Vec_IntEntry(vLeaves, 0), pTruth[0] == ~uTruthVar) ); //printf("Group = %d. po = %d\n", iGroup>>1, iMfsId ); } +done: Vec_IntWriteEntry( vMfs2Gia, iMfsId, iLitNew ); } Vec_IntFree( vCover ); diff --git a/src/opt/sfm/sfmWin.c b/src/opt/sfm/sfmWin.c index 53f9a71efe..2e1681991a 100644 --- a/src/opt/sfm/sfmWin.c +++ b/src/opt/sfm/sfmWin.c @@ -142,18 +142,19 @@ void Sfm_NtkDfs_rec( Sfm_Ntk_t * p, int iNode, Vec_Int_t * vNodes, Vec_Wec_t * v return; if ( Vec_IntEntry(vGroupMap, iNode) >= 0 ) { - int k, iGroup = Abc_Lit2Var( Vec_IntEntry(vGroupMap, iNode) ); - Vec_Int_t * vGroup = Vec_WecEntry( vGroups, iGroup ); - Vec_IntForEachEntry( vGroup, iNode, i ) - assert( Sfm_ObjIsNode(p, iNode) ); - Vec_IntForEachEntry( vGroup, iNode, i ) - Sfm_ObjSetTravIdCurrent( p, iNode ); - Vec_IntForEachEntry( vGroup, iNode, i ) - Sfm_ObjForEachFanin( p, iNode, iFanin, k ) - Sfm_NtkDfs_rec( p, iFanin, vNodes, vGroups, vGroupMap, vBoxesLeft ); - Vec_IntForEachEntry( vGroup, iNode, i ) - Vec_IntPush( vNodes, iNode ); - Vec_IntPush( vBoxesLeft, iGroup ); + int iGroup = Abc_Lit2Var(Vec_IntEntry(vGroupMap, iNode)); + Vec_Int_t * vGroup = Vec_WecEntry(vGroups, iGroup); + assert(Abc_LitIsCompl(Vec_IntEntry(vGroupMap, iNode))); + Vec_IntPush(vBoxesLeft, iGroup); + Vec_IntForEachEntry(vGroup, iNode, i) + { + // ignore inputs + if ( !Abc_LitIsCompl(Vec_IntEntry(vGroupMap, iNode))) + continue; + assert(Sfm_ObjIsNode(p, iNode)); + Sfm_ObjSetTravIdCurrent(p, iNode); + Vec_IntPush(vNodes, iNode); + } } else { @@ -170,14 +171,33 @@ Vec_Int_t * Sfm_NtkDfs( Sfm_Ntk_t * p, Vec_Wec_t * vGroups, Vec_Int_t * vGroupMa Vec_IntClear( vBoxesLeft ); vNodes = Vec_IntAlloc( p->nObjs ); Sfm_NtkIncrementTravId( p ); - if ( fAllBoxes ) + + assert(!fAllBoxes); // TODO + + Sfm_NtkForEachPo( p, i ) { + int iFanin = Sfm_ObjFanin(p, i, 0); + // detect fake PO modeling blackbox input + if (Vec_IntEntry(vGroupMap, iFanin) >= 0 && !Abc_LitIsCompl(Vec_IntEntry(vGroupMap, iFanin))) + continue; + Sfm_NtkDfs_rec( p, iFanin, vNodes, vGroups, vGroupMap, vBoxesLeft ); + } + + for (i = 0; i < Vec_IntSize( vBoxesLeft ); i++) { - Vec_Int_t * vGroup; - Vec_WecForEachLevel( vGroups, vGroup, i ) - Sfm_NtkDfs_rec( p, Vec_IntEntry(vGroup, 0), vNodes, vGroups, vGroupMap, vBoxesLeft ); + int k, j, iNode, iFanin; + int iGroup = Vec_IntEntry( vBoxesLeft, i ); + Vec_Int_t * vGroup = Vec_WecEntry( vGroups, iGroup ); + Vec_IntForEachEntry( vGroup, iNode, k ) + { + // ignore outputs + if ( Abc_LitIsCompl( Vec_IntEntry(vGroupMap, iNode)) ) + continue; + Sfm_ObjForEachFanin( p, iNode, iFanin, j ) + Sfm_NtkDfs_rec( p, iFanin, vNodes, vGroups, vGroupMap, vBoxesLeft ); + Vec_IntPush( vNodes, iNode ); + } } - Sfm_NtkForEachPo( p, i ) - Sfm_NtkDfs_rec( p, Sfm_ObjFanin(p, i, 0), vNodes, vGroups, vGroupMap, vBoxesLeft ); + return vNodes; } From d7fc8fe98f99febca56b2ca8118d71fc0cde5793 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20Povi=C5=A1er?= Date: Fri, 22 Mar 2024 22:42:43 +0100 Subject: [PATCH 53/79] &mfs: Handle blackboxes robustly When the network is being handed over to the "sfm" core, all blackboxes are modeled by inserting new PIs, POs, and those being connected by buffers to the nodes representing the CIs, COs. Make two changes: * Robustly deny the fake PIs from being considered when shopping for LUT fanin substitutions. Such reconnection occurring would trip up the code reintegrating the result. * Make sure the buffer connecting the fake-PO to the CO doesn't get rewritten as part of the `mfs` transformation, and extend this protection to any whitebox models. --- src/aig/gia/giaMfs.c | 13 +++++++++++-- src/base/abci/abcMfs.c | 4 ++-- src/opt/sfm/sfm.h | 2 +- src/opt/sfm/sfmCore.c | 12 ++++++------ src/opt/sfm/sfmInt.h | 2 ++ src/opt/sfm/sfmNtk.c | 6 ++++-- 6 files changed, 26 insertions(+), 13 deletions(-) diff --git a/src/aig/gia/giaMfs.c b/src/aig/gia/giaMfs.c index e5d7c37eea..91241aaff6 100644 --- a/src/aig/gia/giaMfs.c +++ b/src/aig/gia/giaMfs.c @@ -58,6 +58,7 @@ Sfm_Ntk_t * Gia_ManExtractMfs( Gia_Man_t * p ) Vec_Wec_t * vFanins; // mfs data Vec_Str_t * vFixed; // mfs data Vec_Str_t * vEmpty; // mfs data + Vec_Str_t * vDenied; // mfs data Vec_Wrd_t * vTruths; // mfs data Vec_Int_t * vArray; Vec_Int_t * vStarts; @@ -81,9 +82,14 @@ Sfm_Ntk_t * Gia_ManExtractMfs( Gia_Man_t * p ) vFanins = Vec_WecStart( nMfsVars ); vFixed = Vec_StrStart( nMfsVars ); vEmpty = Vec_StrStart( nMfsVars ); + vDenied = Vec_StrStart( nMfsVars ); vTruths = Vec_WrdStart( nMfsVars ); vStarts = Vec_IntStart( nMfsVars ); vTruths2 = Vec_WrdAlloc( 10000 ); + // deny the PIs which are modeling blackbox outputs from being considered + // in substitutions + for (int i = 0; i < nBbOuts; i++) + Vec_StrWriteEntry( vDenied, i, (char)1 ); // set internal PIs Gia_ManCleanCopyArray( p ); Gia_ManForEachCiId( p, Id, i ) @@ -232,6 +238,7 @@ Sfm_Ntk_t * Gia_ManExtractMfs( Gia_Man_t * p ) Vec_IntFill( vArray, 1, iBbOut++ ); Vec_StrWriteEntry( vFixed, Counter, (char)1 ); Vec_StrWriteEntry( vEmpty, Counter, (char)1 ); + Vec_StrWriteEntry( vDenied, Counter, (char)1 ); Vec_WrdWriteEntry( vTruths, Counter, uTruths6[0] ); } for ( i = 0; i < nBoxIns; i++ ) @@ -240,7 +247,9 @@ Sfm_Ntk_t * Gia_ManExtractMfs( Gia_Man_t * p ) pObj = Gia_ManCo( p, curCo + i ); Counter = Gia_ObjCopyArray( p, Gia_ObjId(p, pObj) ); // connect it with the special primary output (iBbIn) - vArray = Vec_WecEntry( vFanins, nMfsVars - nBbIns + iBbIn++ ); + int poNum = nMfsVars - nBbIns + iBbIn++; + vArray = Vec_WecEntry( vFanins, poNum ); + Vec_StrWriteEntry( vFixed, poNum, (char)1 ); assert( Vec_IntSize(vArray) == 0 ); Vec_IntFill( vArray, 1, Counter ); } @@ -268,7 +277,7 @@ Sfm_Ntk_t * Gia_ManExtractMfs( Gia_Man_t * p ) } // finalize Vec_IntFree( vLeaves ); - return Sfm_NtkConstruct( vFanins, nBbOuts + nRealPis, nRealPos + nBbIns, vFixed, vEmpty, vTruths, vStarts, vTruths2 ); + return Sfm_NtkConstruct( vFanins, nBbOuts + nRealPis, nRealPos + nBbIns, vFixed, vEmpty, vDenied, vTruths, vStarts, vTruths2 ); } /**Function************************************************************* diff --git a/src/base/abci/abcMfs.c b/src/base/abci/abcMfs.c index 0533d189f9..69c04c2c3b 100644 --- a/src/base/abci/abcMfs.c +++ b/src/base/abci/abcMfs.c @@ -216,7 +216,7 @@ Sfm_Ntk_t * Abc_NtkExtractMfs( Abc_Ntk_t * pNtk, int nFirstFixed ) // for ( i = Abc_NtkCiNum(pNtk); i + Abc_NtkCoNum(pNtk) < Abc_NtkObjNum(pNtk); i++ ) // if ( rand() % 10 == 0 ) // Vec_StrWriteEntry( vFixed, i, (char)1 ); - return Sfm_NtkConstruct( vFanins, Abc_NtkCiNum(pNtk), Abc_NtkCoNum(pNtk), vFixed, NULL, vTruths, vStarts, vTruths2 ); + return Sfm_NtkConstruct( vFanins, Abc_NtkCiNum(pNtk), Abc_NtkCoNum(pNtk), vFixed, NULL, NULL, vTruths, vStarts, vTruths2 ); } Sfm_Ntk_t * Abc_NtkExtractMfs2( Abc_Ntk_t * pNtk, int iPivot ) { @@ -285,7 +285,7 @@ Sfm_Ntk_t * Abc_NtkExtractMfs2( Abc_Ntk_t * pNtk, int iPivot ) Abc_NtkForEachNode( pNtk, pObj, i ) if ( i >= iPivot ) Vec_StrWriteEntry( vFixed, pObj->iTemp, (char)1 ); - return Sfm_NtkConstruct( vFanins, Abc_NtkCiNum(pNtk), Abc_NtkCoNum(pNtk), vFixed, NULL, vTruths, vStarts, vTruths2 ); + return Sfm_NtkConstruct( vFanins, Abc_NtkCiNum(pNtk), Abc_NtkCoNum(pNtk), vFixed, NULL, NULL, vTruths, vStarts, vTruths2 ); } /**Function************************************************************* diff --git a/src/opt/sfm/sfm.h b/src/opt/sfm/sfm.h index 9cdf1bc670..5fd564fce9 100644 --- a/src/opt/sfm/sfm.h +++ b/src/opt/sfm/sfm.h @@ -89,7 +89,7 @@ struct Sfm_Par_t_ extern void Sfm_ParSetDefault( Sfm_Par_t * pPars ); extern int Sfm_NtkPerform( Sfm_Ntk_t * p, Sfm_Par_t * pPars ); /*=== sfmNtk.c ==========================================================*/ -extern Sfm_Ntk_t * Sfm_NtkConstruct( Vec_Wec_t * vFanins, int nPis, int nPos, Vec_Str_t * vFixed, Vec_Str_t * vEmpty, Vec_Wrd_t * vTruths, Vec_Int_t * vStarts, Vec_Wrd_t * vTruths2 ); +extern Sfm_Ntk_t * Sfm_NtkConstruct( Vec_Wec_t * vFanins, int nPis, int nPos, Vec_Str_t * vFixed, Vec_Str_t * vEmpty, Vec_Str_t * vDenied, Vec_Wrd_t * vTruths, Vec_Int_t * vStarts, Vec_Wrd_t * vTruths2 ); extern void Sfm_NtkFree( Sfm_Ntk_t * p ); extern Vec_Int_t * Sfm_NodeReadFanins( Sfm_Ntk_t * p, int i ); extern word * Sfm_NodeReadTruth( Sfm_Ntk_t * p, int i ); diff --git a/src/opt/sfm/sfmCore.c b/src/opt/sfm/sfmCore.c index c0dc70dd50..87c11e615f 100644 --- a/src/opt/sfm/sfmCore.c +++ b/src/opt/sfm/sfmCore.c @@ -115,7 +115,7 @@ int Sfm_NodeResubSolve( Sfm_Ntk_t * p, int iNode, int f, int fRemoveOnly ) int fSkipUpdate = 0; int fVeryVerbose = 0;//p->pPars->fVeryVerbose && Vec_IntSize(p->vDivs) < 200;// || pNode->Id == 556; int i, iFanin, iVar = -1; - int iFaninRem = -1, iFaninSkip = -1; + int iFaninRem = -1; int nFanins = Sfm_ObjFaninNum(p, iNode); word uTruth, uSign, uMask; abctime clk; @@ -138,9 +138,6 @@ int Sfm_NodeResubSolve( Sfm_Ntk_t * p, int iNode, int f, int fRemoveOnly ) else iFaninRem = iFanin; assert( iFaninRem != -1 ); - // find fanin to skip - if ( Sfm_ObjIsFixed(p, iFaninRem) && Sfm_ObjFaninNum(p, iFaninRem) == 1 ) - iFaninSkip = Sfm_ObjFanin(p, iFaninRem, 0); clk = Abc_Clock(); uTruth = Sfm_ComputeInterpolant( p ); p->timeSat += Abc_Clock() - clk; @@ -176,11 +173,10 @@ p->timeSat += Abc_Clock() - clk; // find the next divisor to try uMask = (~(word)0) >> (64 - p->nCexes); Vec_WrdForEachEntry( p->vDivCexes, uSign, iVar ) - if ( uSign == uMask && Vec_IntEntry(p->vDivs, iVar) != iFaninSkip ) + if ( uSign == uMask && !Sfm_ObjIsDenied(p, Vec_IntEntry(p->vDivs, iVar)) ) break; if ( iVar == Vec_IntSize(p->vDivs) ) return 0; - assert( Vec_IntEntry(p->vDivs, iVar) != iFaninSkip ); // try replacing the critical fanin Vec_IntPush( p->vDivIds, Sfm_ObjSatVar(p, Vec_IntEntry(p->vDivs, iVar)) ); clk = Abc_Clock(); @@ -289,6 +285,10 @@ p->timeSat += Abc_Clock() - clk; int Sfm_NodeResub( Sfm_Ntk_t * p, int iNode ) { int i, iFanin; + + if (Sfm_NodeReadFixed(p, iNode)) + return 0; + p->nNodesTried++; // prepare SAT solver if ( !Sfm_NtkCreateWindow( p, iNode, p->pPars->fVeryVerbose ) ) diff --git a/src/opt/sfm/sfmInt.h b/src/opt/sfm/sfmInt.h index ae4bb60a80..6f86a5c6a3 100644 --- a/src/opt/sfm/sfmInt.h +++ b/src/opt/sfm/sfmInt.h @@ -81,6 +81,7 @@ struct Sfm_Ntk_t_ // user data Vec_Str_t * vFixed; // persistent objects Vec_Str_t * vEmpty; // transparent objects + Vec_Str_t * vDenied; // denied objects Vec_Wrd_t * vTruths; // truth tables Vec_Wec_t vFanins; // fanins Vec_Int_t * vStarts; // offsets @@ -157,6 +158,7 @@ static inline int Sfm_ObjIsPi( Sfm_Ntk_t * p, int i ) { return static inline int Sfm_ObjIsPo( Sfm_Ntk_t * p, int i ) { return i + p->nPos >= p->nObjs; } static inline int Sfm_ObjIsNode( Sfm_Ntk_t * p, int i ) { return i >= p->nPis && i + p->nPos < p->nObjs; } static inline int Sfm_ObjIsFixed( Sfm_Ntk_t * p, int i ) { return Vec_StrEntry(p->vFixed, i); } +static inline int Sfm_ObjIsDenied( Sfm_Ntk_t * p, int i ) { return p->vDenied && Vec_StrEntry(p->vDenied, i); } static inline int Sfm_ObjAddsLevelArray( Vec_Str_t * p, int i ) { return p == NULL || Vec_StrEntry(p, i) == 0; } static inline int Sfm_ObjAddsLevel( Sfm_Ntk_t * p, int i ) { return Sfm_ObjAddsLevelArray(p->vEmpty, i); } diff --git a/src/opt/sfm/sfmNtk.c b/src/opt/sfm/sfmNtk.c index 70afd91aae..9f32cbb580 100644 --- a/src/opt/sfm/sfmNtk.c +++ b/src/opt/sfm/sfmNtk.c @@ -58,7 +58,7 @@ void Sfm_CheckConsistency( Vec_Wec_t * vFanins, int nPis, int nPos, Vec_Str_t * assert( Fanin + nPos < Vec_WecSize(vFanins) ); // POs have one fanout if ( i + nPos >= Vec_WecSize(vFanins) ) - assert( Vec_IntSize(vArray) == 1 && Vec_StrEntry(vFixed, i) == (char)0 ); + assert( Vec_IntSize(vArray) == 1 ); } } @@ -164,7 +164,7 @@ void Sfm_CreateLevelR( Vec_Wec_t * vFanouts, Vec_Int_t * vLevelsR, Vec_Str_t * v SeeAlso [] ***********************************************************************/ -Sfm_Ntk_t * Sfm_NtkConstruct( Vec_Wec_t * vFanins, int nPis, int nPos, Vec_Str_t * vFixed, Vec_Str_t * vEmpty, Vec_Wrd_t * vTruths, Vec_Int_t * vStarts, Vec_Wrd_t * vTruths2 ) +Sfm_Ntk_t * Sfm_NtkConstruct( Vec_Wec_t * vFanins, int nPis, int nPos, Vec_Str_t * vFixed, Vec_Str_t * vEmpty, Vec_Str_t * vDenied, Vec_Wrd_t * vTruths, Vec_Int_t * vStarts, Vec_Wrd_t * vTruths2 ) { Sfm_Ntk_t * p; int i; Sfm_CheckConsistency( vFanins, nPis, nPos, vFixed ); @@ -177,6 +177,7 @@ Sfm_Ntk_t * Sfm_NtkConstruct( Vec_Wec_t * vFanins, int nPis, int nPos, Vec_Str_t p->vFixed = vFixed; p->vEmpty = vEmpty; p->vTruths = vTruths; + p->vDenied = vDenied; p->vFanins = *vFanins; p->vStarts = vStarts; p->vTruths2 = vTruths2; @@ -221,6 +222,7 @@ void Sfm_NtkFree( Sfm_Ntk_t * p ) // user data Vec_StrFree( p->vFixed ); Vec_StrFreeP( &p->vEmpty ); + Vec_StrFreeP( &p->vDenied ); Vec_WrdFree( p->vTruths ); Vec_WecErase( &p->vFanins ); Vec_IntFree( p->vStarts ); From 1107634fa60253eb359f71f0a80dc32d6ba60459 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20Povi=C5=A1er?= Date: Fri, 22 Mar 2024 22:47:40 +0100 Subject: [PATCH 54/79] &mfs: Make it no biggie when a network is all blackboxes, no whiteboxes --- src/aig/gia/giaMfs.c | 20 ++++++++++++-------- 1 file changed, 12 insertions(+), 8 deletions(-) diff --git a/src/aig/gia/giaMfs.c b/src/aig/gia/giaMfs.c index 91241aaff6..654e53f303 100644 --- a/src/aig/gia/giaMfs.c +++ b/src/aig/gia/giaMfs.c @@ -153,12 +153,16 @@ Sfm_Ntk_t * Gia_ManExtractMfs( Gia_Man_t * p ) // skip POs due to box inputs Counter += nBbIns; assert( Counter == nMfsVars ); + // add functions of the boxes if ( p->pAigExtra ) { - int iBbIn = 0, iBbOut = 0; assert( Gia_ManCiNum(p->pAigExtra) < 16 ); Gia_ObjComputeTruthTableStart( p->pAigExtra, Gia_ManCiNum(p->pAigExtra) ); + } + + { + int iBbIn = 0, iBbOut = 0; curCi = nRealPis; curCo = 0; for ( k = 0; k < nBoxes; k++ ) @@ -168,6 +172,7 @@ Sfm_Ntk_t * Gia_ManExtractMfs( Gia_Man_t * p ) // iterate through box outputs if ( !Tim_ManBoxIsBlack(pManTime, k) ) //&& Tim_ManBoxInputNum(pManTime, k) <= 6 ) { + assert(p->pAigExtra); // collect truth table leaves Vec_IntClear( vLeaves ); for ( i = 0; i < nBoxIns; i++ ) @@ -267,14 +272,18 @@ Sfm_Ntk_t * Gia_ManExtractMfs( Gia_Man_t * p ) curCi += nBoxOuts; } curCo += nRealPos; - Gia_ObjComputeTruthTableStop( p->pAigExtra ); // verify counts assert( curCi == Gia_ManCiNum(p) ); assert( curCo == Gia_ManCoNum(p) ); - assert( curCi - nRealPis == Gia_ManCoNum(p->pAigExtra) ); assert( iBbIn == nBbIns ); assert( iBbOut == nBbOuts ); } + + if (p->pAigExtra) { + Gia_ObjComputeTruthTableStop( p->pAigExtra ); + assert( curCi - nRealPis == Gia_ManCoNum(p->pAigExtra) ); + } + // finalize Vec_IntFree( vLeaves ); return Sfm_NtkConstruct( vFanins, nBbOuts + nRealPis, nRealPos + nBbIns, vFixed, vEmpty, vDenied, vTruths, vStarts, vTruths2 ); @@ -512,11 +521,6 @@ Gia_Man_t * Gia_ManPerformMfs( Gia_Man_t * p, Sfm_Par_t * pPars ) int nFaninMax, nNodes; assert( Gia_ManRegNum(p) == 0 ); assert( p->vMapping != NULL ); - if ( p->pManTime != NULL && p->pAigExtra == NULL ) - { - Abc_Print( 1, "Timing manager is given but there is no GIA of boxes.\n" ); - return NULL; - } if ( p->pManTime != NULL && p->pAigExtra != NULL && Gia_ManCiNum(p->pAigExtra) > 15 ) { Abc_Print( 1, "Currently \"&mfs\" cannot process the network containing white-boxes with more than 15 inputs.\n" ); From 316eec6d3f7addf81424bd51f846731f3b8696d7 Mon Sep 17 00:00:00 2001 From: David A Roberts Date: Mon, 1 Apr 2024 09:40:41 +1000 Subject: [PATCH 55/79] Fix Assertion using &if: `pCutSet->nCuts > 0' --- src/map/if/ifMap.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/map/if/ifMap.c b/src/map/if/ifMap.c index b3a4caf665..b4242adbbc 100644 --- a/src/map/if/ifMap.c +++ b/src/map/if/ifMap.c @@ -444,7 +444,7 @@ void If_ObjPerformMappingAnd( If_Man_t * p, If_Obj_t * pObj, int Mode, int fPrep pCut->Delay = If_CutDelay( p, pObj, pCut ); if ( pCut->Delay == -1 ) continue; - if ( Mode && pCut->Delay > pObj->Required + p->fEpsilon ) + if ( Mode && pCut->Delay > pObj->Required + p->fEpsilon && pCutSet->nCuts > 0 ) continue; // compute area of the cut (this area may depend on the application specific cost) pCut->Area = (Mode == 2)? If_CutAreaDerefed( p, pCut ) : If_CutAreaFlow( p, pCut ); From accf50468a0fb88db8de9743b751ab9226e8f343 Mon Sep 17 00:00:00 2001 From: David A Roberts Date: Mon, 1 Apr 2024 10:03:10 +1000 Subject: [PATCH 56/79] Apply patch to If_ObjPerformMappingChoice too --- src/map/if/ifMap.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/map/if/ifMap.c b/src/map/if/ifMap.c index b4242adbbc..ffc1e38467 100644 --- a/src/map/if/ifMap.c +++ b/src/map/if/ifMap.c @@ -542,7 +542,7 @@ void If_ObjPerformMappingChoice( If_Man_t * p, If_Obj_t * pObj, int Mode, int fP continue; // check if the cut satisfies the required times // assert( pCut->Delay == If_CutDelay( p, pTemp, pCut ) ); - if ( Mode && pCut->Delay > pObj->Required + p->fEpsilon ) + if ( Mode && pCut->Delay > pObj->Required + p->fEpsilon && pCutSet->nCuts > 0 ) continue; // set the phase attribute pCut->fCompl = pObj->fPhase ^ pTemp->fPhase; From 2c52e3f969827fbba60224b15b4da0f867e62a26 Mon Sep 17 00:00:00 2001 From: Roland Coeurjoly Date: Thu, 11 Apr 2024 16:28:36 +0200 Subject: [PATCH 57/79] Support out of tree builds --- Makefile | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/Makefile b/Makefile index 8b38e71537..1da42b778b 100644 --- a/Makefile +++ b/Makefile @@ -5,7 +5,8 @@ AR := ar LD := $(CXX) MSG_PREFIX ?= -ABCSRC = . +ABCSRC ?= . +VPATH = $(ABCSRC) $(info $(MSG_PREFIX)Using CC=$(CC)) $(info $(MSG_PREFIX)Using CXX=$(CXX)) @@ -41,7 +42,7 @@ default: $(PROG) ARCHFLAGS_EXE ?= ./arch_flags $(ARCHFLAGS_EXE) : arch_flags.c - $(CC) arch_flags.c -o $(ARCHFLAGS_EXE) + $(CC) $< -o $(ARCHFLAGS_EXE) INCLUDES += -I$(ABCSRC)/src @@ -173,26 +174,32 @@ DEP := $(OBJ:.o=.d) # implicit rules %.o: %.c + @mkdir -p $(dir $@) @echo "$(MSG_PREFIX)\`\` Compiling:" $(LOCAL_PATH)/$< $(VERBOSE)$(CC) -c $(OPTFLAGS) $(INCLUDES) $(CFLAGS) $< -o $@ %.o: %.cc + @mkdir -p $(dir $@) @echo "$(MSG_PREFIX)\`\` Compiling:" $(LOCAL_PATH)/$< $(VERBOSE)$(CXX) -c $(OPTFLAGS) $(INCLUDES) $(CXXFLAGS) $< -o $@ %.o: %.cpp + @mkdir -p $(dir $@) @echo "$(MSG_PREFIX)\`\` Compiling:" $(LOCAL_PATH)/$< $(VERBOSE)$(CXX) -c $(OPTFLAGS) $(INCLUDES) $(CXXFLAGS) $< -o $@ %.d: %.c + @mkdir -p $(dir $@) @echo "$(MSG_PREFIX)\`\` Generating dependency:" $(LOCAL_PATH)/$< $(VERBOSE)$(ABCSRC)/depends.sh "$(CC)" `dirname $*.c` $(OPTFLAGS) $(INCLUDES) $(CFLAGS) $< > $@ %.d: %.cc + @mkdir -p $(dir $@) @echo "$(MSG_PREFIX)\`\` Generating dependency:" $(LOCAL_PATH)/$< $(VERBOSE)$(ABCSRC)/depends.sh "$(CXX)" `dirname $*.cc` $(OPTFLAGS) $(INCLUDES) $(CXXFLAGS) $< > $@ %.d: %.cpp + @mkdir -p $(dir $@) @echo "$(MSG_PREFIX)\`\` Generating dependency:" $(LOCAL_PATH)/$< $(VERBOSE)$(ABCSRC)/depends.sh "$(CXX)" `dirname $*.cpp` $(OPTFLAGS) $(INCLUDES) $(CXXFLAGS) $< > $@ From b502f0022290e0a52c624d87fcc42b6ba5881720 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20Povi=C5=A1er?= Date: Mon, 22 Apr 2024 17:34:38 +0200 Subject: [PATCH 58/79] Fix prototype mismatch for `Gia_ManSimRsb` --- src/base/abci/abc.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/base/abci/abc.c b/src/base/abci/abc.c index 0146f6a735..d7e16576ba 100644 --- a/src/base/abci/abc.c +++ b/src/base/abci/abc.c @@ -35055,7 +35055,7 @@ int Abc_CommandAbc9GenSim( Abc_Frame_t * pAbc, int argc, char ** argv ) ***********************************************************************/ int Abc_CommandAbc9SimRsb( Abc_Frame_t * pAbc, int argc, char ** argv ) { - extern void Gia_ManSimRsb( Gia_Man_t * p, int nCands, int fVerbose ); + extern int Gia_ManSimRsb( Gia_Man_t * p, int nCands, int fVerbose ); int c, nCands = 32, fVerbose = 0; Extra_UtilGetoptReset(); while ( ( c = Extra_UtilGetopt( argc, argv, "Nvh" ) ) != EOF ) From 03da96f12fb4deb153cc0dc73936df346ecd4bcf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20Povi=C5=A1er?= Date: Tue, 23 Apr 2024 11:40:09 +0200 Subject: [PATCH 59/79] Patch for lack of `system` on WASM --- src/aig/gia/giaStoch.c | 4 ++++ src/base/abci/abc.c | 4 ++++ 2 files changed, 8 insertions(+) diff --git a/src/aig/gia/giaStoch.c b/src/aig/gia/giaStoch.c index fc65c80bb7..f7515e8350 100644 --- a/src/aig/gia/giaStoch.c +++ b/src/aig/gia/giaStoch.c @@ -145,7 +145,11 @@ Gia_Man_t * Gia_StochProcessOne( Gia_Man_t * p, char * pScript, int Rand, int Ti sprintf( FileName, "%06x.aig", Rand ); Gia_AigerWrite( p, FileName, 0, 0, 0 ); sprintf( Command, "./abc -q \"&read %s; %s; &write %s\"", FileName, pScript, FileName ); +#if defined(__wasm) + if ( 1 ) +#else if ( system( (char *)Command ) ) +#endif { fprintf( stderr, "The following command has returned non-zero exit status:\n" ); fprintf( stderr, "\"%s\"\n", (char *)Command ); diff --git a/src/base/abci/abc.c b/src/base/abci/abc.c index d7e16576ba..6d6530741c 100644 --- a/src/base/abci/abc.c +++ b/src/base/abci/abc.c @@ -7252,7 +7252,11 @@ int Abc_CommandRunSat( Abc_Frame_t * pAbc, int argc, char ** argv ) sprintf( pCommand, "kissat -q --seed=%d %s", i, pFileCnf ); else if ( fWalk ) sprintf( pCommand, "walk -s%d %s", i, pFileCnf ); +#if defined(__wasm) + if (1) { +#else if (system(pCommand) == -1) { +#endif fprintf(stdout, "Command \"%s\" did not succeed.\n", pCommand); return 0; } From 237d81397fcc85dd3894bf1a449d2955cd3df02d Mon Sep 17 00:00:00 2001 From: "William D. Jones" Date: Sat, 27 Apr 2024 19:06:36 -0400 Subject: [PATCH 60/79] Modify include guards in cmd.c so that Windows compilers don't compile Unix-only code. --- src/base/cmd/cmd.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/base/cmd/cmd.c b/src/base/cmd/cmd.c index c8dd443259..5b6919f769 100644 --- a/src/base/cmd/cmd.c +++ b/src/base/cmd/cmd.c @@ -51,7 +51,7 @@ static int CmdCommandUnsetVariable ( Abc_Frame_t * pAbc, int argc, char ** argv static int CmdCommandUndo ( Abc_Frame_t * pAbc, int argc, char ** argv ); static int CmdCommandRecall ( Abc_Frame_t * pAbc, int argc, char ** argv ); static int CmdCommandEmpty ( Abc_Frame_t * pAbc, int argc, char ** argv ); -#if defined(WIN32) && !defined(__cplusplus) +#if defined(WIN32) static int CmdCommandScanDir ( Abc_Frame_t * pAbc, int argc, char ** argv ); static int CmdCommandRenameFiles ( Abc_Frame_t * pAbc, int argc, char ** argv ); static int CmdCommandLs ( Abc_Frame_t * pAbc, int argc, char ** argv ); @@ -105,7 +105,7 @@ void Cmd_Init( Abc_Frame_t * pAbc ) Cmd_CommandAdd( pAbc, "Basic", "undo", CmdCommandUndo, 0 ); Cmd_CommandAdd( pAbc, "Basic", "recall", CmdCommandRecall, 0 ); Cmd_CommandAdd( pAbc, "Basic", "empty", CmdCommandEmpty, 0 ); -#if defined(WIN32) && !defined(__cplusplus) +#if defined(WIN32) Cmd_CommandAdd( pAbc, "Basic", "scandir", CmdCommandScanDir, 0 ); Cmd_CommandAdd( pAbc, "Basic", "renamefiles", CmdCommandRenameFiles, 0 ); Cmd_CommandAdd( pAbc, "Basic", "ls", CmdCommandLs, 0 ); @@ -1209,7 +1209,7 @@ int CmdCommandUndo( Abc_Frame_t * pAbc, int argc, char **argv ) #endif -#if defined(WIN32) && !defined(__cplusplus) +#if defined(WIN32) #include #include @@ -1395,10 +1395,10 @@ int CmfFindNumber( char * pName ) ***********************************************************************/ void CnfDupFileUnzip( char * pOldName ) { - extern char * Io_MvLoadFileBz2( char * pFileName, int * pnFileSize ); + extern char * Io_MvLoadFileBz2( char * pFileName, long * pnFileSize ); char pNewName[1000]; FILE * pFile; - int nFileSize; + long nFileSize; char * pBuffer = Io_MvLoadFileBz2( pOldName, &nFileSize ); assert( strlen(pOldName) < 1000 ); sprintf( pNewName, "%s.v", pOldName ); From 04f50406fd93f4193a58083e391b27e195e4bfd5 Mon Sep 17 00:00:00 2001 From: Jade Lovelace Date: Thu, 27 Jun 2024 13:41:10 -0700 Subject: [PATCH 61/79] Fix archive reproducibility MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The git archive export-subst option does not have consistent results over time, since the abbreviated commit hash can get longer over time. Instead, let's export the full commit hash. This was found in an audit of source archive reproducibility in nixpkgs: ``` ~ » expected=$(nix-store -r /nix/store/4j9rj4m6akjskp0f7k923qff817k6hv5-source) actual=$(nix-prefetch-url --print-path --unpack --name source https://github.com/yosyshq/abc/archive/896e5e7dedf9b9b1459fa019f1fa8aa8101fdf43.tar.gz | tail -n1) nix-shell -p diffoscope --run "diffoscope $expected $actual" this path will be fetched (3.77 MiB download, 34.50 MiB unpacked): /nix/store/4j9rj4m6akjskp0f7k923qff817k6hv5-source copying path '/nix/store/4j9rj4m6akjskp0f7k923qff817k6hv5-source' from 'https://cache.nixos.org'... warning: you did not specify '--add-root'; the result might be removed by the garbage collector --- /nix/store/4j9rj4m6akjskp0f7k923qff817k6hv5-source +++ /nix/store/v7ms5ghibzi8pk71nzlhvsbn7a0rdpy7-source │ --- /nix/store/4j9rj4m6akjskp0f7k923qff817k6hv5-source/.gitcommit ├── +++ /nix/store/v7ms5ghibzi8pk71nzlhvsbn7a0rdpy7-source/.gitcommit │ @@ -1 +1 @@ │ -896e5e7de │ +896e5e7ded │ ├── stat {} │ │ @@ -1,7 +1,7 @@ │ │ │ │ - Size: 10 Blocks: 1 IO Block: 512 regular file │ │ + Size: 11 Blocks: 1 IO Block: 512 regular file │ │ Device: 0,24 Access: (0444/-r--r--r--) Uid: ( 0/ root) Gid: ( 0/ root) │ │ │ │ Modify: 1970-01-01 00:00:01.000000000 +0000 ``` --- .gitcommit | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.gitcommit b/.gitcommit index 46b7856fbc..6828f88dcb 100644 --- a/.gitcommit +++ b/.gitcommit @@ -1 +1 @@ -$Format:%h$ +$Format:%H$ From cac8f99eaa220a5e3db5caeb87cef0a975c953a2 Mon Sep 17 00:00:00 2001 From: Alan Mishchenko Date: Sun, 17 Sep 2023 11:29:26 +0800 Subject: [PATCH 62/79] Revert "Merge pull request #247 from QuantamHD/abc_unit_tests" This reverts commit d91a2a049adec1797c7fa4aaeaef02a3c168734a, reversing changes made to 475c8dad8ecbbe3cc1c864fb3462c83438184f6d. (cherry picked from commit 4222921d61b1f84460e29199eb15b27878b57d8c) --- .github/workflows/build-posix-cmake.yml | 4 -- CMakeLists.txt | 14 ------- test/CMakeLists.txt | 1 - test/gia/CMakeLists.txt | 11 ----- test/gia/gia_test.cc | 54 ------------------------- 5 files changed, 84 deletions(-) delete mode 100644 test/CMakeLists.txt delete mode 100644 test/gia/CMakeLists.txt delete mode 100644 test/gia/gia_test.cc diff --git a/.github/workflows/build-posix-cmake.yml b/.github/workflows/build-posix-cmake.yml index 50f9ef902f..f8ccde47df 100644 --- a/.github/workflows/build-posix-cmake.yml +++ b/.github/workflows/build-posix-cmake.yml @@ -44,10 +44,6 @@ jobs: run: | cmake --build build - - name: Run Unit Tests - run: | - ctest --output-on-failure - - name: Test Executable run: | ./build/abc -c "r i10.aig; b; ps; b; rw -l; rw -lz; b; rw -lz; b; ps; cec" diff --git a/CMakeLists.txt b/CMakeLists.txt index 05e6001f9c..ce541b994b 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -112,17 +112,3 @@ add_library(libabc-pic EXCLUDE_FROM_ALL ${ABC_SRC}) abc_properties(libabc-pic PUBLIC) set_property(TARGET libabc-pic PROPERTY POSITION_INDEPENDENT_CODE ON) set_property(TARGET libabc-pic PROPERTY OUTPUT_NAME abc-pic) - -if(NOT DEFINED ABC_SKIP_TESTS) - enable_testing() - include(FetchContent) - FetchContent_Declare( - googletest - DOWNLOAD_EXTRACT_TIMESTAMP TRUE - # Specify the commit you depend on and update it regularly. - URL "https://github.com/google/googletest/archive/refs/tags/v1.14.0.zip" - ) - FetchContent_MakeAvailable(googletest) - include(GoogleTest) - add_subdirectory(test) -endif() \ No newline at end of file diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt deleted file mode 100644 index 5a187fcac7..0000000000 --- a/test/CMakeLists.txt +++ /dev/null @@ -1 +0,0 @@ -add_subdirectory(gia) \ No newline at end of file diff --git a/test/gia/CMakeLists.txt b/test/gia/CMakeLists.txt deleted file mode 100644 index c9022dc8c0..0000000000 --- a/test/gia/CMakeLists.txt +++ /dev/null @@ -1,11 +0,0 @@ -add_executable(gia_test gia_test.cc) - -target_link_libraries(gia_test - gtest - gtest_main - libabc -) - -gtest_discover_tests(gia_test - WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} -) \ No newline at end of file diff --git a/test/gia/gia_test.cc b/test/gia/gia_test.cc deleted file mode 100644 index a6b288f791..0000000000 --- a/test/gia/gia_test.cc +++ /dev/null @@ -1,54 +0,0 @@ -#include "gtest/gtest.h" - -#include "aig/gia/gia.h" - -ABC_NAMESPACE_IMPL_START - -TEST(GiaTest, CanAllocateGiaManager) { - Gia_Man_t* aig_manager = Gia_ManStart(100); - - EXPECT_TRUE(aig_manager != nullptr); - Gia_ManStop(aig_manager); -} - -TEST(GiaTest, CanAddACi) { - Gia_Man_t* aig_manager = Gia_ManStart(100); - Gia_ManAppendCi(aig_manager); - - EXPECT_EQ(Gia_ManCiNum(aig_manager), 1); - Gia_ManStop(aig_manager); -} - -TEST(GiaTest, CanAddACo) { - Gia_Man_t* aig_manager = Gia_ManStart(100); - int input1 = Gia_ManAppendCi(aig_manager); - Gia_ManAppendCo(aig_manager, input1); - - EXPECT_EQ(Gia_ManCiNum(aig_manager), 1); - EXPECT_EQ(Gia_ManCoNum(aig_manager), 1); - Gia_ManStop(aig_manager); -} - -TEST(GiaTest, CanAddAnAndGate) { - Gia_Man_t* aig_manager = Gia_ManStart(100); - - int input1 = Gia_ManAppendCi(aig_manager); - int input2 = Gia_ManAppendCi(aig_manager); - - int and_output = Gia_ManAppendAnd(aig_manager, input1, input2); - Gia_ManAppendCo(aig_manager, and_output); - - Vec_Wrd_t* stimulus = Vec_WrdAlloc(2); - Vec_WrdPush(stimulus, /*A*/1); - Vec_WrdPush(stimulus, /*B*/1); - Vec_Wrd_t* output = Gia_ManSimPatSimOut(aig_manager, stimulus, /*fouts*/1); - - EXPECT_EQ(Gia_ManCiNum(aig_manager), 2); - EXPECT_EQ(Gia_ManCoNum(aig_manager), 1); - // A = 1, B = 1 -> A & B == 1 - EXPECT_EQ(Vec_WrdGetEntry(output, 0), 1); - Vec_WrdFree(output); - Gia_ManStop(aig_manager); -} - -ABC_NAMESPACE_IMPL_END From 013023f1bf139b70119c8b863baa8d5cad45f002 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20Povi=C5=A1er?= Date: Mon, 20 Jan 2025 12:45:53 +0100 Subject: [PATCH 63/79] Fix UB in `&mfs -r` print --- src/aig/gia/giaMfs.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/aig/gia/giaMfs.c b/src/aig/gia/giaMfs.c index 654e53f303..f4a8415108 100644 --- a/src/aig/gia/giaMfs.c +++ b/src/aig/gia/giaMfs.c @@ -518,7 +518,7 @@ Gia_Man_t * Gia_ManPerformMfs( Gia_Man_t * p, Sfm_Par_t * pPars ) { Sfm_Ntk_t * pNtk; Gia_Man_t * pNew; - int nFaninMax, nNodes; + int nFaninMax, nNodes = 0; assert( Gia_ManRegNum(p) == 0 ); assert( p->vMapping != NULL ); if ( p->pManTime != NULL && p->pAigExtra != NULL && Gia_ManCiNum(p->pAigExtra) > 15 ) From 5ecc7c333ceeadef9b2f3e4a82f7ec72fa512d86 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20Povi=C5=A1er?= Date: Tue, 11 Mar 2025 20:15:41 +0100 Subject: [PATCH 64/79] Revert addition of CaDiCaL This reverts the upstream PR berkeley-abc/abc#382 --- Makefile | 2 +- abclib.dsp | 368 - src/base/abci/abc.c | 3 - src/sat/cadical/LICENSE | 28 - src/sat/cadical/VERSION | 1 - src/sat/cadical/arena.hpp | 111 - src/sat/cadical/averages.hpp | 43 - src/sat/cadical/bins.hpp | 28 - src/sat/cadical/block.hpp | 43 - src/sat/cadical/cadical.hpp | 1351 --- src/sat/cadical/cadicalSolver.c | 305 - src/sat/cadical/cadicalSolver.h | 76 - src/sat/cadical/cadicalTest.c | 210 - src/sat/cadical/cadical_analyze.cpp | 1285 --- src/sat/cadical/cadical_arena.cpp | 36 - src/sat/cadical/cadical_assume.cpp | 613 -- src/sat/cadical/cadical_averages.cpp | 40 - src/sat/cadical/cadical_backtrack.cpp | 179 - src/sat/cadical/cadical_backward.cpp | 237 - src/sat/cadical/cadical_bins.cpp | 28 - src/sat/cadical/cadical_block.cpp | 830 -- src/sat/cadical/cadical_ccadical.cpp | 211 - src/sat/cadical/cadical_checker.cpp | 654 -- src/sat/cadical/cadical_clause.cpp | 649 -- src/sat/cadical/cadical_collect.cpp | 551 -- src/sat/cadical/cadical_compact.cpp | 557 -- src/sat/cadical/cadical_condition.cpp | 946 --- src/sat/cadical/cadical_config.cpp | 107 - src/sat/cadical/cadical_congruence.cpp | 7567 ----------------- src/sat/cadical/cadical_constrain.cpp | 70 - src/sat/cadical/cadical_contract.cpp | 33 - src/sat/cadical/cadical_cover.cpp | 710 -- src/sat/cadical/cadical_decide.cpp | 258 - src/sat/cadical/cadical_decompose.cpp | 739 -- src/sat/cadical/cadical_deduplicate.cpp | 176 - src/sat/cadical/cadical_definition.cpp | 289 - src/sat/cadical/cadical_drattracer.cpp | 159 - src/sat/cadical/cadical_elim.cpp | 1178 --- src/sat/cadical/cadical_elimfast.cpp | 576 -- src/sat/cadical/cadical_ema.cpp | 101 - src/sat/cadical/cadical_extend.cpp | 287 - src/sat/cadical/cadical_external.cpp | 1028 --- .../cadical/cadical_external_propagate.cpp | 1232 --- src/sat/cadical/cadical_factor.cpp | 927 -- src/sat/cadical/cadical_file.cpp | 527 -- src/sat/cadical/cadical_flags.cpp | 141 - src/sat/cadical/cadical_flip.cpp | 275 - src/sat/cadical/cadical_format.cpp | 95 - src/sat/cadical/cadical_frattracer.cpp | 283 - src/sat/cadical/cadical_gates.cpp | 772 -- src/sat/cadical/cadical_idruptracer.cpp | 572 -- src/sat/cadical/cadical_instantiate.cpp | 371 - src/sat/cadical/cadical_internal.cpp | 1196 --- src/sat/cadical/cadical_ipasir.cpp | 46 - src/sat/cadical/cadical_kitten.c | 2609 ------ src/sat/cadical/cadical_lidruptracer.cpp | 662 -- src/sat/cadical/cadical_limit.cpp | 135 - src/sat/cadical/cadical_logging.cpp | 221 - src/sat/cadical/cadical_lookahead.cpp | 526 -- src/sat/cadical/cadical_lratchecker.cpp | 835 -- src/sat/cadical/cadical_lrattracer.cpp | 206 - src/sat/cadical/cadical_lucky.cpp | 440 - src/sat/cadical/cadical_message.cpp | 218 - src/sat/cadical/cadical_minimize.cpp | 230 - src/sat/cadical/cadical_occs.cpp | 58 - src/sat/cadical/cadical_options.cpp | 365 - src/sat/cadical/cadical_parse.cpp | 442 - src/sat/cadical/cadical_phases.cpp | 50 - src/sat/cadical/cadical_probe.cpp | 993 --- src/sat/cadical/cadical_profile.cpp | 113 - src/sat/cadical/cadical_proof.cpp | 663 -- src/sat/cadical/cadical_propagate.cpp | 586 -- src/sat/cadical/cadical_queue.cpp | 96 - src/sat/cadical/cadical_random.cpp | 233 - src/sat/cadical/cadical_reap.cpp | 142 - src/sat/cadical/cadical_reduce.cpp | 284 - src/sat/cadical/cadical_rephase.cpp | 342 - src/sat/cadical/cadical_report.cpp | 302 - src/sat/cadical/cadical_resources.cpp | 166 - src/sat/cadical/cadical_restart.cpp | 165 - src/sat/cadical/cadical_restore.cpp | 273 - src/sat/cadical/cadical_score.cpp | 57 - src/sat/cadical/cadical_shrink.cpp | 513 -- src/sat/cadical/cadical_signal.cpp | 144 - src/sat/cadical/cadical_solution.cpp | 56 - src/sat/cadical/cadical_solver.cpp | 1764 ---- src/sat/cadical/cadical_stable.cpp | 39 - src/sat/cadical/cadical_stats.cpp | 826 -- src/sat/cadical/cadical_subsume.cpp | 652 -- src/sat/cadical/cadical_sweep.cpp | 1959 ----- src/sat/cadical/cadical_terminal.cpp | 49 - src/sat/cadical/cadical_ternary.cpp | 456 - src/sat/cadical/cadical_tier.cpp | 59 - src/sat/cadical/cadical_transred.cpp | 259 - src/sat/cadical/cadical_unstable.cpp | 38 - src/sat/cadical/cadical_util.cpp | 135 - src/sat/cadical/cadical_var.cpp | 45 - src/sat/cadical/cadical_veripbtracer.cpp | 400 - src/sat/cadical/cadical_version.cpp | 113 - src/sat/cadical/cadical_vivify.cpp | 1962 ----- src/sat/cadical/cadical_walk.cpp | 710 -- src/sat/cadical/cadical_watch.cpp | 132 - src/sat/cadical/ccadical.h | 74 - src/sat/cadical/checker.hpp | 178 - src/sat/cadical/clause.hpp | 194 - src/sat/cadical/config.hpp | 26 - src/sat/cadical/congruence.hpp | 720 -- src/sat/cadical/contract.hpp | 142 - src/sat/cadical/cover.hpp | 36 - src/sat/cadical/decompose.hpp | 29 - src/sat/cadical/delay.hpp | 44 - src/sat/cadical/drattracer.hpp | 59 - src/sat/cadical/elim.hpp | 59 - src/sat/cadical/ema.hpp | 74 - src/sat/cadical/external.hpp | 467 - src/sat/cadical/factor.hpp | 60 - src/sat/cadical/file.hpp | 221 - src/sat/cadical/flags.hpp | 91 - src/sat/cadical/format.hpp | 42 - src/sat/cadical/frattracer.hpp | 68 - src/sat/cadical/global.h | 21 - src/sat/cadical/heap.hpp | 218 - src/sat/cadical/idruptracer.hpp | 116 - src/sat/cadical/instantiate.hpp | 51 - src/sat/cadical/internal.hpp | 1866 ---- src/sat/cadical/inttypes.hpp | 30 - src/sat/cadical/ipasir.h | 35 - src/sat/cadical/kitten.h | 95 - src/sat/cadical/level.hpp | 39 - src/sat/cadical/lidruptracer.hpp | 123 - src/sat/cadical/limit.hpp | 162 - src/sat/cadical/logging.hpp | 104 - src/sat/cadical/lratchecker.hpp | 170 - src/sat/cadical/lrattracer.hpp | 63 - src/sat/cadical/message.hpp | 71 - src/sat/cadical/module.make | 91 - src/sat/cadical/occs.hpp | 42 - src/sat/cadical/options.hpp | 422 - src/sat/cadical/parse.hpp | 78 - src/sat/cadical/phases.hpp | 24 - src/sat/cadical/profile.hpp | 280 - src/sat/cadical/proof.hpp | 120 - src/sat/cadical/queue.hpp | 70 - src/sat/cadical/radix.hpp | 186 - src/sat/cadical/random.h | 50 - src/sat/cadical/random.hpp | 104 - src/sat/cadical/range.hpp | 105 - src/sat/cadical/reap.hpp | 34 - src/sat/cadical/reluctant.hpp | 88 - src/sat/cadical/resources.hpp | 22 - src/sat/cadical/score.hpp | 22 - src/sat/cadical/signal.hpp | 39 - src/sat/cadical/stack.h | 116 - src/sat/cadical/stats.hpp | 376 - src/sat/cadical/sweep.hpp | 67 - src/sat/cadical/terminal.hpp | 104 - src/sat/cadical/testing.hpp | 30 - src/sat/cadical/tracer.hpp | 183 - src/sat/cadical/util.hpp | 173 - src/sat/cadical/var.hpp | 28 - src/sat/cadical/veripbtracer.hpp | 108 - src/sat/cadical/version.hpp | 19 - src/sat/cadical/vivify.hpp | 68 - src/sat/cadical/watch.hpp | 78 - 164 files changed, 1 insertion(+), 59697 deletions(-) delete mode 100644 src/sat/cadical/LICENSE delete mode 100644 src/sat/cadical/VERSION delete mode 100644 src/sat/cadical/arena.hpp delete mode 100644 src/sat/cadical/averages.hpp delete mode 100644 src/sat/cadical/bins.hpp delete mode 100644 src/sat/cadical/block.hpp delete mode 100644 src/sat/cadical/cadical.hpp delete mode 100644 src/sat/cadical/cadicalSolver.c delete mode 100644 src/sat/cadical/cadicalSolver.h delete mode 100644 src/sat/cadical/cadicalTest.c delete mode 100644 src/sat/cadical/cadical_analyze.cpp delete mode 100644 src/sat/cadical/cadical_arena.cpp delete mode 100644 src/sat/cadical/cadical_assume.cpp delete mode 100644 src/sat/cadical/cadical_averages.cpp delete mode 100644 src/sat/cadical/cadical_backtrack.cpp delete mode 100644 src/sat/cadical/cadical_backward.cpp delete mode 100644 src/sat/cadical/cadical_bins.cpp delete mode 100644 src/sat/cadical/cadical_block.cpp delete mode 100644 src/sat/cadical/cadical_ccadical.cpp delete mode 100644 src/sat/cadical/cadical_checker.cpp delete mode 100644 src/sat/cadical/cadical_clause.cpp delete mode 100644 src/sat/cadical/cadical_collect.cpp delete mode 100644 src/sat/cadical/cadical_compact.cpp delete mode 100644 src/sat/cadical/cadical_condition.cpp delete mode 100644 src/sat/cadical/cadical_config.cpp delete mode 100644 src/sat/cadical/cadical_congruence.cpp delete mode 100644 src/sat/cadical/cadical_constrain.cpp delete mode 100644 src/sat/cadical/cadical_contract.cpp delete mode 100644 src/sat/cadical/cadical_cover.cpp delete mode 100644 src/sat/cadical/cadical_decide.cpp delete mode 100644 src/sat/cadical/cadical_decompose.cpp delete mode 100644 src/sat/cadical/cadical_deduplicate.cpp delete mode 100644 src/sat/cadical/cadical_definition.cpp delete mode 100644 src/sat/cadical/cadical_drattracer.cpp delete mode 100644 src/sat/cadical/cadical_elim.cpp delete mode 100644 src/sat/cadical/cadical_elimfast.cpp delete mode 100644 src/sat/cadical/cadical_ema.cpp delete mode 100644 src/sat/cadical/cadical_extend.cpp delete mode 100644 src/sat/cadical/cadical_external.cpp delete mode 100644 src/sat/cadical/cadical_external_propagate.cpp delete mode 100644 src/sat/cadical/cadical_factor.cpp delete mode 100644 src/sat/cadical/cadical_file.cpp delete mode 100644 src/sat/cadical/cadical_flags.cpp delete mode 100644 src/sat/cadical/cadical_flip.cpp delete mode 100644 src/sat/cadical/cadical_format.cpp delete mode 100644 src/sat/cadical/cadical_frattracer.cpp delete mode 100644 src/sat/cadical/cadical_gates.cpp delete mode 100644 src/sat/cadical/cadical_idruptracer.cpp delete mode 100644 src/sat/cadical/cadical_instantiate.cpp delete mode 100644 src/sat/cadical/cadical_internal.cpp delete mode 100644 src/sat/cadical/cadical_ipasir.cpp delete mode 100644 src/sat/cadical/cadical_kitten.c delete mode 100644 src/sat/cadical/cadical_lidruptracer.cpp delete mode 100644 src/sat/cadical/cadical_limit.cpp delete mode 100644 src/sat/cadical/cadical_logging.cpp delete mode 100644 src/sat/cadical/cadical_lookahead.cpp delete mode 100644 src/sat/cadical/cadical_lratchecker.cpp delete mode 100644 src/sat/cadical/cadical_lrattracer.cpp delete mode 100644 src/sat/cadical/cadical_lucky.cpp delete mode 100644 src/sat/cadical/cadical_message.cpp delete mode 100644 src/sat/cadical/cadical_minimize.cpp delete mode 100644 src/sat/cadical/cadical_occs.cpp delete mode 100644 src/sat/cadical/cadical_options.cpp delete mode 100644 src/sat/cadical/cadical_parse.cpp delete mode 100644 src/sat/cadical/cadical_phases.cpp delete mode 100644 src/sat/cadical/cadical_probe.cpp delete mode 100644 src/sat/cadical/cadical_profile.cpp delete mode 100644 src/sat/cadical/cadical_proof.cpp delete mode 100644 src/sat/cadical/cadical_propagate.cpp delete mode 100644 src/sat/cadical/cadical_queue.cpp delete mode 100644 src/sat/cadical/cadical_random.cpp delete mode 100644 src/sat/cadical/cadical_reap.cpp delete mode 100644 src/sat/cadical/cadical_reduce.cpp delete mode 100644 src/sat/cadical/cadical_rephase.cpp delete mode 100644 src/sat/cadical/cadical_report.cpp delete mode 100644 src/sat/cadical/cadical_resources.cpp delete mode 100644 src/sat/cadical/cadical_restart.cpp delete mode 100644 src/sat/cadical/cadical_restore.cpp delete mode 100644 src/sat/cadical/cadical_score.cpp delete mode 100644 src/sat/cadical/cadical_shrink.cpp delete mode 100644 src/sat/cadical/cadical_signal.cpp delete mode 100644 src/sat/cadical/cadical_solution.cpp delete mode 100644 src/sat/cadical/cadical_solver.cpp delete mode 100644 src/sat/cadical/cadical_stable.cpp delete mode 100644 src/sat/cadical/cadical_stats.cpp delete mode 100644 src/sat/cadical/cadical_subsume.cpp delete mode 100644 src/sat/cadical/cadical_sweep.cpp delete mode 100644 src/sat/cadical/cadical_terminal.cpp delete mode 100644 src/sat/cadical/cadical_ternary.cpp delete mode 100644 src/sat/cadical/cadical_tier.cpp delete mode 100644 src/sat/cadical/cadical_transred.cpp delete mode 100644 src/sat/cadical/cadical_unstable.cpp delete mode 100644 src/sat/cadical/cadical_util.cpp delete mode 100644 src/sat/cadical/cadical_var.cpp delete mode 100644 src/sat/cadical/cadical_veripbtracer.cpp delete mode 100644 src/sat/cadical/cadical_version.cpp delete mode 100644 src/sat/cadical/cadical_vivify.cpp delete mode 100644 src/sat/cadical/cadical_walk.cpp delete mode 100644 src/sat/cadical/cadical_watch.cpp delete mode 100644 src/sat/cadical/ccadical.h delete mode 100644 src/sat/cadical/checker.hpp delete mode 100644 src/sat/cadical/clause.hpp delete mode 100644 src/sat/cadical/config.hpp delete mode 100644 src/sat/cadical/congruence.hpp delete mode 100644 src/sat/cadical/contract.hpp delete mode 100644 src/sat/cadical/cover.hpp delete mode 100644 src/sat/cadical/decompose.hpp delete mode 100644 src/sat/cadical/delay.hpp delete mode 100644 src/sat/cadical/drattracer.hpp delete mode 100644 src/sat/cadical/elim.hpp delete mode 100644 src/sat/cadical/ema.hpp delete mode 100644 src/sat/cadical/external.hpp delete mode 100644 src/sat/cadical/factor.hpp delete mode 100644 src/sat/cadical/file.hpp delete mode 100644 src/sat/cadical/flags.hpp delete mode 100644 src/sat/cadical/format.hpp delete mode 100644 src/sat/cadical/frattracer.hpp delete mode 100644 src/sat/cadical/global.h delete mode 100644 src/sat/cadical/heap.hpp delete mode 100644 src/sat/cadical/idruptracer.hpp delete mode 100644 src/sat/cadical/instantiate.hpp delete mode 100644 src/sat/cadical/internal.hpp delete mode 100644 src/sat/cadical/inttypes.hpp delete mode 100644 src/sat/cadical/ipasir.h delete mode 100644 src/sat/cadical/kitten.h delete mode 100644 src/sat/cadical/level.hpp delete mode 100644 src/sat/cadical/lidruptracer.hpp delete mode 100644 src/sat/cadical/limit.hpp delete mode 100644 src/sat/cadical/logging.hpp delete mode 100644 src/sat/cadical/lratchecker.hpp delete mode 100644 src/sat/cadical/lrattracer.hpp delete mode 100644 src/sat/cadical/message.hpp delete mode 100644 src/sat/cadical/module.make delete mode 100644 src/sat/cadical/occs.hpp delete mode 100644 src/sat/cadical/options.hpp delete mode 100644 src/sat/cadical/parse.hpp delete mode 100644 src/sat/cadical/phases.hpp delete mode 100644 src/sat/cadical/profile.hpp delete mode 100644 src/sat/cadical/proof.hpp delete mode 100644 src/sat/cadical/queue.hpp delete mode 100644 src/sat/cadical/radix.hpp delete mode 100644 src/sat/cadical/random.h delete mode 100644 src/sat/cadical/random.hpp delete mode 100644 src/sat/cadical/range.hpp delete mode 100644 src/sat/cadical/reap.hpp delete mode 100644 src/sat/cadical/reluctant.hpp delete mode 100644 src/sat/cadical/resources.hpp delete mode 100644 src/sat/cadical/score.hpp delete mode 100644 src/sat/cadical/signal.hpp delete mode 100644 src/sat/cadical/stack.h delete mode 100644 src/sat/cadical/stats.hpp delete mode 100644 src/sat/cadical/sweep.hpp delete mode 100644 src/sat/cadical/terminal.hpp delete mode 100644 src/sat/cadical/testing.hpp delete mode 100644 src/sat/cadical/tracer.hpp delete mode 100644 src/sat/cadical/util.hpp delete mode 100644 src/sat/cadical/var.hpp delete mode 100644 src/sat/cadical/veripbtracer.hpp delete mode 100644 src/sat/cadical/version.hpp delete mode 100644 src/sat/cadical/vivify.hpp delete mode 100644 src/sat/cadical/watch.hpp diff --git a/Makefile b/Makefile index a546f65fdb..2da81734b6 100644 --- a/Makefile +++ b/Makefile @@ -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 \ diff --git a/abclib.dsp b/abclib.dsp index 921030b34f..7f6af6f819 100644 --- a/abclib.dsp +++ b/abclib.dsp @@ -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" diff --git a/src/base/abci/abc.c b/src/base/abci/abc.c index c126df352a..7ec94e2cb6 100644 --- a/src/base/abci/abc.c +++ b/src/base/abci/abc.c @@ -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; diff --git a/src/sat/cadical/LICENSE b/src/sat/cadical/LICENSE deleted file mode 100644 index ead29d7a34..0000000000 --- a/src/sat/cadical/LICENSE +++ /dev/null @@ -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. diff --git a/src/sat/cadical/VERSION b/src/sat/cadical/VERSION deleted file mode 100644 index a1bba89215..0000000000 --- a/src/sat/cadical/VERSION +++ /dev/null @@ -1 +0,0 @@ -2.2.0-rc1 diff --git a/src/sat/cadical/arena.hpp b/src/sat/cadical/arena.hpp deleted file mode 100644 index 522fce36e6..0000000000 --- a/src/sat/cadical/arena.hpp +++ /dev/null @@ -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 diff --git a/src/sat/cadical/averages.hpp b/src/sat/cadical/averages.hpp deleted file mode 100644 index 3ebdcddfaa..0000000000 --- a/src/sat/cadical/averages.hpp +++ /dev/null @@ -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 diff --git a/src/sat/cadical/bins.hpp b/src/sat/cadical/bins.hpp deleted file mode 100644 index aeefeceeb1..0000000000 --- a/src/sat/cadical/bins.hpp +++ /dev/null @@ -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 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 diff --git a/src/sat/cadical/block.hpp b/src/sat/cadical/block.hpp deleted file mode 100644 index 202355f825..0000000000 --- a/src/sat/cadical/block.hpp +++ /dev/null @@ -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 BlockSchedule; - -class Blocker { - - friend struct Internal; - - vector candidates; - vector 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 diff --git a/src/sat/cadical/cadical.hpp b/src/sat/cadical/cadical.hpp deleted file mode 100644 index c9ed413328..0000000000 --- a/src/sat/cadical/cadical.hpp +++ /dev/null @@ -1,1351 +0,0 @@ -#ifndef _cadical_hpp_INCLUDED -#define _cadical_hpp_INCLUDED - -#include "global.h" - -#include -#include -#include - -ABC_NAMESPACE_CXX_HEADER_START - -namespace CaDiCaL { - -/*========================================================================*/ - -// This provides the actual API of the CaDiCaL solver, which is implemented -// in the class 'Solver' below. Beside its constructor and destructor most -// important is the IPASIR part which you can find between 'BEGIN IPASIR' -// and 'END IPASIR' comments below. The following '[Example]' below might -// also be a good starting point to understand the API. - -/*========================================================================*/ - -// The SAT competition standardized the exit code of SAT solvers to the -// following which then is also used return code for 'solve' functions. -// In the following example we use those constants for brevity though. - -enum Status { - SATISFIABLE = 10, - UNSATISFIABLE = 20, - UNKNOWN = 0, -}; - -/*========================================================================*/ - -// [Example] -// -// The internal solver state follows the IPASIR API model used in the -// incremental track of the SAT competition. State transitions are -// triggered by member function calls, declared and described below. -// -// Consider the following code (from 'test/api/example.cpp') of API usage: -// -// CaDiCaL::Solver * solver = new CaDiCaL::Solver; -// -// // ------------------------------------------------------------------ -// // Encode Problem and check without assumptions. -// -// enum { TIE = 1, SHIRT = 2 }; -// -// solver->add (-TIE), solver->add (SHIRT), solver->add (0); -// solver->add (TIE), solver->add (SHIRT), solver->add (0); -// solver->add (-TIE), solver->add (-SHIRT), solver->add (0); -// -// int res = solver->solve (); // Solve instance. -// CADICAL_assert (res == 10); // Check it is 'SATISFIABLE'. -// -// res = solver->val (TIE); // Obtain assignment of 'TIE'. -// CADICAL_assert (res < 0); // Check 'TIE' assigned to 'false'. -// -// res = solver->val (SHIRT); // Obtain assignment of 'SHIRT'. -// CADICAL_assert (res > 0); // Check 'SHIRT' assigned to 'true'. -// -// // ------------------------------------------------------------------ -// // Incrementally solve again under one assumption. -// -// solver->assume (TIE); // Now force 'TIE' to true. -// -// res = solver->solve (); // Solve again incrementally. -// CADICAL_assert (res == 20); // Check it is 'UNSATISFIABLE'. -// -// res = solver->failed (TIE); // Check 'TIE' responsible. -// CADICAL_assert (res); // Yes, 'TIE' in core. -// -// res = solver->failed (SHIRT); // Check 'SHIRT' responsible. -// CADICAL_assert (!res); // No, 'SHIRT' not in core. -// -// // ------------------------------------------------------------------ -// // Incrementally solve once more under another assumption. -// -// solver->assume (-SHIRT); // Now force 'SHIRT' to false. -// -// res = solver->solve (); // Solve again incrementally. -// CADICAL_assert (res == 20); // Check it is 'UNSATISFIABLE'. -// -// res = solver->failed (TIE); // Check 'TIE' responsible. -// CADICAL_assert (!res); // No, 'TIE' not in core. -// -// res = solver->failed (-SHIRT); // Check '!SHIRT' responsible. -// CADICAL_assert (res); // Yes, '!SHIRT' in core. -// -// // ------------------------------------------------------------------ -// -// delete solver; - -/*========================================================================*/ - -// [States and Transitions] -// -// Compared to IPASIR we also use an 'ADDING' state in which the solver -// stays while adding non-zero literals until the clause is completed -// through adding a zero literal. The additional 'INITIALIZING', -// 'CONFIGURING' and 'DELETING' states are also not part of IPASIR but also -// useful for testing and debugging. -// -// We have the following transitions which are all synchronous except for -// the reentrant 'terminate' call: -// -// new -// INITIALIZING --------------------------> CONFIGURING -// -// set / trace -// CONFIGURING --------------------------> CONFIGURING -// -// add (non zero literal) -// VALID --------------------------> ADDING -// -// add (zero literal) -// VALID --------------------------> STEADY -// -// assume (non zero literal) -// READY --------------------------> STEADY -// -// solve -// READY --------------------------> SOLVING -// -// (internal) -// SOLVING --------------------------> SOLVED -// -// val (non zero literal) -// SATISFIED --------------------------> SATISFIED -// -// failed (non zero literal) -// UNSATISFIED --------------------------> UNSATISFIED -// -// implied (non zero literal) -// INCONCLUSIVE --------------------------> INCONCLUSIVE -// -// delete -// VALID --------------------------> DELETING -// -// where -// -// SOLVED = SATISFIED | UNSATISFIED | INCONCLUSIVE -// READY = CONFIGURING | STEADY | SOLVED -// VALID = READY | ADDING -// INVALID = INITIALIZING | DELETING -// -// The 'SOLVING' state is only visible in different contexts, i.e., from -// another thread or from a signal handler. It is used to implement -// 'terminate'. Here is the only asynchronous transition: -// -// terminate (asynchronously) -// SOLVING -------------------------> STEADY -// -// The important behaviour to remember is that adding, assuming or -// constraining a literal (immediately) destroys the satisfying assignment -// in the 'SATISFIED' state and vice versa resets all assumptions in the -// 'UNSATISFIED' state. This is exactly the behaviour required by the IPASIR -// interface. -// -// Furthermore, the model can only be queried through 'val' in the -// 'SATISFIED' state, while extracting failed assumptions with 'failed' only -// in the 'UNSATISFIED' state. Solving can only be started in the 'STEADY ' -// or 'CONFIGURING' state or after the previous call to 'solve' yielded an -// 'INCONCLUSIVE , 'SATISFIED' or 'UNSATISFIED' state. -// -// All literals have to be valid literals too, i.e., 32-bit integers -// different from 'INT_MIN'. If any of these requirements is violated the -// solver aborts with an 'API contract violation' message. -// -// HINT: If you do not understand why a contract is violated you can run -// 'mobical' on the failing API call trace. Point the environment variable -// 'CADICAL_API_TRACE' to the file where you want to save the trace during -// execution of your program linking against the library. You probably need -// for 'mobical' to use the option '--do-not-enforce-contracts' though to -// force running into the same contract violation. -// -// Additional API calls (like 'freeze' and 'melt') do not change the state -// of the solver and are all described below. - -/*========================================================================*/ - -// States are represented by a bit-set in order to combine them. - -enum State { - INITIALIZING = 1, // during initialization (invalid) - CONFIGURING = 2, // configure options (with 'set') - STEADY = 4, // ready to call 'solve' - ADDING = 8, // adding clause literals (zero missing) - SOLVING = 16, // while solving (within 'solve') - SATISFIED = 32, // satisfiable allows 'val' - UNSATISFIED = 64, // unsatisfiable allows 'failed' - DELETING = 128, // during and after deletion (invalid) - INCONCLUSIVE = 256, // unknown allows 'implied' - - // These combined states are used to check contracts. - - READY = CONFIGURING | STEADY | SATISFIED | UNSATISFIED | INCONCLUSIVE, - VALID = READY | ADDING, - INVALID = INITIALIZING | DELETING -}; - -/*------------------------------------------------------------------------*/ - -// Opaque classes needed in the API and declared in the same namespace. - -class File; -class Testing; -struct Internal; -struct External; - -/*------------------------------------------------------------------------*/ - -// Forward declaration of call-back classes. See bottom of this file. - -class Learner; -class FixedAssignmentListener; -class Terminator; -class ClauseIterator; -class WitnessIterator; -class ExternalPropagator; -class Tracer; -struct InternalTracer; -class FileTracer; -class StatTracer; - -/*------------------------------------------------------------------------*/ - -class Solver { - -public: - // ====== BEGIN IPASIR =================================================== - - // This section implements the corresponding IPASIR functionality. - - Solver (); - ~Solver (); - - static const char *signature (); // name of this library - - // Core functionality as in the IPASIR incremental SAT solver interface. - // (recall 'READY = CONFIGURING | STEADY | SATISFIED | UNSATISFIED'). - // Further note that 'lit' is required to be different from 'INT_MIN' and - // different from '0' except for 'add'. - - // Add valid literal to clause or zero to terminate clause. - // - // require (VALID) // recall 'VALID = READY | ADDING' - // if (lit) ensure (ADDING) // and thus VALID but not READY - // if (!lit) ensure (STEADY ) // and thus READY - // - void add (int lit); - - // Here are functions simplifying clause addition. The given literals - // should all be valid (different from 'INT_MIN' and different from '0'). - // - // require (VALID) - // ensure (STEADY ) - // - void clause (int); // Add unit clause. - void clause (int, int); // Add binary clause. - void clause (int, int, int); // Add ternary clause. - void clause (int, int, int, int); // Add quaternary clause. - void clause (int, int, int, int, int); // Add quinternary clause. - void clause (const std::vector &); // Add literal vector as clause. - void clause (const int *, size_t); // Add literal array as clause. - - // This function can be used to check if the formula is already - // inconsistent (contains the empty clause or was proven to be - // root-level unsatisfiable). - - bool inconsistent (); - - // Assume valid non zero literal for next call to 'solve'. These - // assumptions are reset after the call to 'solve' as well as after - // returning from 'simplify' and 'lookahead. - // - // require (READY) - // ensure (STEADY ) - // - void assume (int lit); - - // Try to solve the current formula. Returns - // - // 0 = UNKNOWN (limit reached or interrupted through 'terminate') - // 10 = SATISFIABLE - // 20 = UNSATISFIABLE - // - // require (READY) - // ensure (INCONCLUSIVE | SATISFIED | UNSATISFIED) - // - // Note, that while in this call the solver actually transitions to state - // 'SOLVING', which however is only visible from a different context, - // i.e., from a different thread or from a signal handler. Only right - // before returning from this call it goes into a 'READY' state. - // - int solve (); - - // Get value (-lit=false, lit=true) of valid non-zero literal. - // - // require (SATISFIED) - // ensure (SATISFIED) - // - int val (int lit); - - // Try to flip the value of the given literal without falsifying the - // formula. Returns 'true' if this was successful. Otherwise the model is - // not changed and 'false' is returned. If a literal was eliminated or - // substituted flipping will fail on that literal and in particular the - // solver will not taint it nor restore any clauses. - // - // The 'flip' function can only flip the value of a variables not acting - // as witness on the reconstruction stack. - // - // As a side effect of calling this function first all assigned variables - // are propagated again without using blocking literal. Thus the first - // call to this function after obtaining a model adds a substantial - // overhead. Subsequent calls will not need to properly propagate again. - // - // Furthermore if the reconstruction stack is non-empty and has been - // traversed to reconstruct a full extended model for eliminated - // variables (and to satisfy removed blocked clauses), the values of these - // witness variables obtained via 'val' before become invalid. The user - // thus will need to call 'val' again after calling 'flip' which will - // trigger then a traversal of the reconstruction stack. - // - // So try to avoid mixing 'flip' and 'val' (for efficiency only). - // Further, this functionality is currently not supported in the presence - // of an external propagator. - // - // require (SATISFIED) - // ensure (SATISFIED) - // - bool flip (int lit); - - // Same as 'flip' without actually flipping it. This functionality is - // currently not supported in the presence of an external propagator. - // - // require (SATISFIED) - // ensure (SATISFIED) - // - bool flippable (int lit); - - // Determine whether the valid non-zero literal is in the core. - // Returns 'true' if the literal is in the core and 'false' otherwise. - // Note that the core does not have to be minimal. - // - // require (UNSATISFIED) - // ensure (UNSATISFIED) - // - bool failed (int lit); - - // Add call-back which is checked regularly for termination. There can - // only be one terminator connected. If a second (non-zero) one is added - // the first one is implicitly disconnected. - // - // require (VALID) - // ensure (VALID) - // - void connect_terminator (Terminator *terminator); - void disconnect_terminator (); - - // Add call-back which allows to export learned clauses. - // - // require (VALID) - // ensure (VALID) - // - void connect_learner (Learner *learner); - void disconnect_learner (); - - // ====== END IPASIR ===================================================== - - // Add call-back which allows to observe when a variable is fixed. - // - // require (VALID) - // ensure (VALID) - // - void connect_fixed_listener (FixedAssignmentListener *fixed_listener); - void disconnect_fixed_listener (); - - // ====== BEGIN IPASIR-UP ================================================ - - // Add call-back which allows to learn, propagate and backtrack based on - // external constraints. Only one external propagator can be connected - // and after connection every related variables must be 'observed' (use - // 'add_observed_var' function). - // Disconnection of the external propagator resets all the observed - // variables. - // - // require (VALID) - // ensure (VALID) - // - void connect_external_propagator (ExternalPropagator *propagator); - void disconnect_external_propagator (); - - // Mark as 'observed' those variables that are relevant to the external - // propagator. External propagation, clause addition during search and - // notifications are all over these observed variables. - // A variable can not be observed without having an external propagator - // connected. Observed variables are "frozen" internally, and so - // inprocessing will not consider them as candidates for elimination. - // An observed variable is allowed to be a fresh variable and it can be - // added also during solving. - // - // require (VALID_OR_SOLVING) - // ensure (VALID_OR_SOLVING) - // - void add_observed_var (int var); - - // Removes the 'observed' flag from the given variable. A variable can be - // set unobserved only between solve calls, not during it (to guarantee - // that no yet unexplained external propagation involves it). - // - // require (VALID) - // ensure (VALID) - // - void remove_observed_var (int var); - - // Removes all the 'observed' flags from the variables. Disconnecting the - // propagator invokes this step as well. - // - // require (VALID) - // ensure (VALID) - // - void reset_observed_vars (); - - // Get reason of valid observed literal (true = it is an observed variable - // and it got assigned by a decision during the CDCL loop. Otherwise: - // false. - // - // require (VALID_OR_SOLVING) - // ensure (VALID_OR_SOLVING) - // - bool is_decision (int lit); - - // Force solve to backtrack to certain decision level. Can be called only - // during 'cb_decide' of a connected External Propagator. - // Invoking in any other time will not have an effect. - // If the call had an effect, the External Propagator will be notified - // about the backtrack via 'notify_backtrack'. - // - // require (SOLVING) - // ensure (SOLVING) - // - void force_backtrack (size_t new_level); - - // ====== END IPASIR-UP ================================================== - - //------------------------------------------------------------------------ - // Adds a literal to the constraint clause. Same functionality as 'add' - // but the clause only exists for the next call to solve (same lifetime as - // assumptions). Only one constraint may exists at a time. A new - // constraint replaces the old. The main application of this functionality - // is the model checking algorithm IC3. See our FMCAD'21 paper - // [FroleyksBiere-FMCAD'19] for more details. - // - // Add valid literal to the constraint clause or zero to terminate it. - // - // require (VALID) // recall 'VALID = READY | - // ADDING' if (lit) ensure (ADDING) // and thus VALID but not - // READY if (!lit) && !adding_clause ensure (STEADY ) // and thus READY - // - void constrain (int lit); - - // Determine whether the constraint was used to proof the - // unsatisfiability. Note that the formula might still be unsatisfiable - // without the constraint. - // - // require (UNSATISFIED) - // ensure (UNSATISFIED) - // - bool constraint_failed (); - - // Collects a subset of those literals that are implied by unit - // propagation by assuming the currently defined (potentially empty) set - // of assumptions (see IPASIR assume(lit)) function. In case unit - // propgation over the defined set of assumptions (or over the clause - // database on its own) leads to conflict, the function returns 20 and the - // content of 'implicants' is undefined. In case unit propagation happens - // to satisfy all the clauses (not probable, but not impossible), the - // function returns 10 and 'implicants' is a solution of the current - // formula under the current assumptions (after solution reconstruction). - // In any other case, the function returns 0 (indicating 'UNKNOWN') and - // 'implicants' lists the non-conflicting current value of the trail. - - // Returns - // - // 0 = UNKNOWN (unit propagation did not lead to a conflict nor to a - // complete assignment, or limit reached or interrupted - // through 'terminate') - // 10 = SATISFIABLE - // 20 = UNSATISFIABLE - - // require (READY) - // ensure (INCONCLUSIVE | SATISFIED | UNSATISFIED) - int propagate (); - - // - // require (INCONCLUSIVE) - // ensure (INCONCLUSIVE) - // - void implied (std::vector &implicants); - - //------------------------------------------------------------------------ - // This function determines a good splitting literal. The result can be - // zero if the formula is proven to be satisfiable or unsatisfiable. This - // can then be checked by 'state ()'. If the formula is empty and - // the function is not able to determine satisfiability also zero is - // returned but the state remains steady. - // - // require (READY) - // ensure (INCONCLUSIVE |SATISFIED|UNSATISFIED) - // - int lookahead (void); - - struct CubesWithStatus { - int status; - std::vector> cubes; - }; - - CubesWithStatus generate_cubes (int, int min_depth = 0); - - void reset_assumptions (); - void reset_constraint (); - - // Return the current state of the solver as defined above. - // - const State &state () const { return _state; } - - // Similar to 'state ()' but using the standard competition exit codes of - // '10' for 'SATISFIABLE', '20' for 'UNSATISFIABLE' and '0' otherwise. - // - int status () const { - if (_state == SATISFIED) - return 10; - else if (_state == UNSATISFIED) - return 20; - else - return 0; - } - - /*----------------------------------------------------------------------*/ - - static const char *version (); // return version string - - /*----------------------------------------------------------------------*/ - // Copy 'this' into a fresh 'other'. The copy procedure is not a deep - // clone, but only copies irredundant clauses and units. It also makes - // sure that witness reconstruction works with the copy as with the - // original formula such that both solvers have the same models. - // Assumptions are not copied. Options however are copied as well as - // flags which remember the current state of variables in preprocessing. - // - // require (READY) // for 'this' - // ensure (READY) // for 'this' - // - // other.require (CONFIGURING) - // other.ensure (CONFIGURING | STEADY ) - // - void copy (Solver &other) const; - - /*----------------------------------------------------------------------*/ - // Variables are usually added and initialized implicitly whenever a - // literal is used as an argument except for the functions 'val', 'fixed', - // 'failed' and 'frozen'. However, the library internally keeps a maximum - // variable index, which can be queried. - // With factor (BVA) the solver might also add new variables. In that case - // the user is required to use this to check which variables are currently - // free before adding new variables of their own. - // The alternative is to reserve variables in batches with - // 'reserve_difference'. Using 'reserve' in combination with any technique - // that could add variables (currently only factor) is not advised. - // - // require (VALID | SOLVING) - // ensure (VALID | SOLVING) - // - int vars (); - - // Increase the maximum variable index explicitly. This function makes - // sure that at least 'min_max_var' variables are initialized. Since it - // might need to reallocate tables, it destroys a satisfying assignment - // and has the same state transition and conditions as 'assume' etc. - // - // require (READY) - // ensure (STEADY ) - // - void reserve (int min_max_var); - - // Increase the maximum variable index by a number of new variables. - // initializes 'number_of_vars' new variables and protects them from - // being used by the solver as extension variables (BVA). - // It returns the new maximum variable index which is the highest - // variable name of the consecutive range of newly reserved variables. - // It has the same state transition and conditions as 'reserve' above. - // - // require (READY) - // ensure (STEADY ) - // - int reserve_difference (int number_of_vars); - -#ifndef CADICAL_NTRACING - //------------------------------------------------------------------------ - // This function can be used to write API calls to a file. The same - // format is used which 'mobical' can read, execute and also shrink - // through delta debugging. - // - // Tracing API calls can also be achieved by using the environment - // variable 'CADICAL_API_TRACE'. That alternative is useful if you do not - // want to change the source code using the solver, e.g., if you only have - // a binary with the solver linked in. However, that method only allows - // to trace one solver instance, while with the following function API - // tracing can be enabled for different solver instances individually. - // - // The solver will flush the file after every trace API call but does not - // close it during deletion. It remains owned by the user of the library. - // - // require (VALID) - // ensure (VALID) - // - void trace_api_calls (FILE *file); -#endif - - //------------------------------------------------------------------------ - // Option handling. - - // Determine whether 'name' is a valid option name. - // - static bool is_valid_option (const char *name); - - // Determine whether 'name' enables a specific preprocessing technique. - // - static bool is_preprocessing_option (const char *name); - - // Determine whether 'arg' is a valid long option of the form '--', - // '--=' or '--no-' similar to 'set_long_option' below. - // Legal values are 'true', 'false', or '[-][e]'. - - static bool is_valid_long_option (const char *arg); - - // Get the current value of the option 'name'. If 'name' is invalid then - // zero is returned. Here '--...' arguments as invalid options. - // - int get (const char *name); - - // Set the default verbose message prefix (default "c "). - // - void prefix (const char *verbose_message_prefix); - - // Explicit version of setting an option. If the option '' exists - // and '' can be parsed then 'true' is returned. If the option value - // is out of range the actual value is computed as the closest (minimum or - // maximum) value possible, but still 'true' is returned. - // - // require (CONFIGURING) - // ensure (CONFIGURING) - // - // Thus options can only bet set right after initialization. - // - bool set (const char *name, int val); - - // This function accepts options in command line syntax: - // - // '--=', '--', or '--no-' - // - // It actually calls the previous 'set' function after parsing 'arg'. The - // same values are expected as for 'is_valid_long_option' above and as - // with 'set' any value outside of the range of legal values for a - // particular option are set to either the minimum or maximum depending on - // which side of the valid interval they lie. - // - // require (CONFIGURING) - // ensure (CONFIGURING) - // - bool set_long_option (const char *arg); - - // Determine whether 'name' is a valid configuration. - // - static bool is_valid_configuration (const char *); - - // Overwrite (some) options with the forced values of the configuration. - // The result is 'true' iff the 'name' is a valid configuration. - // - // require (CONFIGURING) - // ensure (CONFIGURING) - // - bool configure (const char *); - - // Increase preprocessing and inprocessing limits by '10^'. Values - // below '0' are ignored and values above '9' are reduced to '9'. - // - // require (READY) - // ensure (READY) - // - void optimize (int val); - - // Specify search limits, where currently 'name' can be "conflicts", - // "decisions", "preprocessing", or "localsearch". The first two limits - // are unbounded by default. Thus using a negative limit for conflicts or - // decisions switches back to the default of unlimited search (for that - // particular limit). The preprocessing limit determines the number of - // preprocessing rounds, which is zero by default. Similarly, the local - // search limit determines the number of local search rounds (also zero by - // default). As with 'set', the return value denotes whether the limit - // 'name' is valid. These limits are only valid for the next 'solve' or - // 'simplify' call and reset to their default after 'solve' returns (as - // well as overwritten and reset during calls to 'simplify' and - // 'lookahead'). We actually also have an internal "terminate" limit - // which however should only be used for testing and debugging. - // - // require (READY) - // ensure (READY) - // - bool limit (const char *arg, int val); - bool is_valid_limit (const char *arg); - - // The number of currently active variables and clauses can be queried by - // these functions. Variables become active if a clause is added with it. - // They become inactive if they are eliminated or fixed at the root level - // Clauses become inactive if they are satisfied, subsumed, eliminated. - // Redundant clauses are reduced regularly and thus the 'redundant' - // function is less useful. - // - // require (VALID) - // ensure (VALID) - // - int active () const; // Number of active variables. - int64_t redundant () const; // Number of active redundant clauses. - int64_t irredundant () const; // Number of active irredundant clauses. - - //------------------------------------------------------------------------ - // This function executes the given number of preprocessing rounds. It is - // similar to 'solve' with 'limits ("preprocessing", rounds)' except that - // no CDCL nor local search, nor lucky phases are executed. The result - // values are also the same: 0=UNKNOWN, 10=SATISFIABLE, 20=UNSATISFIABLE. - // As 'solve' it resets current assumptions and limits before returning. - // The numbers of rounds should not be negative. If the number of rounds - // is zero only clauses are restored (if necessary) and top level unit - // propagation is performed, which both take some time. - // - // require (READY) - // ensure (INCONCLUSIVE | SATISFIED | UNSATISFIED) - // - int simplify (int rounds = 3); - - //------------------------------------------------------------------------ - // Force termination of 'solve' asynchronously. - // - // require (SOLVING | READY) - // ensure (INCONCLUSIVE ) // actually not immediately (synchronously) - // - void terminate (); - - //------------------------------------------------------------------------ - - // We have the following common reference counting functions, which avoid - // to restore clauses but require substantial user guidance. This was the - // only way to use inprocessing in incremental SAT solving in Lingeling - // (and before in MiniSAT's 'freeze' / 'thaw') and which did not use - // automatic clause restoring. In general this is slower than - // restoring clauses and should not be used. - // - // In essence the user freezes variables which potentially are still - // needed in clauses added or assumptions used after the next 'solve' - // call. As in Lingeling you can freeze a variable multiple times, but - // then have to melt it the same number of times again in order to enable - // variable eliminating on it etc. The arguments can be literals - // (negative indices) but conceptually variables are frozen. - // - // In the old way of doing things without restore you should not use a - // variable incrementally (in 'add' or 'assume'), which was used before - // and potentially could have been eliminated in a previous 'solve' call. - // This can lead to spurious satisfying assignment. In order to check - // this API contract one can use the 'checkfrozen' option. This has the - // drawback that restoring clauses implicitly would fail with a fatal - // error message even if in principle the solver could just restore - // clauses. Thus this option is disabled by default. - // - // See our SAT'19 paper [FazekasBiereScholl-SAT'19] for more details. - // - // require (VALID) - // ensure (VALID) - // - bool frozen (int lit) const; - void freeze (int lit); - void melt (int lit); // Also needs 'require (frozen (lit))'. - - //------------------------------------------------------------------------ - - // Root level assigned variables can be queried with this function. - // It returns '1' if the literal is implied by the formula, '-1' if its - // negation is implied, or '0' if this is unclear at this point. - // - // require (VALID) - // ensure (VALID) - // - int fixed (int lit) const; - - //------------------------------------------------------------------------ - // Force the default decision phase of a variable to a certain value. - // - void phase (int lit); - void unphase (int lit); - - //------------------------------------------------------------------------ - - // Enables clausal proof tracing in DRAT format and returns 'true' if - // successfully opened for writing. Writing proofs has to be enabled - // before calling 'solve', 'add' and 'dimacs', that is in state - // 'CONFIGURING'. Otherwise only partial proofs would be written. - // - // require (CONFIGURING) - // ensure (CONFIGURING) - // - bool trace_proof (FILE *file, const char *name); // Write DRAT proof. - bool trace_proof (const char *path); // Open & write proof. - - // Flushing the proof trace file eventually calls 'fflush' on the actual - // file or pipe and thus if this function returns all the proof steps - // should have been written (with the same guarantees as 'fflush'). - // - // The additional optional argument forces to print the number of addition - // and deletion steps in the proof even if the verbosity level is zero but - // not if quiet is set as well. The default for the stand-alone solver is - // to print this information (in the 'closing proof' section) but for API - // usage of the library we want to stay silent unless explicitly requested - // or verbosity is non-zero (and as explained quiet is not set). - // - // This function can be called multiple times. - // - // require (VALID) - // ensure (VALID) - // - void flush_proof_trace (bool print = false); - - // Close proof trace early. Similar to 'flush' we allow the user to - // control with 'print' in a more fine-grained way whether statistics - // about the size of the written proof file and if compressed on-the-fly - // the number of actual bytes written (including deflation percentage) are - // printed. Before actually closing (or detaching in case of writing to - // '') we check whether 'flush_proof_trace' was called since the - // last time a proof step (addition or deletion) was traced. If this is - // not the case we would call 'flush_proof_trace' with the same 'print' - // argument. - // - // require (VALID) - // ensure (VALID) - // - void close_proof_trace (bool print = false); - - // Enables clausal proof tracing with or without antecedents using - // the Tracer interface defined in 'tracer.hpp' - // - // InternalTracer, StatTracer and FileTracer for internal use - // - // require (CONFIGURING) - // ensure (CONFIGURING) - // - void connect_proof_tracer (Tracer *tracer, bool antecedents, - bool finalize_clauses = false); - void connect_proof_tracer (InternalTracer *tracer, bool antecedents, - bool finalize_clauses = false); - void connect_proof_tracer (StatTracer *tracer, bool antecedents, - bool finalize_clauses = false); - void connect_proof_tracer (FileTracer *tracer, bool antecedents, - bool finalize_clauses = false); - - // Triggers the conclusion of incremental proofs. - // if the solver is SATISFIED it will trigger extend () - // and give the model to the proof tracer through conclude_sat () - // if the solver is UNSATISFIED it will trigger failing () - // which will learn new clauses as explained below: - // In case of failed assumptions will provide a core negated - // as a clause through the proof tracer interface. - // With a failing constraint these can be multiple clauses. - // Then it will trigger a conclude_unsat event with the id(s) - // of the newly learnt clauses or the id of the global conflict. - // In case the solver is in UNKNOWN, it will collect the currently - // entrailed literals and add them to the proof. - // - // require (SATISFIED || UNSATISFIED || UNKNOWN) - // ensure (SATISFIED || UNSATISFIED || UNKNOWN) - // - void conclude (); - - // Disconnect proof tracer. If this is not done before deleting - // the tracer will be deleted. Returns true if successful. - // - // require (VALID) - // ensure (VALID) - // - bool disconnect_proof_tracer (Tracer *tracer); - bool disconnect_proof_tracer (StatTracer *tracer); - bool disconnect_proof_tracer (FileTracer *tracer); - - //------------------------------------------------------------------------ - - static void usage (); // print usage information for long options - - static void configurations (); // print configuration usage options - - // require (!DELETING) - // ensure (!DELETING) - // - void statistics (); // print statistics - void resources (); // print resource usage (time and memory) - - // require (VALID) - // ensure (VALID) - // - void options (); // print current option and value list - - //------------------------------------------------------------------------ - // Traverse irredundant clauses or the extension stack in reverse order. - // - // The return value is false if traversal is aborted early due to one of - // the visitor functions returning false. See description of the - // iterators below for more details on how to use these functions. - // - // require (VALID) - // ensure (VALID) - // - bool traverse_clauses (ClauseIterator &) const; - bool traverse_witnesses_backward (WitnessIterator &) const; - bool traverse_witnesses_forward (WitnessIterator &) const; - - //------------------------------------------------------------------------ - // Files with explicit path argument support compressed input and output - // if appropriate helper functions 'gzip' etc. are available. They are - // called through opening a pipe to an external command. - // - // If the 'strict' argument is zero then the number of variables and - // clauses specified in the DIMACS headers are ignored, i.e., the header - // 'p cnf 0 0' is always legal. If the 'strict' argument is larger '1' - // strict formatting of the header is required, i.e., single spaces - // everywhere and no trailing white space. - // - // Returns zero if successful and otherwise an error message. - // - // require (VALID) - // ensure (VALID) - // - const char *read_dimacs (FILE *file, const char *name, int &vars, - int strict = 1); - - const char *read_dimacs (const char *path, int &vars, int strict = 1); - - // The following routines work the same way but parse both DIMACS and - // INCCNF files (with 'p inccnf' header and 'a ' lines). If the - // parser finds and 'p inccnf' header or cubes then '*incremental' is set - // to true and the cubes are stored in the given vector (each cube - // terminated by a zero). - - const char *read_dimacs (FILE *file, const char *name, int &vars, - int strict, bool &incremental, - std::vector &cubes); - - const char *read_dimacs (const char *path, int &vars, int strict, - bool &incremental, std::vector &cubes); - - //------------------------------------------------------------------------ - // Write current irredundant clauses and all derived unit clauses - // to a file in DIMACS format. Clauses on the extension stack are - // not included, nor any redundant clauses. - // - // The 'min_max_var' parameter gives a lower bound on the number '' - // of variables used in the DIMACS 'p cnf ...' header. - // - // Returns zero if successful and otherwise an error message. - // - // require (VALID) - // ensure (VALID) - // - const char *write_dimacs (const char *path, int min_max_var = 0); - - // The extension stack for reconstruction a solution can be written too. - // - const char *write_extension (const char *path); - - // Print build configuration to a file with prefix 'c '. If the file - // is '' or '' then terminal color codes might be used. - // - static void build (FILE *file, const char *prefix = "c "); - -private: - //==== start of state ==================================================== - - // The solver is in the state ADDING if either the current clause or the - // constraint (or both) is not yet terminated. - bool adding_clause; - bool adding_constraint; - - State _state; // API states as discussed above. - - /*----------------------------------------------------------------------*/ - - // The 'Solver' class is a 'facade' object for 'External'. It exposes the - // public API of 'External' but hides everything else (except for the some - // private functions). It is supposed to make it easier to understand the - // API and use the solver through the API. - - // This approach has the benefit of decoupling this header file from all - // internal data structures, which is particularly useful if the rest of - // the source is not available. For instance if only a CaDiCaL library is - // installed in a system, then only this header file has to be installed - // too, and still allows to compile and link against the library. - - /*----------------------------------------------------------------------*/ - - // More precisely the CaDiCaL code is split into three layers: - // - // Solver: facade object providing the actual API of the solver - // External: communication layer between 'Solver' and 'Internal' - // Internal: the actual solver code - // - // The 'External' and 'Internal' layers are declared and implemented in - // the corresponding '{external,internal}.{hpp,cpp}' files (as expected), - // while the 'Solver' facade class is defined in 'cadical.hpp' (here) but - // implemented in 'solver.cpp'. The reason for this naming mismatch is, - // that we want to use 'cadical.hpp' for the library header (this header - // file) and call the binary of the stand alone SAT also 'cadical', which - // is more naturally implemented in 'cadical.cpp'. - // - // Separating 'External' from 'Internal' also allows us to map external - // literals to internal literals, which is useful with many fixed or - // eliminated variables (during 'compact' the internal variable range is - // reduced and external variables are remapped). Such an approach is also - // necessary, if we want to use extended resolution in the future (such as - // bounded variable addition). - // - Internal *internal; // Hidden internal solver. - External *external; // Hidden API to internal solver mapping. - - friend class Testing; // Access to 'internal' for testing only! - -#ifndef CADICAL_NTRACING - // The API calls to the solver can be traced by setting the environment - // variable 'CADICAL_API_TRACE' to point to the path of a file to which - // API calls are written. The same format is used which 'mobical' can - // read, execute and also shrink through delta debugging. - // - // The environment variable is read in the constructor and the trace is - // opened for writing and then closed again in the destructor. - // - // Alternatively one case use 'trace_api_calls'. Both - // - bool close_trace_api_file; // Close file if owned by solver it. - FILE *trace_api_file; // Also acts as flag that we are tracing. - - static bool tracing_api_through_environment; - - //===== end of state ==================================================== - - void trace_api_call (const char *) const; - void trace_api_call (const char *, int) const; - void trace_api_call (const char *, const char *) const; - void trace_api_call (const char *, const char *, int) const; -#endif - - void transition_to_steady_state (); - - //------------------------------------------------------------------------ - // Used in the stand alone solver application 'App' and the model based - // tester 'Mobical'. So only these two classes need direct access to the - // otherwise more application specific functions listed here together with - // the internal DIMACS parser. - - friend class App; - friend class Mobical; - friend class Parser; - - // Read solution in competition format for debugging and testing. - // - // require (VALID) - // ensure (VALID) - // - const char *read_solution (const char *path); - - // Cross-compilation with 'MinGW' needs some work-around for 'printf' - // style printing of 64-bit numbers including warning messages. The - // followings lines are copies of similar code in 'inttypes.hpp' but we - // want to keep the 'cadical.hpp' header file stand-alone. - -#ifndef PRINTF_FORMAT -#ifdef __MINGW32__ -#define __USE_MINGW_ANSI_STDIO 1 -#define PRINTF_FORMAT __MINGW_PRINTF_FORMAT -#else -#define PRINTF_FORMAT printf -#endif -#endif - - // This gives warning messages for wrong 'printf' style format string - // usage. Apparently (on 'gcc 9' at least) the first argument is 'this' - // here. - // - // TODO: support for other compilers (beside 'gcc' and 'clang'). - - /* -#define CADICAL_ATTRIBUTE_FORMAT(FORMAT_POSITION, \ - VARIADIC_ARGUMENT_POSITION) \ - __attribute__ ((format (PRINTF_FORMAT, FORMAT_POSITION, \ - VARIADIC_ARGUMENT_POSITION))) - */ -#define CADICAL_ATTRIBUTE_FORMAT(FORMAT_POSITION, VARIADIC_ARGUMENT_POSITION) - - // Messages in a common style. - // - // require (VALID | DELETING) - // ensure (VALID | DELETING) - // - void section (const char *); // print section header - void message (const char *, ...) // ordinary message - CADICAL_ATTRIBUTE_FORMAT (2, 3); - - void message (); // empty line - only prefix - void error (const char *, ...) // produce error message - CADICAL_ATTRIBUTE_FORMAT (2, 3); - - // Explicit verbose level ('section' and 'message' use '0'). - // - // require (VALID | DELETING) - // ensure (VALID | DELETING) - // - void verbose (int level, const char *, ...) - CADICAL_ATTRIBUTE_FORMAT (3, 4); - - // Factoring out common code to both 'read_dimacs' functions above. - // - // require (VALID) - // ensure (VALID) - // - const char *read_dimacs (File *, int &, int strict, bool *incremental = 0, - std::vector * = 0); - - // Factored out common code for 'solve', 'simplify' and 'lookahead'. - // - int call_external_solve_and_check_results (bool preprocess_only); - - //------------------------------------------------------------------------ - // Print DIMACS file to '' for debugging and testing purposes, - // including derived units and assumptions. Since it will print in terms - // of internal literals it is otherwise not really useful. To write a - // DIMACS formula in terms of external variables use 'write_dimacs'. - // - // require (!INITIALIZING) - // ensure (!INITIALIZING) - // - void dump_cnf (); - friend struct DumpCall; // Mobical calls 'dump_cnf' in 'DumpCall::execute' - - /*----------------------------------------------------------------------*/ - - // Used in mobical to test external propagation internally. - // These functions should not be called for any other purposes. - // - ExternalPropagator *get_propagator (); - bool observed (int lit); - bool is_witness (int lit); - - friend struct LemmaCall; - friend struct ObserveCall; - friend struct DisconnectCall; - friend class MockPropagator; -}; - -/*========================================================================*/ - -// Connected terminators are checked for termination regularly. If the -// 'terminate' function of the terminator returns true the solver is -// terminated synchronously as soon it calls this function. - -class Terminator { -public: - virtual ~Terminator () {} - virtual bool terminate () = 0; -}; - -// Connected learners which can be used to export learned clauses. -// The 'learning' can check the size of the learn clause and only if it -// returns true then the individual literals of the learned clause are given -// to the learn through 'learn' one by one terminated by a zero literal. - -class Learner { -public: - virtual ~Learner () {} - virtual bool learning (int size) = 0; - virtual void learn (int lit) = 0; -}; - -// Connected listener gets notified whenever the truth value of a variable -// is fixed (for example during inprocessing or due to some derived unit -// clauses). - -class FixedAssignmentListener { -public: - virtual ~FixedAssignmentListener () {} - - virtual void notify_fixed_assignment (int) = 0; -}; - -/*------------------------------------------------------------------------*/ - -// Allows to connect an external propagator to propagate values to variables -// with an external clause as a reason or to learn new clauses during the -// CDCL loop (without restart). - -class ExternalPropagator { - -public: - bool is_lazy = false; // lazy propagator only checks complete assignments - bool are_reasons_forgettable = - false; // Reason external clauses can be deleted - - virtual ~ExternalPropagator () {} - - // Notify the propagator about assignments to observed variables. - // The notification is not necessarily eager. It usually happens before - // the call of propagator callbacks and when a driving clause is leading - // to an assignment. - // - // virtual void notify_assignment (int lit, bool is_fixed) = 0; - virtual void notify_assignment (const std::vector &lits) = 0; - virtual void notify_new_decision_level () = 0; - virtual void notify_backtrack (size_t new_level) = 0; - - // Check by the external propagator the found complete solution (after - // solution reconstruction). If it returns false, the propagator should - // provide an external clause during the next callback or introduce new - // observed variables during this callback. - // - virtual bool cb_check_found_model (const std::vector &model) = 0; - - // Ask the external propagator for the next decision literal. If it - // returns 0, the solver makes its own choice. - // - virtual int cb_decide () { return 0; }; - - // Ask the external propagator if there is an external propagation to make - // under the current assignment. It returns either a literal to be - // propagated or 0, indicating that there is no external propagation under - // the current assignment. - // - virtual int cb_propagate () { return 0; }; - - // Ask the external propagator for the reason clause of a previous - // external propagation step (done by cb_propagate). The clause must be - // added literal-by-literal closed with a 0. Further, the clause must - // contain the propagated literal. - // - // The clause will be learned as an Irredundant Non-Forgettable Clause - // (see below at 'cb_has_external_clause' more details about it). - // - virtual int cb_add_reason_clause_lit (int propagated_lit) { - (void) propagated_lit; - return 0; - }; - - // The following two functions are used to add external clauses to the - // solver during the CDCL loop. The external clause is added - // literal-by-literal and learned by the solver as an irredundant - // (original) input clause. The clause can be arbitrary, but if it is - // root-satisfied or tautology, the solver will ignore it without learning - // it. Root-falsified literals are eagerly removed from the clause. - // Falsified clauses trigger conflict analysis, propagating clauses - // trigger propagation. In case chrono is 0, the solver backtracks to - // propagate the new literal on the right decision level, otherwise it - // potentially will be an out-of-order assignment on the current level. - // Unit clauses always (unless root-satisfied, see above) trigger - // backtracking (independently from the value of the chrono option and - // independently from being falsified or satisfied or unassigned) to level - // 0. Empty clause (or root falsified clause, see above) makes the problem - // unsat and stops the search immediately. A literal 0 must close the - // clause. - // - // The external propagator indicates that there is a clause to add. - // The parameter of the function allows the user to indicate that how - // 'forgettable' is the external clause. Forgettable clauses are allowed - // to be removed by the SAT solver during clause database reduction. - // However, it is up to the solver to decide when actually the clause is - // deleted. For example, unit clauses, even forgettable ones, will not be - // deleted. In case the clause is not 'forgettable' (the parameter is - // false), the solver considers the clause to be irredundant. - // - // In case the solver produces incremental proofs, these external clauses - // are added to the proof during solving at real-time, i.e., the proof - // checker can ignore them until that point (so added as input clause, but - // input after the query line). - // - // Reason clauses of external propagation steps are assumed to be - // forgettable, parameter 'reason_forgettable' can be used to change it. - // - // Currently, every external clause is expected to be over observed - // (therefore frozen) variables, hence no tainting or restore steps - // are performed upon their addition. This will be changed in later - // versions probably. - // - virtual bool cb_has_external_clause (bool &is_forgettable) = 0; - - // The actual function called to add the external clause. - // - virtual int cb_add_external_clause_lit () = 0; -}; - -/*------------------------------------------------------------------------*/ - -// Allows to traverse all remaining irredundant clauses. Satisfied and -// eliminated clauses are not included, nor any derived units unless such -// a unit literal is frozen. Falsified literals are skipped. If the solver -// is inconsistent only the empty clause is traversed. -// -// If 'clause' returns false traversal aborts early. - -class ClauseIterator { -public: - virtual ~ClauseIterator () {} - virtual bool clause (const std::vector &) = 0; -}; - -/*------------------------------------------------------------------------*/ - -// Allows to traverse all clauses on the extension stack together with their -// witness cubes. If the solver is inconsistent, i.e., an empty clause is -// found and the formula is unsatisfiable, then nothing is traversed. -// -// The clauses traversed in 'traverse_clauses' together with the clauses on -// the extension stack are logically equivalent to the original clauses. -// See our SAT'19 paper for more details. -// -// The witness literals can be used to extend and fix an assignment on the -// remaining clauses to satisfy the clauses on the extension stack too. -// -// All derived units of non-frozen variables are included too. -// -// If 'witness' returns false traversal aborts early. - -class WitnessIterator { -public: - virtual ~WitnessIterator () {} - virtual bool witness (const std::vector &clause, - const std::vector &witness, - int64_t id = 0) = 0; -}; - -/*------------------------------------------------------------------------*/ - -} // namespace CaDiCaL - -ABC_NAMESPACE_CXX_HEADER_END - -#endif diff --git a/src/sat/cadical/cadicalSolver.c b/src/sat/cadical/cadicalSolver.c deleted file mode 100644 index 9caea66052..0000000000 --- a/src/sat/cadical/cadicalSolver.c +++ /dev/null @@ -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 diff --git a/src/sat/cadical/cadicalSolver.h b/src/sat/cadical/cadicalSolver.h deleted file mode 100644 index 0192ada3c0..0000000000 --- a/src/sat/cadical/cadicalSolver.h +++ /dev/null @@ -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 /// -//////////////////////////////////////////////////////////////////////// diff --git a/src/sat/cadical/cadicalTest.c b/src/sat/cadical/cadicalTest.c deleted file mode 100644 index f3919160ba..0000000000 --- a/src/sat/cadical/cadicalTest.c +++ /dev/null @@ -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 diff --git a/src/sat/cadical/cadical_analyze.cpp b/src/sat/cadical/cadical_analyze.cpp deleted file mode 100644 index 74b67fb065..0000000000 --- a/src/sat/cadical/cadical_analyze.cpp +++ /dev/null @@ -1,1285 +0,0 @@ -#include "global.h" - -#include "internal.hpp" - -ABC_NAMESPACE_IMPL_START - -namespace CaDiCaL { - -/*------------------------------------------------------------------------*/ - -// Code for conflict analysis, i.e., to generate the first UIP clause. The -// main function is 'analyze' below. It further uses 'minimize' to minimize -// the first UIP clause, which is in 'minimize.cpp'. An important side -// effect of conflict analysis is to update the decision queue by bumping -// variables. Similarly analyzed clauses are bumped to mark them as active. - -/*------------------------------------------------------------------------*/ - -void Internal::learn_empty_clause () { - CADICAL_assert (!unsat); - build_chain_for_empty (); - LOG ("learned empty clause"); - external->check_learned_empty_clause (); - int64_t id = ++clause_id; - if (proof) { - proof->add_derived_empty_clause (id, lrat_chain); - } - unsat = true; - conflict_id = id; - marked_failed = true; - conclusion.push_back (id); - lrat_chain.clear (); -} - -void Internal::learn_unit_clause (int lit) { - CADICAL_assert (!unsat); - LOG ("learned unit clause %d, stored at position %d", lit, vlit (lit)); - external->check_learned_unit_clause (lit); - int64_t id = ++clause_id; - if (lrat || frat) { - const unsigned uidx = vlit (lit); - unit_clauses (uidx) = id; - } - if (proof) { - proof->add_derived_unit_clause (id, lit, lrat_chain); - } - mark_fixed (lit); -} - -/*------------------------------------------------------------------------*/ - -// Move bumped variables to the front of the (VMTF) decision queue. The -// 'bumped' time stamp is updated accordingly. It is used to determine -// whether the 'queue.assigned' pointer has to be moved in 'unassign'. - -void Internal::bump_queue (int lit) { - CADICAL_assert (opts.bump); - const int idx = vidx (lit); - if (!links[idx].next) - return; - queue.dequeue (links, idx); - queue.enqueue (links, idx); - CADICAL_assert (stats.bumped != INT64_MAX); - btab[idx] = ++stats.bumped; - LOG ("moved to front variable %d and bumped to %" PRId64 "", idx, - btab[idx]); - if (!vals[idx]) - update_queue_unassigned (idx); -} - -/*------------------------------------------------------------------------*/ - -// It would be better to use 'isinf' but there are some historical issues -// with this function. On some platforms it is a macro and even for C++ it -// changed the scope (in pre 5.0 gcc) from '::isinf' to 'std::isinf'. I do -// not want to worry about these strange incompatibilities and thus use the -// same trick as in older solvers (since the MiniSAT team invented EVSIDS) -// and simply put a hard limit here. It is less elegant but easy to port. - -static inline bool evsids_limit_hit (double score) { - CADICAL_assert (sizeof (score) == 8); // assume IEEE 754 64-bit double - return score > 1e150; // MAX_DOUBLE is around 1.8e308 -} - -/*------------------------------------------------------------------------*/ - -// Classical exponential VSIDS as pioneered by MiniSAT. - -void Internal::rescale_variable_scores () { - stats.rescored++; - double divider = score_inc; - for (auto idx : vars) { - const double tmp = stab[idx]; - if (tmp > divider) - divider = tmp; - } - PHASE ("rescore", stats.rescored, "rescoring %d variable scores by 1/%g", - max_var, divider); - CADICAL_assert (divider > 0); - double factor = 1.0 / divider; - for (auto idx : vars) - stab[idx] *= factor; - score_inc *= factor; - PHASE ("rescore", stats.rescored, - "new score increment %g after %" PRId64 " conflicts", score_inc, - stats.conflicts); -} - -void Internal::bump_variable_score (int lit) { - CADICAL_assert (opts.bump); - int idx = vidx (lit); - double old_score = score (idx); - CADICAL_assert (!evsids_limit_hit (old_score)); - double new_score = old_score + score_inc; - if (evsids_limit_hit (new_score)) { - LOG ("bumping %g score of %d hits EVSIDS score limit", old_score, idx); - rescale_variable_scores (); - old_score = score (idx); - CADICAL_assert (!evsids_limit_hit (old_score)); - new_score = old_score + score_inc; - } - CADICAL_assert (!evsids_limit_hit (new_score)); - LOG ("new %g score of %d", new_score, idx); - score (idx) = new_score; - if (scores.contains (idx)) - scores.update (idx); -} - -// Important variables recently used in conflict analysis are 'bumped', - -void Internal::bump_variable (int lit) { - if (use_scores ()) - bump_variable_score (lit); - else - bump_queue (lit); -} - -// After every conflict the variable score increment is increased by a -// factor (if we are currently using scores). - -void Internal::bump_variable_score_inc () { - CADICAL_assert (use_scores ()); - CADICAL_assert (!evsids_limit_hit (score_inc)); - double f = 1e3 / opts.scorefactor; - double new_score_inc = score_inc * f; - if (evsids_limit_hit (new_score_inc)) { - LOG ("bumping %g increment by %g hits EVSIDS score limit", score_inc, - f); - rescale_variable_scores (); - new_score_inc = score_inc * f; - } - CADICAL_assert (!evsids_limit_hit (new_score_inc)); - LOG ("bumped score increment from %g to %g with factor %g", score_inc, - new_score_inc, f); - score_inc = new_score_inc; -} - -/*------------------------------------------------------------------------*/ - -struct analyze_bumped_rank { - Internal *internal; - analyze_bumped_rank (Internal *i) : internal (i) {} - typedef uint64_t Type; - Type operator() (const int &a) const { return internal->bumped (a); } -}; - -struct analyze_bumped_smaller { - Internal *internal; - analyze_bumped_smaller (Internal *i) : internal (i) {} - bool operator() (const int &a, const int &b) const { - const auto s = analyze_bumped_rank (internal) (a); - const auto t = analyze_bumped_rank (internal) (b); - return s < t; - } -}; - -/*------------------------------------------------------------------------*/ - -void Internal::bump_variables () { - - CADICAL_assert (opts.bump); - - START (bump); - - if (!use_scores ()) { - - // Variables are bumped in the order they are in the current decision - // queue. This maintains relative order between bumped variables in - // the queue and seems to work best. We also experimented with - // focusing on variables of the last decision level, but results were - // mixed. - - MSORT (opts.radixsortlim, analyzed.begin (), analyzed.end (), - analyze_bumped_rank (this), analyze_bumped_smaller (this)); - } - - for (const auto &lit : analyzed) - bump_variable (lit); - - if (use_scores ()) - bump_variable_score_inc (); - - STOP (bump); -} - -/*------------------------------------------------------------------------*/ - -// We use the glue time stamp table 'gtab' for fast glue computation. - -int Internal::recompute_glue (Clause *c) { - int res = 0; - const int64_t stamp = ++stats.recomputed; - for (const auto &lit : *c) { - int level = var (lit).level; - CADICAL_assert (gtab[level] <= stamp); - if (gtab[level] == stamp) - continue; - gtab[level] = stamp; - res++; - } - return res; -} - -// Clauses resolved since the last reduction are marked as 'used', their -// glue is recomputed and they are promoted if the glue shrinks. Note that -// promotion from 'tier3' to 'tier2' will set 'used' to '2'. - -inline void Internal::bump_clause (Clause *c) { - LOG (c, "bumping"); - c->used = max_used; - if (c->hyper) - return; - if (!c->redundant) - return; - int new_glue = recompute_glue (c); - if (new_glue < c->glue) - promote_clause (c, new_glue); - - const size_t glue = - std::min ((size_t) c->glue, stats.used[stable].size () - 1); - ++stats.used[stable][glue]; - ++stats.bump_used[stable]; -} - -void Internal::bump_clause2 (Clause *c) { bump_clause (c); } -/*------------------------------------------------------------------------*/ - -// During conflict analysis literals not seen yet either become part of the -// first unique implication point (UIP) clause (if on lower decision level), -// are dropped (if fixed), or are resolved away (if on the current decision -// level and different from the first UIP). At the same time we update the -// number of seen literals on a decision level. This helps conflict clause -// minimization. The number of seen levels is the glucose level (also -// called 'glue', or 'LBD'). - -inline void Internal::analyze_literal (int lit, int &open, - int &resolvent_size, - int &antecedent_size) { - CADICAL_assert (lit); - Var &v = var (lit); - Flags &f = flags (lit); - - if (!v.level) { - if (f.seen || !lrat) - return; - f.seen = true; - unit_analyzed.push_back (lit); - CADICAL_assert (val (lit) < 0); - int64_t id = unit_id (-lit); - unit_chain.push_back (id); - return; - } - ++antecedent_size; - if (f.seen) - return; - - // before marking as seen, get reason and check for missed unit - - CADICAL_assert (val (lit) < 0); - CADICAL_assert (v.level <= level); - if (v.reason == external_reason) { - CADICAL_assert (!opts.exteagerreasons); - v.reason = learn_external_reason_clause (-lit, 0, true); - if (!v.reason) { // actually a unit - --antecedent_size; - LOG ("%d unit after explanation", -lit); - if (f.seen || !lrat) - return; - f.seen = true; - unit_analyzed.push_back (lit); - CADICAL_assert (val (lit) < 0); - const unsigned uidx = vlit (-lit); - int64_t id = unit_clauses (uidx); - CADICAL_assert (id); - unit_chain.push_back (id); - return; - } - } - - f.seen = true; - analyzed.push_back (lit); - - CADICAL_assert (v.reason != external_reason); - if (v.level < level) - clause.push_back (lit); - Level &l = control[v.level]; - if (!l.seen.count++) { - LOG ("found new level %d contributing to conflict", v.level); - levels.push_back (v.level); - } - if (v.trail < l.seen.trail) - l.seen.trail = v.trail; - ++resolvent_size; - LOG ("analyzed literal %d assigned at level %d", lit, v.level); - if (v.level == level) - open++; -} - -inline void Internal::analyze_reason (int lit, Clause *reason, int &open, - int &resolvent_size, - int &antecedent_size) { - CADICAL_assert (reason); - CADICAL_assert (reason != external_reason); - bump_clause (reason); - if (lrat) - lrat_chain.push_back (reason->id); - for (const auto &other : *reason) - if (other != lit) - analyze_literal (other, open, resolvent_size, antecedent_size); -} - -/*------------------------------------------------------------------------*/ - -// This is an idea which was implicit in MapleCOMSPS 2016 for 'limit = 1'. -// See also the paragraph on 'bumping reason side literals' in their SAT'16 -// paper [LiangGaneshPoupartCzarnecki-SAT'16]. Reason side bumping was -// performed exactly when 'LRB' based decision heuristics was used, which in -// the original version was enabled after 10000 conflicts until a time limit -// of 2500 seconds was reached (half of the competition time limit). The -// Maple / Glucose / MiniSAT evolution winning the SAT race in 2019 made -// the schedule of reason side bumping deterministic, i.e., avoiding a time -// limit, by switching between 'LRB' and 'VSIDS' in an interval of initially -// 30 million propagations, which then is increased geometrically by 10%. - -inline bool Internal::bump_also_reason_literal (int lit) { - CADICAL_assert (lit); - CADICAL_assert (val (lit) < 0); - Flags &f = flags (lit); - if (f.seen) - return false; - const Var &v = var (lit); - if (!v.level) - return false; - f.seen = true; - analyzed.push_back (lit); - LOG ("bumping also reason literal %d assigned at level %d", lit, v.level); - return true; -} - -// We experimented with deeper reason bumping without much success though. - -inline void Internal::bump_also_reason_literals (int lit, int depth_limit, - size_t analyzed_limit) { - CADICAL_assert (lit); - CADICAL_assert (depth_limit > 0); - const Var &v = var (lit); - CADICAL_assert (val (lit)); - if (!v.level) - return; - Clause *reason = v.reason; - if (!reason || reason == external_reason) - return; - stats.ticks.search[stable]++; - for (const auto &other : *reason) { - if (other == lit) - continue; - if (!bump_also_reason_literal (other)) - continue; - if (depth_limit < 2) - continue; - bump_also_reason_literals (-other, depth_limit - 1, analyzed_limit); - if (analyzed.size () > analyzed_limit) - break; - } -} - -inline void Internal::bump_also_all_reason_literals () { - CADICAL_assert (opts.bump); - if (!opts.bumpreason) - return; - if (averages.current.decisions > opts.bumpreasonrate) { - LOG ("decisions per conflict rate %g > limit %d", - (double) averages.current.decisions, opts.bumpreasonrate); - return; - } - if (delay[stable].bumpreasons.limit) { - LOG ("delaying reason bumping %" PRId64 " more times", - delay[stable].bumpreasons.limit); - delay[stable].bumpreasons.limit--; - return; - } - CADICAL_assert (opts.bumpreasondepth > 0); - const int depth_limit = opts.bumpreasondepth + stable; - size_t saved_analyzed = analyzed.size (); - size_t analyzed_limit = saved_analyzed * opts.bumpreasonlimit; - for (const auto &lit : clause) - if (analyzed.size () <= analyzed_limit) - bump_also_reason_literals (-lit, depth_limit, analyzed_limit); - else - break; - if (analyzed.size () > analyzed_limit) { - LOG ("not bumping reason side literals as limit exhausted"); - for (size_t i = saved_analyzed; i != analyzed.size (); i++) { - const int lit = analyzed[i]; - Flags &f = flags (lit); - CADICAL_assert (f.seen); - f.seen = false; - } - delay[stable].bumpreasons.interval++; - analyzed.resize (saved_analyzed); - } else { - LOG ("bumping reasons up to depth %d", opts.bumpreasondepth); - delay[stable].bumpreasons.interval /= 2; - } - LOG ("delay internal %" PRId64, delay[stable].bumpreasons.interval); - delay[stable].bumpreasons.limit = delay[stable].bumpreasons.interval; -} - -/*------------------------------------------------------------------------*/ - -void Internal::clear_unit_analyzed_literals () { - LOG ("clearing %zd unit analyzed literals", unit_analyzed.size ()); - for (const auto &lit : unit_analyzed) { - Flags &f = flags (lit); - CADICAL_assert (f.seen); - CADICAL_assert (!var (lit).level); - f.seen = false; - CADICAL_assert (!f.keep); - CADICAL_assert (!f.poison); - CADICAL_assert (!f.removable); - } - unit_analyzed.clear (); -} - -void Internal::clear_analyzed_literals () { - LOG ("clearing %zd analyzed literals", analyzed.size ()); - for (const auto &lit : analyzed) { - Flags &f = flags (lit); - CADICAL_assert (f.seen); - f.seen = false; - CADICAL_assert (!f.keep); - CADICAL_assert (!f.poison); - CADICAL_assert (!f.removable); - } - analyzed.clear (); -#if 0 // to expensive, even for debugging mode - if (unit_analyzed.size ()) - return; - for (auto idx : vars) { - Flags &f = flags (idx); - CADICAL_assert (!f.seen); - } -#endif -} - -void Internal::clear_analyzed_levels () { - LOG ("clearing %zd analyzed levels", levels.size ()); - for (const auto &l : levels) - if (l < (int) control.size ()) - control[l].reset (); - levels.clear (); -} - -/*------------------------------------------------------------------------*/ - -// 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 analyze_trail_negative_rank { - Internal *internal; - analyze_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 analyze_trail_larger { - Internal *internal; - analyze_trail_larger (Internal *s) : internal (s) {} - bool operator() (const int &a, const int &b) const { - return analyze_trail_negative_rank (internal) (a) < - analyze_trail_negative_rank (internal) (b); - } -}; - -/*------------------------------------------------------------------------*/ - -// Generate new driving clause and compute jump level. - -Clause *Internal::new_driving_clause (const int glue, int &jump) { - - const size_t size = clause.size (); - Clause *res; - - if (!size) { - - jump = 0; - res = 0; - - } else if (size == 1) { - - iterating = true; - jump = 0; - res = 0; - - } else { - - CADICAL_assert (clause.size () > 1); - - // We have to get the last assigned literals into the watch position. - // Sorting all literals with respect to reverse assignment order is - // overkill but seems to get slightly faster run-time. For 'minimize' - // we sort the literals too heuristically along the trail order (so in - // the opposite order) with the hope to hit the recursion limit less - // frequently. Thus sorting effort is doubled here. - // - MSORT (opts.radixsortlim, clause.begin (), clause.end (), - analyze_trail_negative_rank (this), analyze_trail_larger (this)); - - jump = var (clause[1]).level; - res = new_learned_redundant_clause (glue); - res->used = 1 + (glue <= opts.reducetier2glue); - } - - LOG ("jump level %d", jump); - - return res; -} - -/*------------------------------------------------------------------------*/ - -// determine the OTFS level for OTFS. Unlike the find_conflict_level, we do -// not have to fix the clause - -inline int Internal::otfs_find_backtrack_level (int &forced) { - CADICAL_assert (opts.otfs); - int res = 0; - - for (const auto &lit : *conflict) { - const int tmp = var (lit).level; - if (tmp == level) { - forced = lit; - } else if (tmp > res) { - res = tmp; - LOG ("bt level is now %d due to %d", res, lit); - } - } - return res; -} - -/*------------------------------------------------------------------------*/ - -// If chronological backtracking is enabled we need to find the actual -// conflict level and then potentially can also reuse the conflict clause -// as driving clause instead of deriving a redundant new driving clause -// (forcing 'forced') if the number 'count' of literals in conflict assigned -// at the conflict level is exactly one. - -inline int Internal::find_conflict_level (int &forced) { - - CADICAL_assert (conflict); - CADICAL_assert (opts.chrono || opts.otfs || external_prop); - - int res = 0, count = 0; - - forced = 0; - - for (const auto &lit : *conflict) { - const int tmp = var (lit).level; - if (tmp > res) { - res = tmp; - forced = lit; - count = 1; - } else if (tmp == res) { - count++; - if (res == level && count > 1) - break; - } - } - - LOG ("%d literals on actual conflict level %d", count, res); - - const int size = conflict->size; - int *lits = conflict->literals; - - // Move the two highest level literals to the front. - // - for (int i = 0; i < 2; i++) { - - const int lit = lits[i]; - - int highest_position = i; - int highest_literal = lit; - int highest_level = var (highest_literal).level; - - for (int j = i + 1; j < size; j++) { - const int other = lits[j]; - const int tmp = var (other).level; - if (highest_level >= tmp) - continue; - highest_literal = other; - highest_position = j; - highest_level = tmp; - if (highest_level == res) - break; - } - - // No unwatched higher assignment level literal. - // - if (highest_position == i) - continue; - - if (highest_position > 1) { - LOG (conflict, "unwatch %d in", lit); - remove_watch (watches (lit), conflict); - } - - lits[highest_position] = lit; - lits[i] = highest_literal; - - if (highest_position > 1) - watch_literal (highest_literal, lits[!i], conflict); - } - - // Only if the number of highest level literals in the conflict is one - // then we can reuse the conflict clause as driving clause for 'forced'. - // - if (count != 1) - forced = 0; - - return res; -} - -/*------------------------------------------------------------------------*/ - -inline int Internal::determine_actual_backtrack_level (int jump) { - - int res; - - CADICAL_assert (level > jump); - - if (!opts.chrono) { - res = jump; - LOG ("chronological backtracking disabled using jump level %d", res); - } else if (opts.chronoalways) { - stats.chrono++; - res = level - 1; - LOG ("forced chronological backtracking to level %d", res); - } else if (jump >= level - 1) { - res = jump; - LOG ("jump level identical to chronological backtrack level %d", res); - } else if ((size_t) jump < assumptions.size ()) { - res = jump; - LOG ("using jump level %d since it is lower than assumption level %zd", - res, assumptions.size ()); - } else if (level - jump > opts.chronolevelim) { - stats.chrono++; - res = level - 1; - LOG ("back-jumping over %d > %d levels prohibited" - "thus backtracking chronologically to level %d", - level - jump, opts.chronolevelim, res); - } else if (opts.chronoreusetrail) { - int best_idx = 0, best_pos = 0; - - if (use_scores ()) { - for (size_t i = control[jump + 1].trail; i < trail.size (); i++) { - const int idx = abs (trail[i]); - if (best_idx && !score_smaller (this) (best_idx, idx)) - continue; - best_idx = idx; - best_pos = i; - } - LOG ("best variable score %g", score (best_idx)); - } else { - for (size_t i = control[jump + 1].trail; i < trail.size (); i++) { - const int idx = abs (trail[i]); - if (best_idx && bumped (best_idx) >= bumped (idx)) - continue; - best_idx = idx; - best_pos = i; - } - LOG ("best variable bumped %" PRId64 "", bumped (best_idx)); - } - CADICAL_assert (best_idx); - LOG ("best variable %d at trail position %d", best_idx, best_pos); - - // Now find the frame and decision level in the control stack of that - // best variable index. Note that, as in 'reuse_trail', the frame - // 'control[i]' for decision level 'i' contains the trail before that - // decision level, i.e., the decision 'control[i].decision' sits at - // 'control[i].trail' in the trail and we thus have to check the level - // of the control frame one higher than at the result level. - // - res = jump; - while (res < level - 1 && control[res + 1].trail <= best_pos) - res++; - - if (res == jump) - LOG ("default non-chronological back-jumping to level %d", res); - else { - stats.chrono++; - LOG ("chronological backtracking to level %d to reuse trail", res); - } - - } else { - res = jump; - LOG ("non-chronological back-jumping to level %d", res); - } - - return res; -} - -/*------------------------------------------------------------------------*/ - -void Internal::eagerly_subsume_recently_learned_clauses (Clause *c) { - CADICAL_assert (opts.eagersubsume); - LOG (c, "trying eager subsumption with"); - mark (c); - int64_t lim = stats.eagertried + opts.eagersubsumelim; - const auto begin = clauses.begin (); - auto it = clauses.end (); -#ifdef LOGGING - int64_t before = stats.eagersub; -#endif - while (it != begin && stats.eagertried++ <= lim) { - Clause *d = *--it; - if (c == d) - continue; - if (d->garbage) - continue; - if (!d->redundant) - continue; - int needed = c->size; - for (auto &lit : *d) { - if (marked (lit) <= 0) - continue; - if (!--needed) - break; - } - if (needed) - continue; - LOG (d, "eager subsumed"); - stats.eagersub++; - stats.subsumed++; - mark_garbage (d); - } - unmark (c); -#ifdef LOGGING - uint64_t subsumed = stats.eagersub - before; - if (subsumed) - LOG ("eagerly subsumed %" PRIu64 " clauses", subsumed); -#endif -} - -/*------------------------------------------------------------------------*/ - -Clause *Internal::on_the_fly_strengthen (Clause *new_conflict, int uip) { - CADICAL_assert (new_conflict); - CADICAL_assert (new_conflict->size > 2); - LOG (new_conflict, "applying OTFS on lit %d", uip); - auto sorted = std::vector (); - sorted.reserve (new_conflict->size); - CADICAL_assert (sorted.empty ()); - ++stats.otfs.strengthened; - - int *lits = new_conflict->literals; - - CADICAL_assert (lits[0] == uip || lits[1] == uip); - const int other_init = lits[0] ^ lits[1] ^ uip; - - CADICAL_assert (mini_chain.empty ()); - - const int old_size = new_conflict->size; - int new_size = 0; - for (int i = 0; i < old_size; ++i) { - const int other = lits[i]; - sorted.push_back (other); - if (var (other).level) - lits[new_size++] = other; - } - - LOG (new_conflict, "removing all units in"); - - CADICAL_assert (lits[0] == uip || lits[1] == uip); - const int other = lits[0] ^ lits[1] ^ uip; - lits[0] = other; - lits[1] = lits[--new_size]; - LOG (new_conflict, "putting uip at pos 1"); - - if (other_init != other) - remove_watch (watches (other_init), new_conflict); - remove_watch (watches (uip), new_conflict); - - CADICAL_assert (!lrat || lrat_chain.back () == new_conflict->id); - if (lrat) { - CADICAL_assert (!lrat_chain.empty ()); - for (const auto &id : unit_chain) { - mini_chain.push_back (id); - } - const auto end = lrat_chain.rend (); - const auto begin = lrat_chain.rbegin (); - for (auto i = begin; i != end; i++) { - const auto id = *i; - mini_chain.push_back (id); - } - lrat_chain.clear (); - clear_unit_analyzed_literals (); - unit_chain.clear (); - } - CADICAL_assert (unit_analyzed.empty ()); - // sort the clause - { - int highest_pos = 0; - int highest_level = 0; - for (int i = 1; i < new_size; i++) { - const unsigned other = lits[i]; - CADICAL_assert (val (other) < 0); - const int level = var (other).level; - CADICAL_assert (level); - LOG ("checking %d", other); - if (level <= highest_level) - continue; - highest_pos = i; - highest_level = level; - } - LOG ("highest lit is %d", lits[highest_pos]); - if (highest_pos != 1) - swap (lits[1], lits[highest_pos]); - LOG ("removing %d literals", new_conflict->size - new_size); - - if (new_size == 1) { - LOG (new_conflict, "new size = 1, so interrupting"); - CADICAL_assert (!opts.exteagerreasons); - return 0; - } else { - otfs_strengthen_clause (new_conflict, uip, new_size, sorted); - CADICAL_assert (new_size == new_conflict->size); - } - } - - if (other_init != other) - watch_literal (other, lits[1], new_conflict); - else { - update_watch_size (watches (other), lits[1], new_conflict); - } - watch_literal (lits[1], other, new_conflict); - - LOG (new_conflict, "strengthened clause by OTFS"); - sorted.clear (); - - return new_conflict; -} - -/*------------------------------------------------------------------------*/ -inline void Internal::otfs_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' and units. -// -void Internal::otfs_strengthen_clause (Clause *c, int lit, int new_size, - const std::vector &old) { - stats.strengthened++; - CADICAL_assert (c->size > 2); - (void) shrink_clause (c, new_size); - if (proof) { - proof->otfs_strengthen_clause (c, old, mini_chain); - } - if (!c->redundant) { - mark_removed (lit); - } - mini_chain.clear (); - c->used = true; - LOG (c, "strengthened"); - external->check_shrunken_clause (c); -} - -/*------------------------------------------------------------------------*/ - -// If the average number of decisions per conflict (analysis actually so not -// taking OTFS conflicts into account) is high we do not bump reasons. This -// is the function which updates the exponential moving decision rate -// average. - -void Internal::update_decision_rate_average () { - int64_t current = stats.decisions; - int64_t decisions = current - saved_decisions; - UPDATE_AVERAGE (averages.current.decisions, decisions); - saved_decisions = current; -} - -/*------------------------------------------------------------------------*/ - -// This is the main conflict analysis routine. It assumes that a conflict -// was found. Then we derive the 1st UIP clause, optionally minimize it, -// add it as learned clause, and then uses the clause for conflict directed -// back-jumping and flipping the 1st UIP literal. In combination with -// chronological backtracking (see discussion above) the algorithm becomes -// slightly more involved. - -void Internal::analyze () { - - START (analyze); - - CADICAL_assert (conflict); - CADICAL_assert (lrat_chain.empty ()); - CADICAL_assert (unit_chain.empty ()); - CADICAL_assert (unit_analyzed.empty ()); - CADICAL_assert (clause.empty ()); - - // First update moving averages of trail height at conflict. - // - UPDATE_AVERAGE (averages.current.trail.fast, num_assigned); - UPDATE_AVERAGE (averages.current.trail.slow, num_assigned); - update_decision_rate_average (); - - /*----------------------------------------------------------------------*/ - - if (external_prop && !external_prop_is_lazy && opts.exteagerreasons) { - explain_external_propagations (); - } - - if (opts.chrono || external_prop) { - - int forced; - - const int conflict_level = find_conflict_level (forced); - - // In principle we can perform conflict analysis as in non-chronological - // backtracking except if there is only one literal with the maximum - // assignment level in the clause. Then standard conflict analysis is - // unnecessary and we can use the conflict as a driving clause. In the - // pseudo code of the SAT'18 paper on chronological backtracking this - // corresponds to the situation handled in line 4-6 in Alg. 1, except - // that the pseudo code in the paper only backtracks while we eagerly - // assign the single literal on the highest decision level. - - if (forced) { - - CADICAL_assert (forced); - CADICAL_assert (conflict_level > 0); - LOG ("single highest level literal %d", forced); - - // The pseudo code in the SAT'18 paper actually backtracks to the - // 'second highest decision' level, while their code backtracks - // to 'conflict_level-1', which is more in the spirit of chronological - // backtracking anyhow and thus we also do the latter. - // - backtrack (conflict_level - 1); - - // if we are on decision level 0 search assign will learn unit - // so we need a valid chain here (of course if we are not on decision - // level 0 this will not result in a valid chain). - // we can just use build_chain_for_units in propagate - // - build_chain_for_units (forced, conflict, 0); - - LOG ("forcing %d", forced); - search_assign_driving (forced, conflict); - - conflict = 0; - STOP (analyze); - return; - } - - // Backtracking to the conflict level is in the pseudo code in the - // SAT'18 chronological backtracking paper, but not in their actual - // implementation. In principle we do not need to backtrack here. - // However, as a side effect of backtracking to the conflict level we - // set 'level' to the conflict level which then allows us to reuse the - // old 'analyze' code as is. The alternative (which we also tried but - // then abandoned) is to use 'conflict_level' instead of 'level' in the - // analysis, which however requires to pass it to the 'analyze_reason' - // and 'analyze_literal' functions. - // - backtrack (conflict_level); - } - - // Actual conflict on root level, thus formula unsatisfiable. - // - if (!level) { - learn_empty_clause (); - if (external->learner) - external->export_learned_empty_clause (); - STOP (analyze); - return; - } - - /*----------------------------------------------------------------------*/ - - // First derive the 1st UIP clause by going over literals assigned on the - // current decision level. Literals in the conflict are marked as 'seen' - // as well as all literals in reason clauses of already 'seen' literals on - // the current decision level. Thus the outer loop starts with the - // conflict clause as 'reason' and then uses the 'reason' of the next - // seen literal on the trail assigned on the current decision level. - // During this process maintain the number 'open' of seen literals on the - // current decision level with not yet processed 'reason'. As soon 'open' - // drops to one, we have found the first unique implication point. This - // is sound because the topological order in which literals are processed - // follows the assignment order and a more complex algorithm to find - // articulation points is not necessary. - // - Clause *reason = conflict; - LOG (reason, "analyzing conflict"); - - CADICAL_assert (clause.empty ()); - CADICAL_assert (lrat_chain.empty ()); - - const auto &t = &trail; - int i = t->size (); // Start at end-of-trail. - int open = 0; // Seen but not processed on this level. - int uip = 0; // The first UIP literal. - int resolvent_size = 0; // without the uip - int antecedent_size = 1; // with the uip and without unit literals - int conflict_size = 0; // without the uip and without unit literals - int resolved = 0; // number of resolution (0 = clause in CNF) - const bool otfs = opts.otfs; - - for (;;) { - antecedent_size = 1; // for uip - analyze_reason (uip, reason, open, resolvent_size, antecedent_size); - if (resolved == 0) - conflict_size = antecedent_size - 1; - CADICAL_assert (resolvent_size == open + (int) clause.size ()); - - if (otfs && resolved > 0 && antecedent_size > 2 && - resolvent_size < antecedent_size) { - CADICAL_assert (reason != conflict); - LOG (analyzed, "found candidate for OTFS conflict"); - LOG (clause, "found candidate for OTFS conflict"); - LOG (reason, "found candidate (size %d) for OTFS resolvent", - antecedent_size); - const int other = reason->literals[0] ^ reason->literals[1] ^ uip; - CADICAL_assert (other != uip); - reason = on_the_fly_strengthen (reason, uip); - if (opts.bump) - bump_variables (); - - CADICAL_assert (conflict_size); - if (!reason) { - uip = -other; - CADICAL_assert (open == 1); - LOG ("clause is actually unit %d, stopping", -uip); - reverse (begin (mini_chain), end (mini_chain)); - for (auto id : mini_chain) - lrat_chain.push_back (id); - mini_chain.clear (); - clear_analyzed_levels (); - CADICAL_assert (!opts.exteagerreasons); - clause.clear (); - break; - } - CADICAL_assert (conflict_size >= 2); - - if (resolved == 1 && resolvent_size < conflict_size) { - // here both clauses are part of the CNF, so one subsumes the other - otfs_subsume_clause (reason, conflict); - LOG (reason, "changing conflict to"); - --conflict_size; - CADICAL_assert (conflict_size == reason->size); - ++stats.otfs.subsumed; - ++stats.subsumed; - } - - LOG (reason, "changing conflict to"); - conflict = reason; - if (open == 1) { - int forced = 0; - const int conflict_level = otfs_find_backtrack_level (forced); - int new_level = determine_actual_backtrack_level (conflict_level); - UPDATE_AVERAGE (averages.current.level, new_level); - backtrack (new_level); - - LOG ("forcing %d", forced); - search_assign_driving (forced, conflict); - - // Clean up. - // - conflict = 0; - clear_analyzed_literals (); - clear_analyzed_levels (); - clause.clear (); - STOP (analyze); - return; - } - - stats.conflicts++; - - clear_analyzed_literals (); - clear_analyzed_levels (); - clause.clear (); - resolvent_size = 0; - antecedent_size = 1; - resolved = 0; - open = 0; - analyze_reason (0, reason, open, resolvent_size, antecedent_size); - conflict_size = antecedent_size - 1; - CADICAL_assert (open > 1); - } - - ++resolved; - - uip = 0; - while (!uip) { - CADICAL_assert (i > 0); - const int lit = (*t)[--i]; - if (!flags (lit).seen) - continue; - if (var (lit).level == level) - uip = lit; - } - if (!--open) - break; - reason = var (uip).reason; - if (reason == external_reason) { - CADICAL_assert (!opts.exteagerreasons); - reason = learn_external_reason_clause (-uip, 0, true); - var (uip).reason = reason; - } - CADICAL_assert (reason != external_reason); - LOG (reason, "analyzing %d reason", uip); - CADICAL_assert (resolvent_size); - --resolvent_size; - } - LOG ("first UIP %d", uip); - clause.push_back (-uip); - - // Update glue and learned (1st UIP literals) statistics. - // - int size = (int) clause.size (); - const int glue = (int) levels.size () - 1; - LOG (clause, "1st UIP size %d and glue %d clause", size, glue); - UPDATE_AVERAGE (averages.current.glue.fast, glue); - UPDATE_AVERAGE (averages.current.glue.slow, glue); - stats.learned.literals += size; - stats.learned.clauses++; - CADICAL_assert (glue < size); - - // up to this point lrat_chain contains the proof for current clause in - // reversed order. in minimize and shrink the clause is changed and - // therefore lrat_chain has to be extended. Unfortunately we cannot create - // the chain directly during minimization (or shrinking) but afterwards we - // can calculate it pretty easily and even better the same algorithm works - // for both shrinking and minimization. - - // Minimize the 1st UIP clause as pioneered by Niklas Soerensson in - // MiniSAT and described in our joint SAT'09 paper. - // - if (size > 1) { - if (opts.shrink) - shrink_and_minimize_clause (); - else if (opts.minimize) - minimize_clause (); - - size = (int) clause.size (); - - // Update decision heuristics. - // - if (opts.bump) { - bump_also_all_reason_literals (); - bump_variables (); - } - - if (external->learner) - external->export_learned_large_clause (clause); - } else if (external->learner) - external->export_learned_unit_clause (-uip); - - // Update actual size statistics. - // - stats.units += (size == 1); - stats.binaries += (size == 2); - UPDATE_AVERAGE (averages.current.size, size); - - // reverse lrat_chain. We could probably work with reversed iterators - // (views) to be more efficient but we would have to distinguish in proof - // - if (lrat) { - LOG (unit_chain, "unit chain: "); - for (auto id : unit_chain) - lrat_chain.push_back (id); - unit_chain.clear (); - reverse (lrat_chain.begin (), lrat_chain.end ()); - } - - // Determine back-jump level, learn driving clause, backtrack and assign - // flipped 1st UIP literal. - // - int jump; - Clause *driving_clause = new_driving_clause (glue, jump); - UPDATE_AVERAGE (averages.current.jump, jump); - - int new_level = determine_actual_backtrack_level (jump); - UPDATE_AVERAGE (averages.current.level, new_level); - backtrack (new_level); - - // It should hold that (!level <=> size == 1) - // and (!uip <=> size == 0) - // this means either we have already learned a clause => size >= 2 - // in this case we will not learn empty clause or unit here - // or we haven't actually learned a clause in new_driving_clause - // then lrat_chain is still valid and we will learn a unit or empty clause - // - if (uip) { - search_assign_driving (-uip, driving_clause); - } else - learn_empty_clause (); - - if (stable) - reluctant.tick (); // Reluctant has its own 'conflict' counter. - - // Clean up. - // - clear_analyzed_literals (); - clear_unit_analyzed_literals (); - clear_analyzed_levels (); - clause.clear (); - conflict = 0; - - lrat_chain.clear (); - STOP (analyze); - - if (driving_clause && opts.eagersubsume) - eagerly_subsume_recently_learned_clauses (driving_clause); - - if (lim.recompute_tier <= stats.conflicts) - recompute_tier (); -} - -// We wait reporting a learned unit until propagation of that unit is -// completed. Otherwise the 'i' report gives the number of remaining -// variables before propagating the unit (and hides the actual remaining -// variables after propagating it). - -void Internal::iterate () { - iterating = false; - report ('i'); -} - -} // namespace CaDiCaL - -ABC_NAMESPACE_IMPL_END diff --git a/src/sat/cadical/cadical_arena.cpp b/src/sat/cadical/cadical_arena.cpp deleted file mode 100644 index a8a9b183f3..0000000000 --- a/src/sat/cadical/cadical_arena.cpp +++ /dev/null @@ -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 diff --git a/src/sat/cadical/cadical_assume.cpp b/src/sat/cadical/cadical_assume.cpp deleted file mode 100644 index e7d5b79b79..0000000000 --- a/src/sat/cadical/cadical_assume.cpp +++ /dev/null @@ -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 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> constraint_chains; - vector> constraint_clauses; - vector sum_constraints; - vector 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 empty; - vector 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 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 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 diff --git a/src/sat/cadical/cadical_averages.cpp b/src/sat/cadical/cadical_averages.cpp deleted file mode 100644 index d40a98cdb9..0000000000 --- a/src/sat/cadical/cadical_averages.cpp +++ /dev/null @@ -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 diff --git a/src/sat/cadical/cadical_backtrack.cpp b/src/sat/cadical/cadical_backtrack.cpp deleted file mode 100644 index b3c519ba29..0000000000 --- a/src/sat/cadical/cadical_backtrack.cpp +++ /dev/null @@ -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 diff --git a/src/sat/cadical/cadical_backward.cpp b/src/sat/cadical/cadical_backward.cpp deleted file mode 100644 index 5778029a50..0000000000 --- a/src/sat/cadical/cadical_backward.cpp +++ /dev/null @@ -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 diff --git a/src/sat/cadical/cadical_bins.cpp b/src/sat/cadical/cadical_bins.cpp deleted file mode 100644 index fba96f2164..0000000000 --- a/src/sat/cadical/cadical_bins.cpp +++ /dev/null @@ -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 diff --git a/src/sat/cadical/cadical_block.cpp b/src/sat/cadical/cadical_block.cpp deleted file mode 100644 index 12f852f082..0000000000 --- a/src/sat/cadical/cadical_block.cpp +++ /dev/null @@ -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 diff --git a/src/sat/cadical/cadical_ccadical.cpp b/src/sat/cadical/cadical_ccadical.cpp deleted file mode 100644 index ae5c1093f1..0000000000 --- a/src/sat/cadical/cadical_ccadical.cpp +++ /dev/null @@ -1,211 +0,0 @@ -#include "global.h" - -#include "cadical.hpp" - -#include -#include - -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 diff --git a/src/sat/cadical/cadical_checker.cpp b/src/sat/cadical/cadical_checker.cpp deleted file mode 100644 index 63a5f2b10e..0000000000 --- a/src/sat/cadical/cadical_checker.cpp +++ /dev/null @@ -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 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 &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 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 &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 &c, - const vector &) { - 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 &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 &c, - const vector &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 diff --git a/src/sat/cadical/cadical_clause.cpp b/src/sat/cadical/cadical_clause.cpp deleted file mode 100644 index 1c84994a64..0000000000 --- a/src/sat/cadical/cadical_clause.cpp +++ /dev/null @@ -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 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 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 diff --git a/src/sat/cadical/cadical_collect.cpp b/src/sat/cadical/cadical_collect.cpp deleted file mode 100644 index c36e063b2a..0000000000 --- a/src/sat/cadical/cadical_collect.cpp +++ /dev/null @@ -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 diff --git a/src/sat/cadical/cadical_compact.cpp b/src/sat/cadical/cadical_compact.cpp deleted file mode 100644 index 6747218cbf..0000000000 --- a/src/sat/cadical/cadical_compact.cpp +++ /dev/null @@ -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 void map_vector (vector &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 void map2_vector (vector &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 &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 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 diff --git a/src/sat/cadical/cadical_condition.cpp b/src/sat/cadical/cadical_condition.cpp deleted file mode 100644 index 8c2f72cf74..0000000000 --- a/src/sat/cadical/cadical_condition.cpp +++ /dev/null @@ -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 conditional; - - vector 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 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 diff --git a/src/sat/cadical/cadical_config.cpp b/src/sat/cadical/cadical_config.cpp deleted file mode 100644 index d4268790cf..0000000000 --- a/src/sat/cadical/cadical_config.cpp +++ /dev/null @@ -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 diff --git a/src/sat/cadical/cadical_congruence.cpp b/src/sat/cadical/cadical_congruence.cpp deleted file mode 100644 index 8fbceee24e..0000000000 --- a/src/sat/cadical/cadical_congruence.cpp +++ /dev/null @@ -1,7567 +0,0 @@ -#include "global.h" - -#include "congruence.hpp" -#include "internal.hpp" -#include -#include -#include -#include -#include - -ABC_NAMESPACE_IMPL_START - -namespace CaDiCaL { - -Closure::Closure (Internal *i) - : internal (i), table (128, Hash (nonces)) -#ifdef LOGGING - , - fresh_id (internal->clause_id) -#endif -{ -} - -char &Closure::lazy_propagated (int lit) { - return lazy_propagated_idx[internal->vidx (lit)]; -} - -void update_ite_flags (Gate *g) { - int8_t f = g->degenerated_ite; - const int lhs = g->lhs; - const int cond = g->rhs [0]; - const int then_lit = g->rhs[1]; - const int else_lit = g->rhs[2]; - - if (lhs == cond) { - f |= Special_ITE_GATE::NO_NEG_THEN; - f |= Special_ITE_GATE::NO_PLUS_ELSE; - } - if (lhs == -cond) { - f |= Special_ITE_GATE::NO_PLUS_THEN; - f |= Special_ITE_GATE::NO_NEG_ELSE; - } - if (lhs == then_lit) { - f |= Special_ITE_GATE::NO_PLUS_THEN; - f |= Special_ITE_GATE::NO_NEG_THEN; - } - if (lhs == else_lit) { - f |= Special_ITE_GATE::NO_PLUS_ELSE; - f |= Special_ITE_GATE::NO_NEG_ELSE; - } - g->degenerated_ite = f; - CADICAL_assert (lhs != -then_lit); - CADICAL_assert (lhs != -else_lit); - CADICAL_assert (cond != then_lit); - CADICAL_assert (cond != else_lit); - CADICAL_assert (cond != -then_lit); - CADICAL_assert (cond != -else_lit); -} - -void check_correct_ite_flags (const Gate *const g) { -#ifndef CADICAL_NDEBUG - const int8_t f = g->degenerated_ite; - const int lhs = g->lhs; - const int cond = g->rhs [0]; - const int then_lit = g->rhs[1]; - const int else_lit = g->rhs[2]; - CADICAL_assert (g->pos_lhs_ids.size () == 4); - if (g->pos_lhs_ids[0].clause == nullptr) - CADICAL_assert ((f & Special_ITE_GATE::NO_PLUS_THEN)); - if (g->pos_lhs_ids[1].clause == nullptr) - CADICAL_assert (f & Special_ITE_GATE::NO_NEG_THEN); - if (g->pos_lhs_ids[2].clause == nullptr) - CADICAL_assert (f & Special_ITE_GATE::NO_PLUS_ELSE); - if (g->pos_lhs_ids[3].clause == nullptr) - CADICAL_assert (f & Special_ITE_GATE::NO_NEG_ELSE); - if (lhs == cond) { - CADICAL_assert (f & Special_ITE_GATE::NO_NEG_THEN); - CADICAL_assert (f & Special_ITE_GATE::NO_PLUS_ELSE); - } - if (lhs == -cond) { - CADICAL_assert (f & Special_ITE_GATE::NO_PLUS_THEN); - CADICAL_assert (f & Special_ITE_GATE::NO_NEG_ELSE); - } - if (lhs == then_lit) { - CADICAL_assert (f & Special_ITE_GATE::NO_PLUS_THEN); - CADICAL_assert (f & Special_ITE_GATE::NO_NEG_THEN); - } - if (lhs == else_lit) { - CADICAL_assert (f & Special_ITE_GATE::NO_PLUS_ELSE); - CADICAL_assert (f & Special_ITE_GATE::NO_NEG_ELSE); - } - CADICAL_assert (lhs != -then_lit); - CADICAL_assert (lhs != -else_lit); - CADICAL_assert (cond != then_lit); - CADICAL_assert (cond != else_lit); - CADICAL_assert (cond != -then_lit); - CADICAL_assert (cond != -else_lit); -#else - (void)g; -#endif -} - -/*------------------------------------------------------------------------*/ - -static size_t hash_lits (std::array &nonces, - const vector &lits) { - size_t hash = 0; - const auto end_nonces = end (nonces); - const auto begin_nonces = begin (nonces); - auto n = begin_nonces; - for (auto lit : lits) { - hash += lit; - hash *= *n++; - hash = (hash << 4) | (hash >> 60); - if (n == end_nonces) - n = begin_nonces; - } - hash ^= hash >> 32; - return hash; -} - -size_t Hash::operator() (const Gate *const g) const { - CADICAL_assert (hash_lits (nonces, g->rhs) == g->hash); - return g->hash; -} - -bool gate_contains (Gate *g, int lit) { - return find (begin (g->rhs), end (g->rhs), lit) != end (g->rhs); -} -/*------------------------------------------------------------------------*/ -struct compact_binary_rank { - typedef uint64_t Type; - uint64_t operator() (const CompactBinary &a) { - return ((uint64_t) a.lit1 << 32) + a.lit2; - }; -}; - -struct compact_binary_order { - bool operator() (const CompactBinary &a, const CompactBinary &b) { - return compact_binary_rank () (a) < compact_binary_rank () (b); - }; -}; - -bool Closure::find_binary (int lit, int other) const { - const auto offsize = - offsetsize[internal->vlit (lit)]; // in C++17: [offset, size] = - const auto offset = offsize.first; - const auto size = offsize.second; - const auto begin = std::begin (binaries) + offset; - const auto end = std::begin (binaries) + size; - CADICAL_assert (end <= std::end (binaries)); - const CompactBinary target = CompactBinary (nullptr, 0, lit, other); - auto it = std::lower_bound (begin, end, target, compact_binary_order ()); - // search_binary only returns a bool - bool found = (it != end && it->lit1 == lit && it->lit2 == other); - if (found) { - LOG ("found binary [%zd] %d %d", it->id, lit, other); - if (internal->lrat) - lrat_chain.push_back (it->id); - } - return found; -} - -void Closure::extract_binaries () { - if (!internal->opts.congruencebinaries) - return; - START (extractbinaries); - offsetsize.resize (internal->max_var * 2 + 3, make_pair (0, 0)); - - // in kissat this is done during watch clearing. TODO: consider doing this - // too. - for (Clause *c : internal->clauses) { - if (c->garbage) - continue; - if (c->redundant && c->size != 2) - continue; - if (c->size > 2) - continue; - CADICAL_assert (c->size == 2); - const int lit = c->literals[0]; - const int other = c->literals[1]; - const bool already_sorted = - internal->vlit (lit) < internal->vlit (other); - binaries.push_back (CompactBinary (c, c->id, - already_sorted ? lit : other, - already_sorted ? other : lit)); - } - - MSORT (internal->opts.radixsortlim, begin (binaries), end (binaries), - compact_binary_rank (), compact_binary_order ()); - - { - const size_t size = binaries.size (); - size_t i = 0; - while (i < size) { - CompactBinary bin = binaries[i]; - const int lit = bin.lit1; - size_t j = i; - while (j < size && binaries[j].lit1 == lit) { - ++j; - } - CADICAL_assert (j >= i); - CADICAL_assert (j <= size); - offsetsize[internal->vlit (lit)] = make_pair (i, j); - i = j; - } - } - - size_t extracted = 0, already_present = 0, duplicated = 0; - - const size_t size = internal->clauses.size (); - for (size_t i = 0; i < size; ++i) { - Clause *d = internal->clauses[i]; // binary clauses are appended, so - // reallocation possible - if (d->garbage) - continue; - if (d->redundant) - continue; - if (d->size != 3) - continue; - const int *lits = d->literals; - const int a = lits[0]; - const int b = lits[1]; - const int c = lits[2]; // obfuscating d->literals[2] which triggers an error in pedandic mode - if (internal->val (a)) - continue; - if (internal->val (b)) - continue; - if (internal->val (c)) - continue; - int l = 0, k = 0; - if (find_binary (-a, b) || find_binary (-a, c)) { - l = b, k = c; - } else if (find_binary (-b, a) || find_binary (-b, c)) { - l = a, k = c; - } else if (find_binary (-c, a) || find_binary (-c, b)) { - l = a, k = b; - } else - continue; - LOG (d, "strengthening"); - if (!find_binary (l, k)) { - if (internal->lrat) - lrat_chain.push_back (d->id); - add_binary_clause (l, k); - ++extracted; - } else { - ++already_present; - if (internal->lrat) - lrat_chain.clear (); - } - } - lrat_chain.clear (); - - offsetsize.clear (); - - // kissat has code to remove duplicates, which we have already removed - // before starting congruence - MSORT (internal->opts.radixsortlim, begin (binaries), end (binaries), - compact_binary_rank (), compact_binary_order ()); - const size_t new_size = binaries.size (); - { - size_t i = 0; - for (size_t j = 1; j < new_size; ++j) { - CADICAL_assert (i < j); - if (binaries[i].lit1 == binaries[j].lit1 && - binaries[i].lit2 == binaries[j].lit2) { - // subsuming later clause - subsume_clause (binaries[i].clause, - binaries[j].clause); // the local one is specialized - ++duplicated; - } else { - binaries[++i] = binaries[j]; - } - } - CADICAL_assert (i <= new_size); - binaries.resize (i); - } - binaries.clear (); - STOP (extractbinaries); - LOG ("extracted %zu binaries (plus %zu already present and %zu " - "duplicates)", - extracted, already_present, duplicated); -} - -/*------------------------------------------------------------------------*/ -// marking structure for congruence closure, by reference -signed char &Closure::marked (int lit) { - CADICAL_assert (internal->vlit (lit) < marks.size ()); - return marks[internal->vlit (lit)]; -} - -void Closure::unmark_all () { - for (auto lit : internal->analyzed) { - CADICAL_assert (marked (lit)); - marked (lit) = 0; - } - internal->analyzed.clear (); -} - -void Closure::set_mu1_reason (int lit, Clause *c) { - CADICAL_assert (marked (lit) & 1); - LOG (c, "mu1 %d -> %zd", lit, c->id); - mu1_ids[internal->vlit (lit)] = LitClausePair (lit, c); -} - -void Closure::set_mu2_reason (int lit, Clause *c) { - CADICAL_assert (marked (lit) & 2); - if (!internal->lrat) - return; - LOG (c, "mu2 %d -> %zd", lit, c->id); - mu2_ids[internal->vlit (lit)] = LitClausePair (lit, c); -} - -void Closure::set_mu4_reason (int lit, Clause *c) { - CADICAL_assert (marked (lit) & 4); - if (!internal->lrat) - return; - LOG (c, "mu4 %d -> %zd", lit, c->id); - mu4_ids[internal->vlit (lit)] = LitClausePair (lit, c); -} - -LitClausePair Closure::marked_mu1 (int lit) { - return mu1_ids[internal->vlit (lit)]; -} - -LitClausePair Closure::marked_mu2 (int lit) { - return mu2_ids[internal->vlit (lit)]; -} - -LitClausePair Closure::marked_mu4 (int lit) { - return mu4_ids[internal->vlit (lit)]; -} - -struct sort_literals_by_var_rank { - CaDiCaL::Internal *internal; - sort_literals_by_var_rank (Internal *i) : internal (i) {} - - typedef uint64_t Type; - - Type operator() (const int &a) const { return internal->vlit (a); } -}; -struct sort_literals_by_var_rank_except { - CaDiCaL::Internal *internal; - int lhs; - int except; - sort_literals_by_var_rank_except (Internal *i, int my_lhs, int except2) - : internal (i), lhs (my_lhs), except (except2) {} - sort_literals_by_var_rank_except (Internal *i, int my_lhs) - : internal (i), lhs (my_lhs), except (0) {} - typedef uint64_t Type; - Type operator() (const int &a) const { - Type res = 0; - if (abs (a) == abs (except)) - res = 1 - (a > 0); - else if (abs (a) == abs (lhs)) - res = 3 - (a > 0); - else - res = internal->vlit (a) + 2; // probably +2 enough - return ~res; - } -}; - -struct sort_literals_by_var_smaller_except { - CaDiCaL::Internal *internal; - int lhs; - int except; - sort_literals_by_var_smaller_except (Internal *i, int my_lhs, int except2) - : internal (i), lhs (my_lhs), except (except2) {} - sort_literals_by_var_smaller_except (Internal *i, int my_lhs) - : internal (i), lhs (my_lhs), except (0) {} - bool operator() (const int &a, const int &b) const { - return sort_literals_by_var_rank_except (internal, lhs, except) (a) < - sort_literals_by_var_rank_except (internal, lhs, except) (b); - if (abs (a) == abs (except) && abs (b) != abs (except)) - return false; - if (abs (a) != abs (except) && abs (b) == abs (except)) - return true; - if (abs (a) == abs (lhs) && abs (b) != abs (lhs)) - return false; - if (abs (a) != abs (lhs) && abs (b) == abs (lhs)) - return true; - return sort_literals_by_var_rank (internal) (a) > - sort_literals_by_var_rank (internal) (b); - } -}; -struct sort_literals_by_var_smaller { - CaDiCaL::Internal *internal; - sort_literals_by_var_smaller (Internal *i) : internal (i) {} - bool operator() (const int &a, const int &b) const { - return sort_literals_by_var_rank (internal) (a) < - sort_literals_by_var_rank (internal) (b); - } -}; - -void Closure::sort_literals_by_var_except (vector &rhs, int lhs, - int except2) { - MSORT (internal->opts.radixsortlim, begin (rhs), end (rhs), - sort_literals_by_var_rank_except (internal, lhs, except2), - sort_literals_by_var_smaller_except (internal, lhs, except2)); -} -void Closure::sort_literals_by_var (vector &rhs) { - MSORT (internal->opts.radixsortlim, begin (rhs), end (rhs), - sort_literals_by_var_rank (internal), - sort_literals_by_var_smaller (internal)); -} -/*------------------------------------------------------------------------*/ -int &Closure::representative (int lit) { - CADICAL_assert (internal->vlit (lit) < representant.size ()); - return representant[internal->vlit (lit)]; -} -int Closure::representative (int lit) const { - CADICAL_assert (internal->vlit (lit) < representant.size ()); - return representant[internal->vlit (lit)]; -} - -int &Closure::eager_representative (int lit) { - CADICAL_assert (internal->vlit (lit) < eager_representant.size ()); - return eager_representant[internal->vlit (lit)]; -} - -int Closure::eager_representative (int lit) const { - CADICAL_assert (internal->vlit (lit) < eager_representant.size ()); - return eager_representant[internal->vlit (lit)]; -} - -int Closure::find_lrat_representative_with_marks (int lit) { - int res = lit; - int nxt = lit; - do { - res = nxt; - nxt = representative (nxt); - if (nxt != res) { - LOG ("%d has reason %" PRIu64, res, representative_id (res)); - lrat_chain.push_back (representative_id (res)); - } - } while (nxt != res || marked (nxt) || marked (-nxt)); - - return nxt; -} -int Closure::find_representative (int lit) { - int res = lit; - int nxt = lit; - do { - res = nxt; - nxt = representative (nxt); - } while (nxt != res); - - return res; -} - -int Closure::find_representative_and_compress (int lit, bool update_eager) { - LOG ("finding representative of %d", lit); - int res = lit; - int nxt = lit; - int path_length = 0; - do { - res = nxt; - nxt = representative (nxt); - ++path_length; - LOG ("updating %d -> %d", res, nxt); - } while (nxt != res); - - if (path_length > 2) { - LOG ("learning new rewriting from %d to %d (current path length: %d)", - lit, res, path_length); - if (update_eager) - eager_representative (lit) = res; - if (internal->lrat) { - produce_representative_lrat (lit); - Clause *equiv = add_tmp_binary_clause (-lit, res); - - if (equiv) { - representative_id (lit) = equiv->id; - if (update_eager) - eager_representative_id (lit) = equiv->id; - } - } - if (internal->lrat) - lrat_chain.clear (); - } else if (path_length == 2) { - if (update_eager) { - LOG ("updating information %d -> %d in eager", lit, res); - eager_representative (lit) = res; - if (internal->lrat) - eager_representative_id (lit) = representative_id (lit); - CADICAL_assert (!internal->lrat || eager_representative_id (lit)); - } - } - - if (lit != res) { - representative (lit) = res; - } - LOG ("representative of %d is %d", lit, res); - return res; -} - -void Closure::push_lrat_unit (int lit) { - if (!internal->lrat) - return; - CADICAL_assert (internal->val (lit) > 0); - LRAT_ID id = internal->unit_id (lit); - lrat_chain.push_back (id); -} - -int Closure::find_eager_representative (int lit) { - int res = lit; - int nxt = lit; - do { - res = nxt; - nxt = eager_representative (nxt); - } while (nxt != res); - - return res; -} - -int Closure::find_eager_representative_and_compress (int lit) { - if (!internal->lrat) - return find_representative (lit); - int res = lit; - int nxt = lit; - int path_length = 0; - do { - res = nxt; - nxt = eager_representative (nxt); - ++path_length; - } while (nxt != res); - - // CADICAL_assert (res == find_representative (lit)); - // we have to do path compression to support LRAT proofs - if (path_length > 2) { - LOG ("learning new rewriting from %d to %d (current path length: %d)", - lit, res, path_length); - std::vector tmp_lrat_chain; - if (internal->lrat) { - tmp_lrat_chain = std::move (lrat_chain); - lrat_chain.clear (); - produce_eager_representative_lrat (lit); - } - eager_representative (lit) = res; - Clause *equiv = add_tmp_binary_clause (-lit, res); - equiv->hyper = true; - - if (internal->lrat && equiv) { - eager_representative_id (lit) = equiv->id; - } - if (internal->lrat) { - lrat_chain = std::move (tmp_lrat_chain); - } - } else if (path_length == 2) { - LOG ("duplicated information %d -> %d to eager with clause %" PRIu64, - lit, res, eager_representative_id (lit)); - CADICAL_assert (eager_representative (lit) == res); - CADICAL_assert (!internal->lrat || eager_representative_id (lit)); - } - CADICAL_assert (internal->clause.empty ()); - return res; -} - -void Closure::find_representative_and_compress_both (int lit) { - find_representative_and_compress (lit, false); - find_representative_and_compress (-lit, false); -} - -void Closure::import_lazy_and_find_eager_representative_and_compress_both ( - int lit) { - find_representative_and_compress (lit); - find_eager_representative_and_compress (lit); - find_representative_and_compress (-lit); - find_eager_representative_and_compress (-lit); -} - -void Closure::produce_representative_lrat (int lit) { - CADICAL_assert (internal->lrat); - LOG ("production of LRAT chain for %d with representative %" PRIu64, lit, - representative_id (lit)); - CADICAL_assert (internal->lrat); - CADICAL_assert (lrat_chain.empty ()); - int res = lit; - int nxt = lit; - CADICAL_assert (nxt != representative (nxt)); - do { - res = nxt; - nxt = representative (nxt); - if (nxt != res) { - LOG ("%d has reason %" PRIu64, res, representative_id (res)); - lrat_chain.push_back (representative_id (res)); - } - } while (nxt != res); -} - -void Closure::produce_eager_representative_lrat (int lit) { - CADICAL_assert (internal->lrat); - LOG ("production of LRAT chain for %d with representative %" PRIu64, lit, - eager_representative_id (lit)); - CADICAL_assert (internal->lrat); - CADICAL_assert (lrat_chain.empty ()); - int res = lit; - int nxt = lit; - CADICAL_assert (nxt != eager_representative (nxt)); - do { - res = nxt; - nxt = eager_representative (nxt); - if (nxt != res) { - LOG ("%d has reason %" PRIu64, res, eager_representative_id (res)); - lrat_chain.push_back (eager_representative_id (res)); - } - } while (nxt != res); -} - -LRAT_ID Closure::find_representative_lrat (int lit) { - if (!internal->lrat) - return 0; - int res = lit; -#ifndef CADICAL_NDEBUG - int nxt = representative (res); - CADICAL_assert (nxt == representative (res)); -#endif - LOG ("checking for existing LRAT chain for %d with clause %" PRIu64, lit, - eager_representative_id (res)); - CADICAL_assert (representative_id (res)); - return representative_id (res); -} - -LRAT_ID Closure::find_eager_representative_lrat (int lit) { - if (!internal->lrat) - return 0; - int res = lit; -#ifndef CADICAL_NDEBUG - int nxt = eager_representative (res); - CADICAL_assert (nxt == eager_representative (res)); -#endif - LOG ("checking for existing LRAT chain for %d with clause %" PRIu64, lit, - eager_representative_id (res)); - CADICAL_assert (eager_representative_id (res)); - return eager_representative_id (res); -} - -LRAT_ID &Closure::eager_representative_id (int lit) { - CADICAL_assert (internal->vlit (lit) < eager_representant_id.size ()); - return eager_representant_id[internal->vlit (lit)]; -} -LRAT_ID Closure::eager_representative_id (int lit) const { - CADICAL_assert (internal->vlit (lit) < eager_representant_id.size ()); - return eager_representant_id[internal->vlit (lit)]; -} - -LRAT_ID &Closure::representative_id (int lit) { - CADICAL_assert (internal->vlit (lit) < representant_id.size ()); - return representant_id[internal->vlit (lit)]; -} -LRAT_ID Closure::representative_id (int lit) const { - CADICAL_assert (internal->vlit (lit) < representant_id.size ()); - return representant_id[internal->vlit (lit)]; -} - -void Closure::mark_garbage (Gate *g) { - LOG (g, "marking as garbage"); - CADICAL_assert (!g->garbage); - g->garbage = true; - garbage.push_back (g); -} - -bool Closure::remove_gate (GatesTable::iterator git) { - CADICAL_assert (git != end (table)); - CADICAL_assert (!internal->unsat); - (*git)->indexed = false; - LOG ((*git), "removing from hash table"); - table.erase (git); - return true; -} - -bool Closure::remove_gate (Gate *g) { - if (!g->indexed) - return false; - CADICAL_assert (!internal->unsat); - CADICAL_assert (table.find (g) != end (table)); - table.erase (table.find (g)); - g->indexed = false; - LOG (g, "removing from hash table"); - return true; -} - -void Closure::index_gate (Gate *g) { - CADICAL_assert (!g->indexed); - CADICAL_assert (!internal->unsat); - CADICAL_assert (g->arity () > 1); - CADICAL_assert (g->hash == hash_lits (nonces, g->rhs)); - LOG (g, "adding to hash table"); - table.insert (g); - g->indexed = true; -} - -void Closure::produce_rewritten_clause_lrat_and_clean ( - std::vector &litIds, int except_lhs, bool remove_units) { - CADICAL_assert (internal->lrat_chain.empty ()); - for (auto &litId : litIds) { - CADICAL_assert (litId.clause); - litId.clause = produce_rewritten_clause_lrat (litId.clause, except_lhs, - remove_units); - litId.current_lit = find_eager_representative (litId.current_lit); - } - litIds.erase ( - std::remove_if (begin (litIds), end (litIds), - [] (const LitClausePair &p) { return !p.clause; }), - end (litIds)); -} - -void Closure::produce_rewritten_clause_lrat_and_clean ( - std::vector &litIds, int except_lhs, - size_t &old_position1, size_t &old_position2, bool remove_units) { - CADICAL_assert (internal->lrat_chain.empty ()); - CADICAL_assert (old_position1 != old_position2); - size_t j = 0; - for (size_t i = 0; i < litIds.size (); ++i) { - CADICAL_assert (j <= i); - litIds[j].clause = produce_rewritten_clause_lrat ( - litIds[i].clause, except_lhs, remove_units); - litIds[j].current_lit = - find_eager_representative (litIds[i].current_lit); - if (i == old_position1) { - if (litIds[j].clause) - old_position1 = j; - else - old_position1 = -1; - } - if (i == old_position2) { - if (litIds[j].clause) - old_position2 = j; - else - old_position2 = -1; - } - if (litIds[j].clause) - ++j; - } - litIds.resize (j); -} - -void Closure::compute_rewritten_clause_lrat_simple (Clause *c, int except) { - CADICAL_assert (internal->clause.empty ()); - CADICAL_assert (internal->lrat_chain.empty ()); - CADICAL_assert (clause.empty ()); - CADICAL_assert (lrat_chain.empty ()); - bool changed = false; - bool tautology = false; - for (auto lit : *c) { - LOG ("checking if %d is required", lit); - if (internal->marked2 (lit)) { - continue; - } - if (internal->marked2 (-lit)) { - tautology = true; - break; - } - if (lit == except || lit == -except) { - internal->mark2 (lit); - clause.push_back (lit); - continue; - } - if (internal->val (lit) < 0) { -#if 1 - LOG ("found unit %d, removing it", -lit); - LRAT_ID id = internal->unit_id (-lit); - lrat_chain.push_back (id); - changed = true; - continue; -#else - LOG ("found unit %d, but ignoring it", -lit); -#endif - } - if (internal->val (lit) > 0) { - LOG ("found positive unit, so clause is subsumed by unit"); - tautology = true; - } - const int other = find_eager_representative_and_compress (lit); - const bool marked = internal->marked2 (other); - const bool neg_marked = internal->marked2 (-other); - if (!marked) - internal->mark2 (other); - if (neg_marked) { - tautology = true; - LOG ("tautology due to %d -> %d", lit, other); - } else if (lit == other && marked) { - changed = true; - LOG ("%d -> %d already in", lit, other); - } else if (lit != other) { - if (!marked) - clause.push_back (other); - changed = true; - lrat_chain.push_back (eager_representative_id (lit)); - } else if (!marked) - clause.push_back (lit); - } - - for (auto lit : *c) { - internal->unmark (lit); - } - - for (auto lit : clause) { - internal->unmark (lit); - } - - lrat_chain.push_back (c->id); - if (tautology) { - LOG ("generated clause is a tautology"); - lrat_chain.clear (); - clause.clear (); - } else if (changed && clause.size () == 1) { - LOG (lrat_chain, "LRAT chain"); - } else { - LOG (c, "oops this should not happen"); - CADICAL_assert (false); - } -} - -Clause *Closure::new_tmp_clause (std::vector &clause) { - CADICAL_assert (internal->lrat); - CADICAL_assert (!clause.empty ()); - CADICAL_assert (!lrat_chain.empty ()); - bool clear = false; - - LOG (clause, "learn new tmp clause"); - CADICAL_assert (clause.size () >= 2); - internal->external->check_learned_clause (); - - CADICAL_assert (internal->clause.size () <= (size_t) INT_MAX); - const int size = (int) clause.size (); - CADICAL_assert (size >= 2); - - size_t bytes = Clause::bytes (size); - Clause *c = (Clause *) new char[bytes]; - DeferDeleteArray clause_delete ((char *) c); - - c->id = ++internal->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 = false; - c->transred = false; - c->subsume = false; - c->swept = false; - c->flushed = false; - c->vivified = false; - c->vivify = false; - c->used = 0; - - c->glue = size; - 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); - - clause_delete.release (); - LOG (c, "new pointer %p", (void *) c); - - if (clear) - clause.clear (); - - if (internal->proof) { - internal->proof->add_derived_clause (c, lrat_chain); - } - extra_clauses.push_back (c); - CADICAL_assert (internal->lrat_chain.empty ()); - return c; -} - -Clause *Closure::new_clause () { - CADICAL_assert (internal->clause.empty () || clause.empty ()); - bool clear = false; - if (internal->clause.empty ()) { - swap (internal->clause, clause); - clear = true; - } - - Clause *c = internal->new_clause (false); - - if (clear) - internal->clause.clear (); - - if (internal->proof) { - internal->proof->add_derived_clause (c, lrat_chain); - } - - return c; -} - -// TODO we here duplicate the arguments of push_id_and_rewriting_lrat but we -// probably do not need that. -void Closure::produce_rewritten_clause_lrat ( - std::vector &litIds, int except_lhs, bool remove_units) { - CADICAL_assert (internal->lrat_chain.empty ()); - for (auto &litId : litIds) { - if (!litId.clause) - continue; - litId.clause = produce_rewritten_clause_lrat (litId.clause, except_lhs, - remove_units); - litId.current_lit = find_eager_representative (litId.current_lit); - } -} - -Clause *Closure::produce_rewritten_clause_lrat (Clause *c, int except_lhs, - bool remove_units, bool fail_on_unit) { - CADICAL_assert (internal->clause.empty ()); - CADICAL_assert (internal->lrat_chain.empty ()); - auto tmp_lrat (std::move (lrat_chain)); - lrat_chain.clear (); - LOG (c, "rewriting clause for LRAT proof, except for rewriting %d", - except_lhs); - CADICAL_assert (internal->clause.empty ()); - CADICAL_assert (lrat_chain.empty ()); - bool changed = false; - bool tautology = false; - for (auto lit : *c) { - LOG ("checking if %d is required", lit); - if (internal->marked2 (lit)) { - continue; - } - if (internal->marked2 (-lit)) { - tautology = true; - break; - } - if (lit == except_lhs || lit == -except_lhs) { - internal->mark2 (lit); - clause.push_back (lit); - continue; - } - if (internal->val (lit) < 0) { - if (remove_units || lazy_propagated (lit)) { - LOG ("found unit %d, removing it", -lit); - LRAT_ID id = internal->unit_id (-lit); - lrat_chain.push_back (id); - changed = true; - continue; - } else - LOG ("found unit %d, but ignoring it", -lit); - } - if (internal->val (lit) > 0) { - LOG ("found positive unit %d, so clause is subsumed by unit", lit); - if (remove_units || lazy_propagated (lit)) - tautology = true; - } - const int other = find_eager_representative_and_compress (lit); - const bool marked = internal->marked2 (other); - const bool neg_marked = internal->marked2 (-other); - if (!marked) - internal->mark2 (other); - if (neg_marked) { - tautology = true; - LOG ("tautology due to %d -> %d", lit, other); - } else if (lit == other && marked) { - changed = true; - LOG ("%d -> %d already in", lit, other); - } else if (lit != other) { - if (!marked) - clause.push_back (other); - changed = true; - lrat_chain.push_back (eager_representative_id (lit)); - } else if (!marked) - clause.push_back (lit); - } - - for (auto lit : *c) { - internal->unmark (lit); - } - - for (auto lit : clause) { - internal->unmark (lit); - } - - lrat_chain.push_back (c->id); - Clause *d; - CADICAL_assert (internal->clause.empty ()); - if (tautology) { - LOG ("generated clause is a tautology"); - d = nullptr; - lrat_chain.clear (); - } else if (changed && clause.size () == 1) { - LOG (lrat_chain, "LRAT chain"); - if (fail_on_unit) { - d = nullptr; - CADICAL_assert (false && "rewriting produced a unit clause"); - } else { - d = c; - } - } else if (changed) { - LOG (lrat_chain, "LRAT Chain"); - d = new_tmp_clause (clause); - LOG (d, "rewritten clause to"); - } else { - LOG (c, "clause is unchanged, so giving up"); - lrat_chain.clear (); - d = c; - } - clause.clear (); - lrat_chain = std::move (tmp_lrat); - CADICAL_assert (internal->clause.empty ()); - return d; -} - -void Closure::push_id_on_chain (std::vector &chain, - Rewrite rewrite, int lit) { - LOG ("adding reason %zd for rewriting %d marked", - lit == rewrite.src ? rewrite.id1 : rewrite.id2, lit); - chain.push_back (lit == rewrite.src ? rewrite.id1 : rewrite.id2); -} - -void Closure::push_id_and_rewriting_lrat_unit (Clause *c, Rewrite rewrite1, - std::vector &chain, - bool insert_id_after, - Rewrite rewrite2, - int except_lhs, - int except_lhs2) { - LOG (c, - "computing normalized LRAT chain for clause to produce unit, " - "rewriting except for %d (%" PRIu64 ", %" PRIu64 ") and %d (%" PRIu64 - ", %" PRIu64 ") and skipping %d and %d", - rewrite1.src, rewrite1.id1, rewrite1.id2, rewrite2.src, rewrite2.id1, - rewrite2.id2, except_lhs, except_lhs2); - CADICAL_assert (c); - std::vector units, rewriting; - for (auto other : *c) { - // unclear how to achieve this in the simplify context where other == - // g->lhs might be set CADICAL_assert (internal->val (other) <= 0 || other == - // except); - if (other == except_lhs || other == -except_lhs) { - // do nothing; - } else if (other == except_lhs2 || other == -except_lhs2) { - // do nothing; - } else if (internal->val (other) < 0) { - LOG ("found unit %d", -other); - LRAT_ID id = internal->unit_id (-other); - units.push_back (id); - } else if (other == rewrite1.src && rewrite1.id1) { - push_id_on_chain (rewriting, rewrite1, other); - } else if (other == -rewrite1.src && rewrite1.id2) { - push_id_on_chain (rewriting, rewrite1, other); - } else if (other == rewrite2.src && rewrite2.id1) { - push_id_on_chain (rewriting, rewrite2, other); - } else if (other == -rewrite2.src && rewrite2.id2) { - push_id_on_chain (rewriting, rewrite2, other); - } else if (other != find_eager_representative_and_compress (other)) { -#if defined(LOGGING) || !defined(CADICAL_NDEBUG) - const int rewritten_other = eager_representative (other); - CADICAL_assert (other != rewritten_other); - LOG ("reason for representative of %d %d is %" PRIu64 "", other, - rewritten_other, find_eager_representative_lrat (other)); -#endif - rewriting.push_back (find_eager_representative_lrat (other)); - } else { - LOG ("no rewriting needed for %d", other); - } - } - for (auto id : units) - chain.push_back (id); - - if (!insert_id_after) - chain.push_back (c->id); - for (auto id : rewriting) - chain.push_back (id); - - if (insert_id_after) - chain.push_back (c->id); -} - -// Note: it is important that the Rewrite takes over the normal rewriting, -// because we can force rewriting that way that have not been done eagerly -// yet. -void Closure::push_id_and_rewriting_lrat_full (Clause *c, Rewrite rewrite1, - std::vector &chain, - bool insert_id_after, - Rewrite rewrite2, - int except_lhs, - int except_lhs2) { - LOG (c, - "computing normalized LRAT chain for clause, rewriting except for " - "%d (%" PRIu64 ", %" PRIu64 ") and %d (%" PRIu64 ", %" PRIu64 - ") and skipping %d and %d", - rewrite1.src, rewrite1.id1, rewrite1.id2, rewrite2.src, rewrite2.id1, - rewrite2.id2, except_lhs, except_lhs2); - CADICAL_assert (c); - if (!insert_id_after) - chain.push_back (c->id); - for (auto other : *c) { - // unclear how to achieve this in the simplify context where other == - // g->lhs might be set CADICAL_assert (internal->val (other) <= 0 || other == - // except); - if (other == except_lhs) { - // do nothing; - } else if (other == except_lhs2) { - // do nothing; - } else if (internal->val (other) < 0) { - LOG ("found unit %d", -other); - LRAT_ID id = internal->unit_id (-other); - CADICAL_assert (id); - chain.push_back (id); - } else if (other == rewrite1.src && rewrite1.id1) { - push_id_on_chain (chain, rewrite1, other); - } else if (other == -rewrite1.src && rewrite1.id2) { - push_id_on_chain (chain, rewrite1, other); - } else if (other == rewrite2.src && rewrite2.id1) { - push_id_on_chain (chain, rewrite2, other); - } else if (other == -rewrite2.src && rewrite2.id2) { - push_id_on_chain (chain, rewrite2, other); - } else { - CADICAL_assert (other == find_eager_representative (other)); - LOG ("no rewriting needed for %d", other); - } - } - if (insert_id_after) - chain.push_back (c->id); -} - -// Note: it is important that the Rewrite takes over the normal rewriting, -// because we can force rewriting that way that have not been done eagerly -// yet. -void Closure::push_id_on_chain (std::vector &chain, Clause *c) { - CADICAL_assert (c); - chain.push_back (c->id); - LOG (lrat_chain, "chain"); -} - -void Closure::push_id_on_chain (std::vector &chain, - const std::vector &reasons) { - for (auto litId : reasons) { - LOG (litId.clause, "found lrat in gate %d from %zd", litId.current_lit, - litId.clause->id); - push_id_on_chain (chain, litId.clause); - } - LOG (lrat_chain, "chain from %zd reasons", reasons.size ()); -} - -void Closure::learn_congruence_unit_when_lhs_set (Gate *g, int src, - LRAT_ID id1, LRAT_ID id2, - int dst) { - if (!internal->lrat) - return; - LOG ("calculating LRAT chain learn_congruence_unit_when_lhs_set"); - CADICAL_assert (!g->pos_lhs_ids.empty ()); - CADICAL_assert (internal->analyzed.empty ()); - CADICAL_assert (internal->val (g->lhs) < 0); - switch (g->tag) { - case Gate_Type::And_Gate: - LOG (lrat_chain, "lrat"); - LOG (lrat_chain, "lrat"); - for (auto litId : g->neg_lhs_ids) - push_id_and_rewriting_lrat_unit ( - litId.clause, Rewrite (src, dst, id1, id2), lrat_chain); - LOG (lrat_chain, "lrat"); - break; - default: - CADICAL_assert (false); - } -} - -// Something very important here: as we are producing a unit, we cannot -// simplify or rewrite the clauses as this will produce units. -void Closure::learn_congruence_unit_falsifies_lrat_chain ( - Gate *g, int src, int dst, int clashing, int falsified, int unit) { - if (!internal->lrat) - return; - CADICAL_assert (!g->pos_lhs_ids.empty ()); - CADICAL_assert (internal->analyzed.empty ()); - CADICAL_assert (lrat_chain.empty ()); - std::vector proof_chain; - switch (g->tag) { - case Gate_Type::And_Gate: - if (clashing) { - LOG ("clashing %d where -lhs=%d", clashing, -g->lhs); - // Example: -2 = 1&3 and 3=2 - // The proof consists in taking the binary clause of the clashing - // literal - if (clashing == -g->lhs) { - for (auto litId : g->pos_lhs_ids) { - LOG (litId.clause, - "found lrat in gate %d from %zd (looking for %d)", - litId.current_lit, litId.clause->id, falsified); - if (litId.current_lit == clashing) { - push_id_and_rewriting_lrat_unit ( - litId.clause, Rewrite (), proof_chain, true, Rewrite (), - g->degenerated_and_neg || g->degenerated_and_pos ? 0 : -g->lhs); - } - } - } else { - // Example: 3 = (-1&2) and 2=1 - // The proof consists in taking the binary clause with the rewrites - // Example where the rewrite must be before: - // 2: 3v2 - // 9: -2v1 - // 6: 3v1 - // The chain cannot start by 9 - if (g->degenerated_and_neg || g->degenerated_and_pos) { - LOG ("%d %d %d", src, dst, g->lhs); - if (src == g->lhs || dst == g->lhs) { - LOG ("degenerated AND gate with dst=lhs"); - for (const auto &litId : g->pos_lhs_ids) { - LOG (litId.clause, "definition clause %d ->", - litId.current_lit); - if (litId.current_lit == clashing) { - push_id_and_rewriting_lrat_unit (litId.clause, Rewrite (), - proof_chain, true, - Rewrite (), 0); - LOG (proof_chain, "produced lrat chain so far"); - } - } - CADICAL_assert (!proof_chain.empty ()); - } else { - LOG ("degenerated AND gate with conflict without LHS"); - for (const auto &litId : g->pos_lhs_ids) { - LOG (litId.clause, "definition clause %d ->", - litId.current_lit); - push_id_and_rewriting_lrat_unit (litId.clause, Rewrite (), - proof_chain, false, - Rewrite (), -g->lhs); - LOG (proof_chain, "produced lrat chain so far"); - } - } - } else { - LOG ("normal AND gate"); - for (const auto &litId : g->pos_lhs_ids) { - push_id_and_rewriting_lrat_unit (litId.clause, Rewrite (), - proof_chain, false, Rewrite (), - g->degenerated_and_neg || g->degenerated_and_pos ? 0 : -g->lhs); - LOG (proof_chain, "produced lrat chain so far"); - } - } - } - LOG (proof_chain, "produced lrat chain"); - } else if (falsified) { - LOG ("falsifies %d", falsified); - // Example is 3=(1&2) with 2=false or 3=(1&4) with 4=2 and 2=false - // (can happen when the unit was derived in the middle of the - // rewriting) - for (auto litId : g->pos_lhs_ids) { - LOG (litId.clause, - "found lrat in gate %d from %zd (looking for %d)", - litId.current_lit, litId.clause->id, falsified); - if (litId.current_lit == falsified || - (litId.current_lit == src && dst == falsified)) { - push_id_and_rewriting_lrat_unit (litId.clause, Rewrite (), - proof_chain, true, Rewrite (), - -dst, -g->lhs); - } - } - } else { - CADICAL_assert (unit); - // Example is 1 = 2&3 where 2 and 3 are false - for (auto litId : g->neg_lhs_ids) { - push_id_and_rewriting_lrat_unit (litId.clause, Rewrite (), - proof_chain); - } - LOG (proof_chain, "produced lrat chain"); - break; - } - lrat_chain = std::move (proof_chain); - break; - default: - CADICAL_assert (false); - } - (void) unit; -} - -bool Closure::fully_propagate () { - if (internal->unsat) - return false; - LOG ("fully propagating"); - CADICAL_assert (internal->watching ()); - CADICAL_assert (full_watching); - bool no_conflict = internal->propagate (); - - if (no_conflict) - return true; - internal->learn_empty_clause (); - if (internal->lrat) - lrat_chain.clear (); - - return false; -} -bool Closure::learn_congruence_unit (int lit, bool delay_propagation, bool force_propagation) { - if (internal->unsat) - return false; - const signed char val_lit = internal->val (lit); - if (val_lit > 0) { - LOG ("already set lit %d", lit); - if (internal->lrat) - lrat_chain.clear (); - if (force_propagation) - return fully_propagate(); - return true; - } - LOG ("adding unit %s", LOGLIT (lit)); - ++internal->stats.congruence.units; - CADICAL_assert (!internal->lrat || !lrat_chain.empty ()); - if (val_lit < 0) { - if (internal->lrat) { - CADICAL_assert (internal->lrat_chain.empty ()); - LRAT_ID id = internal->unit_id (-lit); - internal->lrat_chain.push_back (id); - for (auto id : lrat_chain) - internal->lrat_chain.push_back (id); - lrat_chain.clear (); - } - internal->learn_empty_clause (); - return false; - } - - LOG (lrat_chain, "assigning due to LRAT chain"); - swap (lrat_chain, internal->lrat_chain); - internal->assign_unit (lit); - CADICAL_assert (lrat_chain.empty ()); - CADICAL_assert (internal->lrat_chain.empty ()); - if (delay_propagation) - return false; - else return fully_propagate (); -} - -// for merging the literals there are many cases -// TODO: LRAT does not work if the LHS is not in normal form and if the -// representative is also in the gate. -bool Closure::merge_literals_lrat ( - Gate *g, Gate *h, int lit, int other, - const std::vector &extra_reasons_lit, - const std::vector &extra_reasons_ulit) { - CADICAL_assert (!internal->unsat); - CADICAL_assert (g->lhs == lit); - CADICAL_assert (g == h || h->lhs == other); - (void) g, (void) h; - LOG ("merging literals %s and %s", LOGLIT(lit), LOGLIT(other)); - // TODO: this should not update_eager but still calculate the LRAT chain - // below! - const int repr_lit = find_representative_and_compress (lit, false); - const int repr_other = find_representative_and_compress (other, false); - find_representative_and_compress (-lit, false); - find_representative_and_compress (-other, false); - LOG ("merging literals %d [=%d] and %d [=%d]", lit, repr_lit, other, - repr_other); - - if (repr_lit == repr_other) { - LOG ("already merged %d and %d", lit, other); - if (internal->lrat) - lrat_chain.clear (); - return false; - } - - const int val_lit = internal->val (lit); - const int val_other = internal->val (other); - if (val_lit) { - if (val_lit == val_other) { - LOG ("not merging lits %d and %d assigned to same value", lit, other); - if (internal->lrat) - lrat_chain.clear (); - return false; - } - } - - // For LRAT we need to distinguish more cases for a more regular - // reconstruction. - // - // 1. if lit = -other, then we learn lit and -lit to derive false - // - // 2. otherwise, we learn the new clauses lit = -other (which are two real - // clauses). - // - // 2a. if repr_lit = -repr_other, we learn the units repr_lit and - // -repr_lit to derive false - // - // 2b. otherwise, we learn the equivalences repr_lit = -repr_other - // (which are two real clauses) - // - // Without LRAT this is easier, as we directly learn the conclusion - // (either false or the equivalence). The steps can also not be merged - // because repr_lit can appear in the gate and hence in the resolution - // chain. - int smaller_repr = repr_lit; - int larger_repr = repr_other; - int smaller = lit; - int larger = other; - const std::vector *smaller_chain = &extra_reasons_ulit; - const std::vector *larger_chain = &extra_reasons_lit; - - if (abs (smaller_repr) > abs (larger_repr)) { - swap (smaller_repr, larger_repr); - swap (smaller, larger); - swap (smaller_chain, larger_chain); - } - - CADICAL_assert (find_representative (smaller_repr) == smaller_repr); - CADICAL_assert (find_representative (larger_repr) == larger_repr); - if (lit == -other) { - CADICAL_assert (chain.empty ()); - LOG ("merging clashing %d and %d", lit, other); - if (internal->proof) { - if (internal->lrat) { - for (auto id : *smaller_chain) - lrat_chain.push_back (id); - } - unsimplified.push_back (smaller); - LRAT_ID id = simplify_and_add_to_proof_chain (unsimplified); - - if (internal->lrat) { - internal->lrat_chain.push_back (id); - for (auto id : *larger_chain) - internal->lrat_chain.push_back (id); - LOG (internal->lrat_chain, "lrat chain"); - } - } - internal->learn_empty_clause (); - delete_proof_chain (); - return false; - } - - LOG ("merging %d and %d first and then the equivalences of %d and %d " - "with LRAT", - lit, other, repr_lit, repr_other); - Clause *eq1_tmp = nullptr; - if (internal->lrat) { - lrat_chain = *smaller_chain; - eq1_tmp = add_tmp_binary_clause (-larger, smaller); - } - CADICAL_assert (!internal->lrat || eq1_tmp); - - Clause *eq2_tmp = nullptr; - if (internal->lrat) { - lrat_chain = *larger_chain; - LOG (lrat_chain, "lrat chain"); - - eq2_tmp = add_tmp_binary_clause (larger, -smaller); - // the order in the clause is important for the - // repr_lit == -repr_other to get the right chain - } - CADICAL_assert (!internal->lrat || eq2_tmp); - if (internal->lrat) - lrat_chain.clear (); - - if (repr_lit == -repr_other) { - // now derive empty clause - Rewrite rew1, rew2; - if (internal->lrat) { - // no need to calculate push_id_and_rewriting_lrat here because all - // the job is done by the arguments already - rew1 = Rewrite (lit == repr_lit ? 0 : lit, repr_lit, - lit == repr_lit ? 0 : representative_id (lit), - lit == repr_lit ? 0 : representative_id (-lit)); - rew2 = Rewrite (other == repr_other ? 0 : other, repr_other, - other == repr_other ? 0 : representative_id (other), - other == repr_other ? 0 : representative_id (-other)); - push_id_and_rewriting_lrat_unit (eq1_tmp, rew1, lrat_chain, true, - rew2); - swap (lrat_chain, internal->lrat_chain); - } - internal->assign_unit (-larger_repr); - if (internal->lrat) { - internal->lrat_chain.clear (); - - if (larger != larger_repr) - push_lrat_unit (-larger_repr); - push_id_and_rewriting_lrat_unit ( - eq2_tmp, rew1, lrat_chain, true, rew2, - larger != larger_repr ? larger_repr : 0); - LOG (lrat_chain, "lrat chain"); - swap (lrat_chain, internal->lrat_chain); - } - internal->learn_empty_clause (); - if (internal->lrat) - internal->lrat_chain.clear (); - return false; - } - - if (val_lit) { - if (val_lit == val_other) { - LOG ("not merging lits %d and %d assigned to same value", lit, other); - if (internal->lrat) - lrat_chain.clear (); - return false; - } - if (val_lit == -val_other) { - LOG ("merging lits %d and %d assigned to inconsistent value", lit, - other); - CADICAL_assert (lrat_chain.empty ()); - if (internal->lrat) { - Clause *c = val_lit ? eq2_tmp : eq1_tmp; - int pos = val_lit ? other : lit; - int neg = val_lit ? -lit : -other; - push_lrat_unit (pos); - push_lrat_unit (neg); - push_id_on_chain (lrat_chain, c); - } - internal->learn_empty_clause (); - if (internal->lrat) - lrat_chain.clear (); - return false; - } - - CADICAL_assert (!val_other); - LOG ("merging assigned %d and unassigned %d", lit, other); - CADICAL_assert (lrat_chain.empty ()); - const int unit = (val_lit < 0) ? -other : other; - if (internal->lrat) { - Clause *c; - if (lit == smaller) { - if (val_lit < 0) - c = eq1_tmp; - else - c = eq2_tmp; - } else { - if (val_lit < 0) - c = eq2_tmp; - else - c = eq1_tmp; - } - push_id_and_rewriting_lrat_unit (c, Rewrite (), lrat_chain, true, - Rewrite (), unit); - } - learn_congruence_unit (unit); - if (internal->lrat) - lrat_chain.clear (); - return false; - } - - if (!val_lit && val_other) { - LOG ("merging assigned %d and unassigned %d", lit, other); - CADICAL_assert (lrat_chain.empty ()); - const int unit = (val_other < 0) ? -lit : lit; - if (internal->lrat) { - Clause *c; - if (lit == smaller) { - if (val_lit < 0) - c = eq1_tmp; - else - c = eq2_tmp; - } else { - if (val_lit < 0) - c = eq2_tmp; - else - c = eq1_tmp; - } - push_id_and_rewriting_lrat_unit (c, Rewrite (), lrat_chain, true, - Rewrite (), lit, unit); - } - learn_congruence_unit (unit); - if (internal->lrat) - lrat_chain.clear (); - return false; - } - - Clause *eq1_repr, *eq2_repr; - if (smaller_repr != smaller || larger != larger_repr) { - if (internal->lrat) { - lrat_chain.clear (); - Rewrite rew1 = Rewrite ( - smaller_repr != smaller ? smaller : 0, - smaller_repr != smaller ? smaller_repr : 0, - smaller_repr != smaller ? representative_id (smaller) : 0, - smaller_repr != smaller ? representative_id (-smaller) : 0); - Rewrite rew2 = - Rewrite (larger_repr != larger ? larger : 0, - larger_repr != larger ? larger_repr : 0, - larger_repr != larger ? representative_id (larger) : 0, - larger_repr != larger ? representative_id (-larger) : 0); - push_id_and_rewriting_lrat_full (eq1_tmp, rew1, lrat_chain, true, - rew2); - } - eq1_repr = learn_binary_tmp_or_full_clause (-larger_repr, smaller_repr); - } else { - if (internal->lrat) - eq1_repr = maybe_promote_tmp_binary_clause (eq1_tmp); - else - eq1_repr = maybe_add_binary_clause (-larger_repr, smaller_repr); - } - - if (internal->lrat) { - lrat_chain.clear (); - } - - if (smaller_repr != smaller || larger != larger_repr) { - - if (internal->lrat) { - lrat_chain.clear (); - // eq2 = larger, -smaller - Rewrite rew1 = Rewrite ( - smaller_repr != smaller ? smaller : 0, - smaller_repr != smaller ? smaller_repr : 0, - smaller_repr != smaller ? representative_id (smaller) : 0, - smaller_repr != smaller ? representative_id (-smaller) : 0); - Rewrite rew2 = - Rewrite (larger_repr != larger ? larger : 0, - larger_repr != larger ? larger_repr : 0, - larger_repr != larger ? representative_id (larger) : 0, - larger_repr != larger ? representative_id (-larger) : 0); - push_id_and_rewriting_lrat_full (eq2_tmp, rew1, lrat_chain, true, - rew2); - } - - eq2_repr = learn_binary_tmp_or_full_clause (larger_repr, -smaller_repr); - - } else { - if (internal->lrat) - eq2_repr = maybe_promote_tmp_binary_clause (eq2_tmp); - else - eq2_repr = maybe_add_binary_clause (larger_repr, -smaller_repr); - } - lrat_chain.clear (); - - if (internal->lrat) { - representative_id (larger_repr) = eq1_repr->id; - CADICAL_assert (std::find (begin (*eq1_repr), end (*eq1_repr), -larger_repr) != - end (*eq1_repr)); - representative_id (-larger_repr) = eq2_repr->id; - CADICAL_assert (std::find (begin (*eq2_repr), end (*eq2_repr), larger_repr) != - end (*eq2_repr)); - } - LOG ("updating %d -> %d", larger_repr, smaller_repr); - representative (larger_repr) = smaller_repr; - representative (-larger_repr) = -smaller_repr; - schedule_literal (larger_repr); - ++internal->stats.congruence.congruent; - CADICAL_assert (lrat_chain.empty ()); - return true; -} - -// Variant when there are no gates -// TODO: this is exactly the same as the other function without the gates. -// Kill the arguments! -bool Closure::merge_literals_lrat ( - int lit, int other, const std::vector &extra_reasons_lit, - const std::vector &extra_reasons_ulit) { - CADICAL_assert (!internal->unsat); - LOG ("merging literals %s and %s", LOGLIT (lit), LOGLIT (other)); - // TODO: this should not update_eager but still calculate the LRAT chain - // below! - const int repr_lit = find_representative_and_compress (lit, false); - const int repr_other = find_representative_and_compress (other, false); - find_representative_and_compress (-lit, false); - find_representative_and_compress (-other, false); - LOG ("merging literals %s [=%d] and %s [=%d]", LOGLIT (lit), repr_lit, - LOGLIT (other), repr_other); - LOG (lrat_chain, "lrat chain beginning of merge"); - - if (repr_lit == repr_other) { - LOG ("already merged %s and %s", LOGLIT (lit), LOGLIT (other)); - if (internal->lrat) - lrat_chain.clear (); - return false; - } - - const int val_lit = internal->val (lit); - const int val_other = internal->val (other); - - // For LRAT we need to distinguish more cases for a more regular - // reconstruction. - // - // 1. if lit = -other, then we learn lit and -lit to derive false - // - // 2. otherwise, we learn the new clauses lit = -other (which are two real - // clauses). - // - // 2a. if repr_lit = -repr_other, we learn the units repr_lit and - // -repr_lit to derive false - // - // 2b. otherwise, we learn the equivalences repr_lit = -repr_other - // (which are two real clauses) - // - // Without LRAT this is easier, as we directly learn the conclusion - // (either false or the equivalence). The steps can also not be merged - // because repr_lit can appear in the gate and hence in the resolution - // chain. - int smaller_repr = repr_lit; - int larger_repr = repr_other; - int val_smaller = val_lit; - int val_larger = val_other; - int smaller = lit; - int larger = other; - const std::vector *smaller_chain = &extra_reasons_ulit; - const std::vector *larger_chain = &extra_reasons_lit; - - if (abs (smaller_repr) > abs (larger_repr)) { - swap (smaller_repr, larger_repr); - swap (smaller, larger); - swap (smaller_chain, larger_chain); - swap (val_smaller, val_larger); - } - - CADICAL_assert (find_representative (smaller_repr) == smaller_repr); - CADICAL_assert (find_representative (larger_repr) == larger_repr); - if (lit == -other) { - LOG ("merging clashing %d and %d", lit, other); - if (!val_smaller) { - if (internal->lrat) - internal->lrat_chain = *smaller_chain; - internal->assign_unit (smaller); - if (internal->lrat) - internal->lrat_chain.clear (); - - push_lrat_unit (smaller); - if (internal->lrat) { - CADICAL_assert (internal->lrat_chain.empty ()); - swap (internal->lrat_chain, lrat_chain); - for (auto id : *larger_chain) - internal->lrat_chain.push_back (id); - LOG (internal->lrat_chain, "lrat chain"); - } - internal->learn_empty_clause (); - return false; - } else { - if (internal->lrat) - internal->lrat_chain = - (val_smaller < 0 ? *smaller_chain : *larger_chain); - if (internal->lrat) - internal->lrat_chain.push_back ( - internal->unit_id (val_smaller > 0 ? smaller : -smaller)); - internal->learn_empty_clause (); - return false; - } - } - - if (val_lit && val_lit == val_other) { - LOG ("not merging lits %d and %d assigned to same value", lit, other); - return false; - } - - if (val_lit && val_lit == -val_other) { - if (internal->lrat) { - internal->lrat_chain.push_back ( - internal->unit_id (val_smaller < 0 ? -smaller : smaller)); - internal->lrat_chain.push_back ( - internal->unit_id (val_larger < 0 ? -larger : larger)); - for (auto id : (val_smaller < 0 ? *smaller_chain : *larger_chain)) { - internal->lrat_chain.push_back(id); - } - } - internal->learn_empty_clause (); - return false; - } - - LOG ("merging %d and %d first and then the equivalences of %d and %d " - "with LRAT", - lit, other, repr_lit, repr_other); - Clause *eq1_tmp = nullptr; - if (internal->lrat) { - lrat_chain = *smaller_chain; - eq1_tmp = add_tmp_binary_clause (-larger, smaller); - CADICAL_assert (eq1_tmp); - } - CADICAL_assert (!internal->lrat || eq1_tmp); - - Clause *eq2_tmp = nullptr; - if (internal->lrat) { - lrat_chain = *larger_chain; - LOG (lrat_chain, "lrat chain"); - // the order in the clause is important for the - // repr_lit == -repr_other to get the right chain - eq2_tmp = add_tmp_binary_clause (larger, -smaller); - CADICAL_assert (eq2_tmp); - } - - CADICAL_assert (!internal->lrat || eq2_tmp); - if (internal->lrat) - lrat_chain.clear (); - - if (repr_lit == -repr_other) { - // now derive empty clause - Rewrite rew1, rew2; - if (internal->lrat) { - // no need to calculate push_id_and_rewriting_lrat here because all - // the job is done by the arguments already - rew1 = Rewrite (lit == repr_lit ? 0 : lit, repr_lit, - lit == repr_lit ? 0 : representative_id (lit), - lit == repr_lit ? 0 : representative_id (-lit)); - rew2 = Rewrite (other == repr_other ? 0 : other, repr_other, - other == repr_other ? 0 : representative_id (other), - other == repr_other ? 0 : representative_id (-other)); - push_id_and_rewriting_lrat_unit (eq1_tmp, rew1, lrat_chain, true, - rew2); - swap (lrat_chain, internal->lrat_chain); - } - CADICAL_assert (val_larger == internal->val (larger_repr)); - if (!val_larger) { - // not assigned, first assign one - internal->assign_unit (-larger_repr); - if (internal->lrat) { - internal->lrat_chain.clear (); - - if (larger != larger_repr) - push_lrat_unit (-larger_repr); - // no need to calculate push_id_and_rewriting_lrat here because all - // the job is done by the arguments already - push_id_and_rewriting_lrat_unit ( - eq2_tmp, rew1, lrat_chain, true, rew2, - larger != larger_repr ? larger_repr : 0); - LOG (lrat_chain, "lrat chain"); - swap (lrat_chain, internal->lrat_chain); - } - } else { - // otherwise, no need to - if (internal->lrat) - lrat_chain.push_back (internal->unit_id (larger_repr)); - } - internal->learn_empty_clause (); - if (internal->lrat) - internal->lrat_chain.clear (); - return false; - } - - if (val_lit) { - if (val_lit == val_other) { - LOG ("not merging lits %d and %d assigned to same value", lit, other); - if (internal->lrat) - lrat_chain.clear (); - return false; - } - if (val_lit == -val_other) { - LOG ("merging lits %d and %d assigned to inconsistent value", lit, - other); - CADICAL_assert (lrat_chain.empty ()); - if (internal->lrat) { - Clause *c = val_lit ? eq2_tmp : eq1_tmp; - int pos = val_lit ? other : lit; - int neg = val_lit ? -lit : -other; - push_lrat_unit (pos); - push_lrat_unit (neg); - push_id_on_chain (lrat_chain, c); - } - internal->learn_empty_clause (); - if (internal->lrat) - lrat_chain.clear (); - return false; - } - - CADICAL_assert (!val_other); - LOG ("merging assigned %d and unassigned %d", lit, other); - CADICAL_assert (lrat_chain.empty ()); - const int unit = (val_lit < 0) ? -other : other; - if (internal->lrat) { - Clause *c; - if (lit == smaller) { - if (val_lit < 0) - c = eq1_tmp; - else - c = eq2_tmp; - } else { - if (val_lit < 0) - c = eq2_tmp; - else - c = eq1_tmp; - } - push_id_and_rewriting_lrat_unit (c, Rewrite (), lrat_chain, true, - Rewrite (), unit); - } - learn_congruence_unit (unit); - if (internal->lrat) - lrat_chain.clear (); - return false; - } - - if (!val_lit && val_other) { - LOG ("merging assigned %d and unassigned %d", lit, other); - CADICAL_assert (lrat_chain.empty ()); - const int unit = (val_other < 0) ? -lit : lit; - if (internal->lrat) { - Clause *c; - if (lit == smaller) { - CADICAL_assert (other == larger); - if (val_other > 0) - c = eq1_tmp; - else - c = eq2_tmp; - } else { - if (val_other > 0) - c = eq2_tmp; - else - c = eq1_tmp; - } - push_id_and_rewriting_lrat_unit (c, Rewrite (), lrat_chain, true, - Rewrite (), lit, unit); - } - learn_congruence_unit (unit); - if (internal->lrat) - lrat_chain.clear (); - return false; - } - - Clause *eq1_repr, *eq2_repr; - if (smaller_repr != smaller || larger != larger_repr) { - if (internal->lrat) { - lrat_chain.clear (); - Rewrite rew1 = Rewrite ( - smaller_repr != smaller ? smaller : 0, - smaller_repr != smaller ? smaller_repr : 0, - smaller_repr != smaller ? representative_id (smaller) : 0, - smaller_repr != smaller ? representative_id (-smaller) : 0); - Rewrite rew2 = - Rewrite (larger_repr != larger ? larger : 0, - larger_repr != larger ? larger_repr : 0, - larger_repr != larger ? representative_id (larger) : 0, - larger_repr != larger ? representative_id (-larger) : 0); - push_id_and_rewriting_lrat_full (eq1_tmp, rew1, lrat_chain, true, - rew2); - } - eq1_repr = learn_binary_tmp_or_full_clause (-larger_repr, smaller_repr); - } else { - if (internal->lrat) - eq1_repr = maybe_promote_tmp_binary_clause (eq1_tmp); - else - eq1_repr = maybe_add_binary_clause (-larger_repr, smaller_repr); - } - - if (internal->lrat) { - lrat_chain.clear (); - } - - if (smaller_repr != smaller || larger != larger_repr) { - - if (internal->lrat) { - lrat_chain.clear (); - // eq2 = larger, -smaller - Rewrite rew1 = Rewrite ( - smaller_repr != smaller ? smaller : 0, - smaller_repr != smaller ? smaller_repr : 0, - smaller_repr != smaller ? representative_id (smaller) : 0, - smaller_repr != smaller ? representative_id (-smaller) : 0); - Rewrite rew2 = - Rewrite (larger_repr != larger ? larger : 0, - larger_repr != larger ? larger_repr : 0, - larger_repr != larger ? representative_id (larger) : 0, - larger_repr != larger ? representative_id (-larger) : 0); - push_id_and_rewriting_lrat_full (eq2_tmp, rew1, lrat_chain, true, - rew2); - } - eq2_repr = learn_binary_tmp_or_full_clause (larger_repr, -smaller_repr); - } else { - if (internal->lrat) - eq2_repr = maybe_promote_tmp_binary_clause (eq2_tmp); - else - eq2_repr = maybe_add_binary_clause (larger_repr, -smaller_repr); - } - lrat_chain.clear (); - - if (internal->lrat) { - representative_id (larger_repr) = eq1_repr->id; - CADICAL_assert (std::find (begin (*eq1_repr), end (*eq1_repr), -larger_repr) != - end (*eq1_repr)); - representative_id (-larger_repr) = eq2_repr->id; - check_not_tmp_binary_clause (eq1_repr); - check_not_tmp_binary_clause (eq2_repr); - CADICAL_assert (std::find (begin (*eq2_repr), end (*eq2_repr), larger_repr) != - end (*eq2_repr)); - } - LOG ("updating %d -> %d", larger_repr, smaller_repr); - representative (larger_repr) = smaller_repr; - representative (-larger_repr) = -smaller_repr; - schedule_literal (larger_repr); - ++internal->stats.congruence.congruent; - CADICAL_assert (lrat_chain.empty ()); - return true; -} - -inline void Closure::promote_clause (Clause *c) { - if (internal->lrat) - check_not_tmp_binary_clause (c); - if (!c) - return; - if (!c->redundant) - return; - LOG (c, "turning redundant subsuming clause into irredundant clause"); - c->redundant = false; - if (internal->proof) - internal->proof->strengthen (c->id); - internal->stats.current.irredundant++; - internal->stats.added.irredundant++; - internal->stats.irrlits += c->size; - CADICAL_assert (internal->stats.current.redundant > 0); - internal->stats.current.redundant--; - CADICAL_assert (internal->stats.added.redundant > 0); - internal->stats.added.redundant--; - // ... and keep 'stats.added.total'. -} - -// This function is rather tricky for LRAT. If you have 2 = 1 and 3=4 you -// cannot add 2=3. You really to connect the representatives directly -// therefore you actually need to learn the clauses 2->3->4 and -2->1 and -// vice-versa -bool Closure::merge_literals_equivalence (int lit, int other, Clause *c1, - Clause *c2) { - CADICAL_assert (!internal->unsat); - LRAT_ID id1 = c1 ? c1->id : 0; - LRAT_ID id2 = c2 ? c2->id : 0; - if (internal->lrat) { - CADICAL_assert (c1); - CADICAL_assert (c2); - CADICAL_assert (c1->size == 2); - CADICAL_assert (c2->size == 2); - CADICAL_assert (c1->literals[0] == lit || c1->literals[1] == lit); - CADICAL_assert (c2->literals[0] == other || c2->literals[1] == other); - CADICAL_assert (c1->literals[0] == -other || c1->literals[1] == -other); - CADICAL_assert (c2->literals[0] == -lit || c2->literals[1] == -lit); - check_not_tmp_binary_clause (c1); - check_not_tmp_binary_clause (c2); - } - int repr_lit = find_representative (lit); - int repr_other = find_representative (other); - find_representative_and_compress_both (lit); - find_representative_and_compress_both (other); - LOG ("merging literals %d [=%d] and %d [=%d] lrat", lit, repr_lit, other, - repr_other); - - if (repr_lit == repr_other) { - LOG ("already merged %d and %d", lit, other); - return false; - } - const int val_lit = internal->val (lit); - const int val_other = internal->val (other); - - if (val_lit) { - if (val_lit == val_other) { - LOG ("not merging lits %d and %d assigned to same value", lit, other); - return false; - } - if (val_lit == -val_other) { - if (internal->lrat) - lrat_chain.push_back (internal->unit_id (lit)), - lrat_chain.push_back (internal->unit_id (other)); - LOG ("merging lits %d and %d assigned to inconsistent value", lit, - other); - internal->learn_empty_clause (); - return false; - } - - CADICAL_assert (!val_other); - LOG ("merging assigned %d and unassigned %d", lit, other); - const int unit = (val_lit < 0) ? -other : other; - if (internal->lrat) - lrat_chain.push_back (internal->unit_id (unit)); - if (val_lit < 0) - lrat_chain.push_back (c2->id); - else - lrat_chain.push_back (c1->id); - learn_congruence_unit (unit); - return false; - } - - if (!val_lit && val_other) { - LOG ("merging assigned %d and unassigned %d", other, lit); - const int unit = (val_other < 0) ? -lit : lit; - if (internal->lrat) - lrat_chain.push_back ( - internal->unit_id (val_other < 0 ? -other : other)); - if (val_lit < 0) - lrat_chain.push_back (c1->id); - else - lrat_chain.push_back (c2->id); - learn_congruence_unit (unit); - return false; - } - - int smaller_repr = repr_lit; - int larger_repr = repr_other; - int smaller = lit; - int larger = other; - - if (abs (smaller_repr) > abs (larger_repr)) { - swap (smaller_repr, larger_repr); - swap (smaller, larger); - } - - CADICAL_assert (find_representative (smaller_repr) == smaller_repr); - CADICAL_assert (find_representative (larger_repr) == larger_repr); - - if (repr_lit == -repr_other) { - LOG ("merging clashing %d [=%d] and %d[=%d], smaller: %d", lit, - repr_lit, other, repr_other, smaller); - if (internal->lrat) { - Rewrite rew1 = - Rewrite (lit, lit == repr_lit ? 0 : repr_lit, - lit == repr_lit ? 0 : find_representative_lrat (lit), - lit == repr_lit ? 0 : find_representative_lrat (-lit)); - Rewrite rew2 = Rewrite ( - other, other == repr_other ? 0 : repr_other, - other == repr_other ? 0 : find_representative_lrat (other), - other == repr_other ? 0 : find_representative_lrat (-other)); - push_id_and_rewriting_lrat_unit (c1, rew1, lrat_chain, true, rew2); - LOG (lrat_chain, "lrat chain"); - swap (internal->lrat_chain, lrat_chain); - } - internal->assign_unit (repr_lit); - if (internal->lrat) { - lrat_chain.clear (); - LRAT_ID id = internal->unit_id (repr_lit); - CADICAL_assert (id); - lrat_chain.push_back (id); - if (lit != repr_lit) { - const LRAT_ID repr_id2 = find_representative_lrat (-lit); - lrat_chain.push_back (repr_id2); - } - lrat_chain.push_back (id2); - if (other != repr_other) { - const LRAT_ID repr_larger_id2 = find_representative_lrat (other); - lrat_chain.push_back (repr_larger_id2); - } - LOG (lrat_chain, "lrat chain"); - swap (internal->lrat_chain, lrat_chain); - } - internal->learn_empty_clause (); - return false; - } - - LOG ("merging %d [=%d] and %d [=%d]", lit, repr_lit, other, repr_other); - promote_clause (c1), promote_clause (c2); - bool learn_clause = (lit != repr_lit) || (other != repr_other); - if (learn_clause) { - if (internal->lrat) { - if (lit != repr_lit) { - LOG ("adding chain for lit %d -> %d", lit, repr_lit); - lrat_chain.push_back (find_representative_lrat (lit)); - } - if (other != repr_other) { - LOG ("adding chain for lit %d -> %d", -other, -repr_other); - lrat_chain.push_back (find_representative_lrat (-other)); - } - lrat_chain.push_back (id1); - } - Clause *eq1 = learn_binary_tmp_or_full_clause (repr_lit, -repr_other); - - if (internal->lrat) { - lrat_chain.clear (); - if (lit != repr_lit) - lrat_chain.push_back (find_representative_lrat (-lit)); - if (other != repr_other) - lrat_chain.push_back (find_representative_lrat (other)); - lrat_chain.push_back (id2); - } - Clause *eq2 = learn_binary_tmp_or_full_clause (-repr_lit, repr_other); - if (internal->lrat) { - lrat_chain.clear (); - if (smaller_repr == repr_lit) { - CADICAL_assert (larger_repr == repr_other); - representative_id (-larger_repr) = eq2->id; - CADICAL_assert (std::find (eq2->begin (), eq2->end (), larger_repr) != - eq2->end ()); - representative_id (larger_repr) = eq1->id; - CADICAL_assert (std::find (eq1->begin (), eq1->end (), -larger_repr) != - eq1->end ()); - } else { - CADICAL_assert (larger_repr == repr_lit); - representative_id (-larger_repr) = eq1->id; - CADICAL_assert (std::find (eq1->begin (), eq1->end (), larger_repr) != - eq1->end ()); - representative_id (larger_repr) = eq2->id; - CADICAL_assert (std::find (eq2->begin (), eq2->end (), -larger_repr) != - eq2->end ()); - } - } - - } else if (internal->lrat) { - LOG ("not learning new clause, using already existing one"); - if (smaller_repr == repr_lit) { - LOG ("setting ids of %d: %" PRIu64 "; %d: %" PRIu64 " (case 1)", - larger, id1, -larger, id2); - representative_id (-larger_repr) = id2; - representative_id (larger_repr) = id1; - } else { - LOG ("setting ids of %d: %" PRIu64 "; %d: %" PRIu64 " (case 2)", - larger, id2, -larger, id1); - representative_id (-larger_repr) = id1; - representative_id (larger_repr) = id2; - } - } - - LOG ("updating %d -> %d", larger_repr, smaller_repr); - representative (larger_repr) = smaller_repr; - representative (-larger_repr) = -smaller_repr; - schedule_literal (larger_repr); - ++internal->stats.congruence.congruent; - return true; -} - -/*------------------------------------------------------------------------*/ -GOccs &Closure::goccs (int lit) { return gtab[internal->vlit (lit)]; } - -void Closure::connect_goccs (Gate *g, int lit) { - LOG (g, "connect %d to", lit); - // incorrect for ITE - // CADICAL_assert (std::find(begin (goccs (lit)), end (goccs (lit)), g) == - // std::end (goccs (lit))); - goccs (lit).push_back (g); -} - -uint64_t &Closure::largecount (int lit) { - CADICAL_assert (internal->vlit (lit) < glargecounts.size ()); - return glargecounts[internal->vlit (lit)]; -} - -/*------------------------------------------------------------------------*/ -// Initialization - -void Closure::init_closure () { - representant.resize (2 * internal->max_var + 3); - eager_representant.resize (2 * internal->max_var + 3); - if (internal->lrat) { - eager_representant_id.resize (2 * internal->max_var + 3); - representant_id.resize (2 * internal->max_var + 3); - lazy_propagated_idx.resize (internal->max_var + 1, false); - for (auto lit : internal->trail) - lazy_propagated (lit) = true; - } - marks.resize (2 * internal->max_var + 3); - mu1_ids.resize (2 * internal->max_var + 3); - if (internal->lrat) { - mu2_ids.resize (2 * internal->max_var + 3); - mu4_ids.resize (2 * internal->max_var + 3); - } -#ifndef CADICAL_NDEBUG - for (auto &it : mu1_ids) - it.current_lit = 0, it.clause = nullptr; - for (auto &it : mu2_ids) - it.current_lit = 0, it.clause = nullptr; - for (auto &it : mu4_ids) - it.current_lit = 0, it.clause = nullptr; -#endif - scheduled.resize (internal->max_var + 1); - gtab.resize (2 * internal->max_var + 3); - for (auto v : internal->vars) { - representative (v) = v; - representative (-v) = -v; - eager_representative (v) = v; - eager_representative (-v) = -v; - } - units = internal->propagated; - Random rand (internal->stats.congruence.rounds); - for (auto &n : nonces) { - n = 1 | rand.next (); - } -#ifdef LOGGING - fresh_id = internal->clause_id; -#endif - internal->init_noccs (); - internal->init_occs (); -} - -void Closure::init_and_gate_extraction () { - LOG ("[gate-extraction]"); - for (Clause *c : internal->clauses) { - if (c->garbage) - continue; - if (c->redundant && c->size != 2) - continue; - if (c->size > 2) - continue; - CADICAL_assert (c->size == 2); - const int lit = c->literals[0]; - const int other = c->literals[1]; - internal->noccs (lit)++; - internal->noccs (other)++; - internal->watch_clause (c); - } -} - -/*------------------------------------------------------------------------*/ -void Closure::check_and_gate_implied (Gate *g) { - CADICAL_assert (internal->clause.empty ()); - CADICAL_assert (g->tag == Gate_Type::And_Gate); - if (internal->lrat) - return; - LOG (g, "checking implied"); - const int lhs = g->lhs; - const int not_lhs = -lhs; - for (auto other : g->rhs) - check_binary_implied (not_lhs, other); - internal->clause = g->rhs; - check_implied (); - internal->clause.clear (); -} - -void Closure::delete_proof_chain () { - if (!internal->proof) { - CADICAL_assert (chain.empty ()); - return; - } - if (chain.empty ()) - return; - LOG ("starting deletion of proof chain"); - auto &clause = internal->clause; - CADICAL_assert (clause.empty ()); - uint32_t id1 = UINT32_MAX, id2 = UINT32_MAX; - LRAT_ID id = 0; - - LOG (chain, "chain:"); - for (auto lit : chain) { - LOG ("reading %d from chain", lit); - if (id1 == UINT32_MAX) { - id1 = lit; - id = (LRAT_ID) id1; - continue; - } - if (id2 == UINT32_MAX) { - id2 = lit; - id = ((LRAT_ID) id1 << 32) + id2; - continue; - } - if (lit) { // parsing the id first - LOG ("found %d as literal in chain", lit); - clause.push_back (lit); - } else { - CADICAL_assert (id); - internal->proof->delete_clause (id, false, clause); - clause.clear (); - id = 0, id1 = UINT32_MAX, id2 = UINT32_MAX; - } - } - CADICAL_assert (clause.empty ()); - chain.clear (); - LOG ("finished deletion of proof chain"); -} - -/*------------------------------------------------------------------------*/ -// Simplification -bool Closure::skip_and_gate (Gate *g) { - CADICAL_assert (g->tag == Gate_Type::And_Gate); - if (g->garbage) - return true; - const int lhs = g->lhs; - if (internal->val (lhs) > 0) { - mark_garbage (g); - return true; - } - - CADICAL_assert (g->arity () > 1); - return false; -} - -bool Closure::skip_xor_gate (Gate *g) { - CADICAL_assert (g->tag == Gate_Type::XOr_Gate); - if (g->garbage) - return true; - CADICAL_assert (g->arity () > 1); - return false; -} - -void Closure::shrink_and_gate (Gate *g, int falsifies, int clashing) { - if (falsifies) { - g->rhs.resize (1); - g->rhs[0] = falsifies; - g->hash = hash_lits (nonces, g->rhs); - } else if (clashing) { - LOG (g, "gate before clashing on %d", clashing); - g->rhs.resize (2); - g->rhs[0] = clashing; - g->rhs[1] = -clashing; - g->hash = hash_lits (nonces, g->rhs); - LOG (g, "gate after clashing on %d", clashing); - } - g->shrunken = true; -} - -void Closure::update_and_gate_unit_build_lrat_chain ( - Gate *g, int src, LRAT_ID id1, LRAT_ID id2, int dst, - std::vector &extra_reasons_lit, - std::vector &extra_reasons_ulit) { - LOG ("generate chain for gate boiling down to unit"); - if (g->neg_lhs_ids.size () != 1) { - - if (g->degenerated_and_neg || g->degenerated_and_pos) { - // can happen for 4 = AND 3 4 (degenerated with only the clause -4 3) - // with a rewriting 4 -> 1 (unchanged clause) - // and later 1 -> 3 (unchanged clause) - // but you do not know anymore from the gate that it is degenerated - CADICAL_assert (g->pos_lhs_ids.size () == 1); - push_id_and_rewriting_lrat_unit (g->pos_lhs_ids[0].clause, Rewrite (), - extra_reasons_ulit, true, Rewrite ()); - return; - } - CADICAL_assert (g->lhs == g->rhs[0] || (g->lhs == src && g->rhs[0] == dst)); - CADICAL_assert (g->pos_lhs_ids.size () <= 1); // either degenerated or empty A = A - return; - } - CADICAL_assert (g->neg_lhs_ids.size () == 1); - CADICAL_assert (!g->pos_lhs_ids.empty ()); - - const int repr_lit = find_representative (g->lhs); - const int repr_other = find_representative (g->rhs[0]); - if (repr_lit == repr_other) { - LOG ("skipping already merged"); - return; - } - - push_id_and_rewriting_lrat_unit (g->neg_lhs_ids[0].clause, Rewrite (), - extra_reasons_ulit, true, Rewrite (), - g->lhs, -dst); - LOG (extra_reasons_ulit, "lrat chain for negative side"); - - lrat_chain.clear (); - - CADICAL_assert (!g->pos_lhs_ids.empty ()); - for (auto litId : g->pos_lhs_ids) - push_id_and_rewriting_lrat_unit ( - litId.clause, Rewrite (src, dst, id1, id2), extra_reasons_lit, true, - Rewrite (), -g->lhs, dst); - LOG (extra_reasons_lit, "lrat chain for positive side"); -} - -void Closure::update_and_gate_build_lrat_chain ( - Gate *g, Gate *h, std::vector &extra_reasons_lit, - std::vector &extra_reasons_ulit, bool remove_units) { - CADICAL_assert (g != h); - LOG (g, "merging"); - LOG (h, "with"); - // If the LHS are identical, do not even attempt to build the LRAT chain - if (find_representative (g->lhs) == find_representative (h->lhs)) - return; - // set to same value, don't do anything - if (internal->val (g->lhs) && internal->val (g->lhs) == internal->val (h->lhs)) - return; - const bool g_tautology = gate_contains (g, g->lhs); - const bool h_tautology = gate_contains (h, h->lhs); - if (g_tautology && h_tautology) { - LOG ("both gates are a tautology"); - // special case: actually we have an equivalence due to binary clauses - // and all gate clauses (except one binary) are actually tautologies - for (auto &litId : g->pos_lhs_ids) { - if (litId.current_lit == h->lhs) { - CADICAL_assert (extra_reasons_lit.empty ()); - LOG (litId.clause, "binary clause to push into the reason"); - litId.clause = produce_rewritten_clause_lrat (litId.clause, g->lhs, - remove_units); - CADICAL_assert (litId.clause); - extra_reasons_lit.push_back (litId.clause->id); - } - } - CADICAL_assert (!extra_reasons_lit.empty ()); - CADICAL_assert (extra_reasons_lit.size () == 1); - - for (auto &litId : h->pos_lhs_ids) { - if (litId.current_lit == g->lhs) { - CADICAL_assert (extra_reasons_ulit.empty ()); - CADICAL_assert (litId.clause); - LOG (litId.clause, "binary clause to push into the reason"); - litId.clause = produce_rewritten_clause_lrat (litId.clause, h->lhs, - remove_units); - CADICAL_assert (litId.clause); - extra_reasons_ulit.push_back (litId.clause->id); - } - } - CADICAL_assert (!extra_reasons_ulit.empty ()); - CADICAL_assert (extra_reasons_ulit.size () == 1); - return; - } - if (g_tautology || h_tautology) { - // special case: actually we have an equivalence due to binary clauses - // and some of the clauses from the gate are actually tautologies - CADICAL_assert (g_tautology != h_tautology); - Gate *tauto = (g_tautology ? g : h); - Gate *other = (g_tautology ? h : g); - LOG (tauto, "one gate is a tautology"); - CADICAL_assert (tauto != other); - CADICAL_assert (tauto == h || tauto == g); - - auto &extra_reasons_tauto = - (!g_tautology ? extra_reasons_lit : extra_reasons_ulit); - auto &extra_reasons_other = - (!g_tautology ? extra_reasons_ulit : extra_reasons_lit); - - // one direction: the binary clause already exists - for (auto &litId : other->pos_lhs_ids) { - if (litId.current_lit == tauto->lhs) { - CADICAL_assert (litId.clause); - CADICAL_assert (extra_reasons_tauto.empty ()); - LOG (litId.clause, "binary clause to push into the reason"); - litId.clause = produce_rewritten_clause_lrat ( - litId.clause, other->lhs, remove_units); - CADICAL_assert (litId.clause); - extra_reasons_tauto.push_back (litId.clause->id); - } - } - CADICAL_assert (!extra_reasons_tauto.empty ()); - - // other direction, we have to resolve - LOG (tauto, "now the other direction"); - for (auto &litId : tauto->pos_lhs_ids) { - CADICAL_assert (litId.clause); - LOG (litId.clause, - "binary clause from %d to push into the reason [avoiding %d]", - litId.current_lit, tauto->lhs); - if (litId.current_lit != tauto->lhs) { - LOG (litId.clause, "binary clause to push into the reason"); - CADICAL_assert (litId.clause); - litId.clause = produce_rewritten_clause_lrat ( - litId.clause, tauto->lhs, remove_units); - if (!litId.clause) // degenerated but does not know yet - continue; - CADICAL_assert (litId.clause); - extra_reasons_other.push_back (litId.clause->id); - } - tauto->pos_lhs_ids.erase (std::remove_if (begin (tauto->pos_lhs_ids), - end (tauto->pos_lhs_ids), - [] (const LitClausePair &p) { - return !p.clause; - }), - end (tauto->pos_lhs_ids)); - } - CADICAL_assert (!extra_reasons_other.empty ()); - produce_rewritten_clause_lrat_and_clean (other->neg_lhs_ids, other->lhs, - remove_units); - push_id_on_chain (extra_reasons_other, other->neg_lhs_ids); - CADICAL_assert (!extra_reasons_tauto.empty ()); - CADICAL_assert (!extra_reasons_other.empty ()); - return; - } - // default: resolve all clauses - // first rewrite - // TODO: do we really need dest as second exclusion? - produce_rewritten_clause_lrat_and_clean (h->pos_lhs_ids, -h->lhs, - remove_units); - CADICAL_assert (internal->clause.empty ()); - produce_rewritten_clause_lrat_and_clean (h->neg_lhs_ids, -h->lhs, - remove_units); - CADICAL_assert (internal->clause.empty ()); - produce_rewritten_clause_lrat_and_clean (g->pos_lhs_ids, -g->lhs, - remove_units); - CADICAL_assert (internal->clause.empty ()); - produce_rewritten_clause_lrat_and_clean (g->neg_lhs_ids, -g->lhs, - remove_units); - CADICAL_assert (internal->clause.empty ()); - - push_id_on_chain (extra_reasons_ulit, h->pos_lhs_ids); - push_id_on_chain (extra_reasons_ulit, g->neg_lhs_ids[0].clause); - lrat_chain.clear (); - LOG (extra_reasons_ulit, "lrat chain for negative side"); - - push_id_on_chain (extra_reasons_lit, g->pos_lhs_ids); - push_id_on_chain (extra_reasons_lit, h->neg_lhs_ids); - - lrat_chain.clear (); - LOG (extra_reasons_lit, "lrat chain for positive side"); - CADICAL_assert (!extra_reasons_lit.empty ()); - CADICAL_assert (!extra_reasons_ulit.empty ()); -} - -void Closure::update_and_gate (Gate *g, GatesTable::iterator it, int src, - int dst, LRAT_ID id1, LRAT_ID id2, - int falsifies, int clashing) { - LOG (g, "update and gate of arity %ld", g->arity ()); - CADICAL_assert (lrat_chain.empty ()); - bool garbage = true; - if (falsifies || clashing) { - if (internal->lrat) - learn_congruence_unit_falsifies_lrat_chain (g, src, dst, clashing, - falsifies, 0); - int unit = -g->lhs; - if (unit == src) - unit = dst; - else if (unit == -src) - unit = -dst; - learn_congruence_unit (unit); - if (internal->lrat) - lrat_chain.clear (); - } else if (g->arity () == 1) { - const signed char v = internal->val (g->lhs); - if (v > 0) { - if (internal->lrat) - learn_congruence_unit_falsifies_lrat_chain (g, src, dst, 0, 0, - g->lhs); - learn_congruence_unit (g->rhs[0]); - if (internal->lrat) - lrat_chain.clear (); - } else if (v < 0) { - if (internal->lrat) - learn_congruence_unit_when_lhs_set (g, src, id1, id2, dst); - learn_congruence_unit (-g->rhs[0]); - if (internal->lrat) - lrat_chain.clear (); - } else { - std::vector extra_reasons_lit; - std::vector extra_reasons_ulit; - if (internal->lrat) - update_and_gate_unit_build_lrat_chain (g, src, id1, id2, g->rhs[0], - extra_reasons_lit, - extra_reasons_ulit); - if (merge_literals_lrat (g, g, g->lhs, g->rhs[0], extra_reasons_lit, - extra_reasons_ulit)) { - ++internal->stats.congruence.unaries; - ++internal->stats.congruence.unary_and; - } - } - } else { - CADICAL_assert (g->arity () > 1); - sort_literals_by_var (g->rhs); - Gate *h = find_and_lits (g->rhs, g); - CADICAL_assert (g != h); - if (h) { - CADICAL_assert (garbage); - std::vector extra_reasons_lit2; - std::vector extra_reasons_ulit2; - if (internal->lrat) - update_and_gate_build_lrat_chain (g, h, extra_reasons_lit2, - extra_reasons_ulit2); - if (merge_literals_lrat (g, h, g->lhs, h->lhs, extra_reasons_lit2, - extra_reasons_ulit2)) - ++internal->stats.congruence.ands; - } else { - if (g->indexed) { - LOG (g, "removing from table"); - (void) table.erase (it); - } - g->hash = hash_lits (nonces, g->rhs); - LOG (g, "inserting gate into table"); - CADICAL_assert (table.count (g) == 0); - table.insert (g); - g->indexed = true; - garbage = false; - if (internal->lrat) - lrat_chain.clear (); - } - } - - if (garbage && !internal->unsat) - mark_garbage (g); -} - -void Closure::update_xor_gate (Gate *g, GatesTable::iterator git) { - CADICAL_assert (g->tag == Gate_Type::XOr_Gate); - CADICAL_assert (!internal->unsat && chain.empty ()); - LOG (g, "updating"); - bool garbage = true; - // TODO Florian LRAT for learn_congruence_unit - CADICAL_assert (g->arity () == 0 || internal->clause.empty ()); - CADICAL_assert (clause.empty ()); - if (g->arity () == 0) { - if (internal->clause.size ()) { - CADICAL_assert (!internal->proof || (internal->clause.size () == 1 && - internal->clause.back () == -g->lhs)); - CADICAL_assert (!internal->lrat || lrat_chain.size ()); - internal->clause.clear (); - - } else if (internal->lrat) { - simplify_unit_xor_lrat_clauses (g->pos_lhs_ids, g->lhs); - CADICAL_assert (clause.size () && clause.back () == -g->lhs); - clause.clear (); - } - - if (internal->lrat && internal->val (-g->lhs) < 0) { - internal->lrat_chain.push_back (internal->unit_id (g->lhs)); - for (auto id : lrat_chain) - internal->lrat_chain.push_back (id); - lrat_chain.clear (); - internal->learn_empty_clause(); - } else - learn_congruence_unit (-g->lhs); - - CADICAL_assert (clause.empty ()); - } else if (g->arity () == 1) { - std::vector reasons_implication, reasons_back; - if (internal->lrat) { - vector first; - simplify_and_sort_xor_lrat_clauses (g->pos_lhs_ids, first, g->lhs); - g->pos_lhs_ids = first; - CADICAL_assert (g->pos_lhs_ids.size () == 2); - reasons_implication.push_back (g->pos_lhs_ids[0].clause->id); - reasons_back.push_back (g->pos_lhs_ids[1].clause->id); - } - const signed char v = internal->val (g->lhs); - if (v > 0) { - if (internal->lrat) { - push_lrat_unit (g->lhs); - lrat_chain.push_back (g->pos_lhs_ids[0].clause->id); - } - learn_congruence_unit (g->rhs[0]); - } else if (v < 0) { - if (internal->lrat) { - push_lrat_unit (-g->lhs); - lrat_chain.push_back (g->pos_lhs_ids[1].clause->id); - } - learn_congruence_unit (-g->rhs[0]); - } else if (merge_literals_lrat ( - g->lhs, g->rhs[0], reasons_implication, - reasons_back)) { // TODO Florian merge with LRAT - ++internal->stats.congruence.unaries; - ++internal->stats.congruence.unary_and; - } - CADICAL_assert (clause.empty ()); - } else { - Gate *h = find_xor_gate (g); - if (h) { - CADICAL_assert (garbage); - std::vector reasons_implication, reasons_back; - add_xor_matching_proof_chain (g, g->lhs, h->pos_lhs_ids, h->lhs, - reasons_implication, reasons_back); - if (merge_literals_lrat (g->lhs, h->lhs, reasons_implication, - reasons_back)) - ++internal->stats.congruence.xors; - delete_proof_chain (); - } else { - if (g->indexed) { - remove_gate (git); - } - g->hash = hash_lits (nonces, g->rhs); - LOG (g, "reinserting in table"); - table.insert (g); - g->indexed = true; - CADICAL_assert (table.find (g) != end (table)); - garbage = false; - } - CADICAL_assert (clause.empty ()); - } - if (garbage && !internal->unsat) - mark_garbage (g); - CADICAL_assert (clause.empty ()); -} - -void Closure::simplify_and_gate (Gate *g) { - if (skip_and_gate (g)) - return; - GatesTable::iterator git = (g->indexed ? table.find (g) : end (table)); - CADICAL_assert (!g->indexed || git != end (table)); - LOG (g, "simplifying"); - int falsifies = 0; - std::vector::iterator it = begin (g->rhs); - bool ulhs_in_rhs = false; - for (auto lit : g->rhs) { - const signed char v = internal->val (lit); - if (v > 0) { - continue; - } - if (v < 0) { - falsifies = lit; - continue; - } - if (lit == -g->lhs) - ulhs_in_rhs = true; - *it++ = lit; - if (lit == g->lhs) - g->degenerated_and_pos = true; - if (lit == -g->lhs) - g->degenerated_and_neg = true; - } - - if (internal->lrat) { // updating reasons - size_t i = 0, size = g->pos_lhs_ids.size (); - for (size_t j = 0; j < size; ++j) { - LOG ("looking at %d [%ld %ld]", g->pos_lhs_ids[j].current_lit, i, j); - g->pos_lhs_ids[i] = g->pos_lhs_ids[j]; - if (!g->degenerated_and_pos && - internal->val (g->pos_lhs_ids[i].current_lit) && - g->pos_lhs_ids[i].current_lit != falsifies) - continue; - LOG ("keeping %d [%ld %ld]", g->pos_lhs_ids[i].current_lit, i, j); - ++i; - } - LOG ("resizing to %ld", i); - g->pos_lhs_ids.resize (i); - } - - CADICAL_assert (it <= end (g->rhs)); // can be equal when ITE are converted to - // ands leading to - CADICAL_assert (it >= begin (g->rhs)); - LOG (g, "shrunken"); - - g->shrunken = true; - g->rhs.resize (it - std::begin (g->rhs)); - g->hash = hash_lits (nonces, g->rhs); - - LOG (g, "shrunken"); - shrink_and_gate (g, falsifies); - std::vector reasons_lrat_src, reasons_lrat_usrc; - - update_and_gate (g, git, 0, 0, 0, 0, falsifies, 0); - ++internal->stats.congruence.simplified_ands; - ++internal->stats.congruence.simplified; - - if (ulhs_in_rhs) { // missing in Kissat, TODO: port back - CADICAL_assert (gate_contains (g, -g->lhs)); - if (internal->lrat) { - for (auto litId : g->pos_lhs_ids) { - if (litId.current_lit == g->lhs) { - compute_rewritten_clause_lrat_simple (litId.clause, 0); - break; - } - } - } - learn_congruence_unit (-g->lhs); - } -} - -bool Closure::simplify_gate (Gate *g) { - switch (g->tag) { - case Gate_Type::And_Gate: - simplify_and_gate (g); - break; - case Gate_Type::XOr_Gate: - simplify_xor_gate (g); - break; - case Gate_Type::ITE_Gate: - simplify_ite_gate (g); - break; - default: - CADICAL_assert (false); - break; - } - CADICAL_assert (lrat_chain.empty ()); - return !internal->unsat; -} - -bool Closure::simplify_gates (int lit) { - const auto &occs = goccs (lit); - for (Gate *g : occs) { - CADICAL_assert (lrat_chain.empty ()); - CADICAL_assert (clause.empty ()); - if (!simplify_gate (g)) - return false; - } - return true; -} -/*------------------------------------------------------------------------*/ -// AND gates - -Gate *Closure::find_and_lits (const vector &rhs, Gate *except) { - CADICAL_assert (is_sorted (begin (rhs), end (rhs), - sort_literals_by_var_smaller (internal))); - return find_gate_lits (rhs, Gate_Type::And_Gate, except); -} - -// search for the gate in the hash-table. We cannot use find, as we might -// be changing a gate, so there might be 2 gates with the same LHS (the one -// we are changing and the other we are looking for) -Gate *Closure::find_gate_lits (const vector &rhs, Gate_Type typ, - Gate *except) { - Gate *g = new Gate; - g->tag = typ; - g->rhs = rhs; - g->hash = hash_lits (nonces, g->rhs); - g->lhs = 0; - g->garbage = false; -#ifdef LOGGING - g->id = 0; -#endif - const auto &its = table.equal_range (g); - Gate *h = nullptr; - for (auto it = its.first; it != its.second; ++it) { - LOG ((*it), "checking gate in the table"); - if (*it == except) - continue; - CADICAL_assert ((*it)->lhs != g->lhs); - if ((*it)->tag != g->tag) - continue; - if ((*it)->rhs != g->rhs) - continue; - h = *it; - break; - } - - if (h) { - LOG (g, "searching"); - LOG (h, "already existing"); - delete g; - return h; - } - - else { - LOG (g->rhs, "gate not found in table"); - delete g; - return nullptr; - } -} - -Gate *Closure::new_and_gate (Clause *base_clause, int lhs) { - rhs.clear (); - auto &lits = this->lits; - - for (auto lit : lits) { - if (lhs != lit) { - CADICAL_assert (lhs != -lit); - rhs.push_back (-lit); - } - } - - CADICAL_assert (rhs.size () + 1 == lits.size ()); - sort_literals_by_var (this->rhs); - - Gate *h = find_and_lits (this->rhs); - Gate *g = new Gate; - g->lhs = lhs; - g->tag = Gate_Type::And_Gate; - if (internal->lrat) { - g->neg_lhs_ids.push_back (LitClausePair (lhs, base_clause)); - for (auto i : lrat_chain_and_gate) - g->pos_lhs_ids.push_back (i); -#ifdef LOGGING - std::vector result; - transform (begin (g->pos_lhs_ids), end (g->pos_lhs_ids), - back_inserter (result), - [] (const LitClausePair &x) { return x.clause->id; }); - LOG (result, "lrat chain positive (%d):", lhs); - result.clear (); - transform (begin (g->neg_lhs_ids), end (g->neg_lhs_ids), - back_inserter (result), - [] (const LitClausePair &x) { return x.clause->id; }); - LOG (result, "lrat chain negative (%d):", lhs); -#endif - } - - if (internal->lrat) - lrat_chain_and_gate.clear (); - - if (h) { - std::vector reasons_lrat_src, reasons_lrat_usrc; - if (internal->lrat) - merge_and_gate_lrat_produce_lrat (g, h, reasons_lrat_src, - reasons_lrat_usrc); - if (merge_literals_lrat (g, h, lhs, h->lhs, reasons_lrat_src, - reasons_lrat_usrc)) { - LOG ("found merged literals"); - ++internal->stats.congruence.ands; - } - return nullptr; - } else { - g->rhs = {rhs}; - CADICAL_assert (!internal->lrat || - g->pos_lhs_ids.size () == - g->arity ()); // otherwise we need intermediate clauses - g->garbage = false; - g->indexed = true; - g->shrunken = false; - g->hash = hash_lits (nonces, g->rhs); - - table.insert (g); - ++internal->stats.congruence.gates; -#ifdef LOGGING - g->id = fresh_id++; -#endif - LOG (g, "creating new"); - for (auto lit : g->rhs) { - connect_goccs (g, lit); - } - } - return g; -} - -Gate *Closure::find_first_and_gate (Clause *base_clause, int lhs) { - CADICAL_assert (internal->analyzed.empty ()); - const int not_lhs = -lhs; - LOG ("trying to find AND gate with first LHS %d", (lhs)); - LOG ("negated LHS %d occurs in %zd binary clauses", (not_lhs), - internal->occs (not_lhs).size ()); - unsigned matched = 0; - - const size_t arity = lits.size () - 1; - - for (auto w : internal->watches (not_lhs)) { - LOG (w.clause, "checking clause for candidates"); - CADICAL_assert (w.binary ()); - CADICAL_assert (w.clause->size == 2); - CADICAL_assert (w.clause->literals[0] == -lhs || w.clause->literals[1] == -lhs); - const int other = w.blit; - signed char &mark = marked (other); - if (mark) { - LOG ("marking %d mu2", other); - ++matched; - CADICAL_assert (~(mark & 2)); - mark |= 2; - internal->analyzed.push_back (other); - set_mu2_reason (other, w.clause); - if (internal->lrat) - lrat_chain_and_gate.push_back (LitClausePair (other, w.clause)); - } - } - - LOG ("found %zd initial LHS candidates", internal->analyzed.size ()); - if (matched < arity) { - if (internal->lrat) - lrat_chain_and_gate.clear (); - return nullptr; - } - - Gate *g = new_and_gate (base_clause, lhs); - - if (internal->lrat) { - lrat_chain_and_gate.clear (); - } - return g; -} - -Clause *Closure::learn_binary_tmp_or_full_clause (int a, int b) { - Clause *eq1; - if (internal->lrat) { - eq1 = add_tmp_binary_clause (a, b); - eq1 = maybe_promote_tmp_binary_clause (eq1); - } else - eq1 = maybe_add_binary_clause (a, b); - return eq1; -} - -Clause *Closure::maybe_add_binary_clause (int a, int b) { - CADICAL_assert (internal->clause.empty ()); - CADICAL_assert (internal->lrat_chain.empty ()); - CADICAL_assert (!internal->lrat); - CADICAL_assert (lrat_chain.empty ()); - LOG ("learning binary clause %d %d", a, b); - if (internal->unsat) - return nullptr; - if (a == -b) - return nullptr; - if (!internal->lrat) { - const signed char a_value = internal->val (a); - if (a_value > 0) - return nullptr; - const signed char b_value = internal->val (b); - if (b_value > 0) - return nullptr; - int unit = 0; - if (a == b) - unit = a; - else if (a_value < 0 && !b_value) { - unit = b; - } else if (!a_value && b_value < 0) - unit = a; - if (unit) { - LOG ("clause reduced to unit %d", unit); - learn_congruence_unit (unit); - return nullptr; - } - CADICAL_assert (!a_value), CADICAL_assert (!b_value); - } - return add_binary_clause (a, b); -} - -Clause *Closure::add_binary_clause (int a, int b) { - CADICAL_assert (internal->clause.empty ()); - internal->clause.push_back (a); - internal->clause.push_back (b); - if (internal->lrat) { - CADICAL_assert (lrat_chain.size () >= 1); - CADICAL_assert (internal->lrat_chain.empty ()); - swap (internal->lrat_chain, lrat_chain); - } - LOG (internal->lrat_chain, "chain"); - Clause *res = internal->new_hyper_ternary_resolved_clause_and_watch ( - false, full_watching); - const bool already_sorted = internal->vlit (a) < internal->vlit (b); - binaries.push_back (CompactBinary (res, res->id, already_sorted ? a : b, - already_sorted ? b : a)); - if (!full_watching) - new_unwatched_binary_clauses.push_back (res); - LOG (res, "learning clause"); - internal->clause.clear (); - if (internal->lrat) { - internal->lrat_chain.clear (); - } - CADICAL_assert (internal->clause.empty ()); - CADICAL_assert (internal->lrat_chain.empty ()); - return res; -} - -void Closure::check_not_tmp_binary_clause (Clause *c) { -#ifndef CADICAL_NDEBUG - CADICAL_assert (internal->lrat); - CADICAL_assert (internal->lrat_chain.empty ()); - CADICAL_assert (c->size == 2); - if (internal->val (c->literals[0]) || internal->val (c->literals[1])) - return; - CADICAL_assert (std::find (begin (extra_clauses), end (extra_clauses), c) == - end (extra_clauses)); -#else - (void) c; -#endif -}; - -Clause *Closure::maybe_promote_tmp_binary_clause (Clause *c) { - CADICAL_assert (internal->lrat); - CADICAL_assert (internal->lrat_chain.empty ()); - CADICAL_assert (c->size == 2); - LOG (c, "promoting tmp"); -#ifndef CADICAL_NDEBUG - CADICAL_assert (std::find (begin (extra_clauses), end (extra_clauses), c) != - end (extra_clauses)); -#endif - if (internal->val (c->literals[0]) || internal->val (c->literals[1])) - return c; - lrat_chain.push_back (c->id); - Clause *res = add_binary_clause (c->literals[0], c->literals[1]); - LOG (res, "promoted to"); - return res; -}; - -Clause *Closure::add_tmp_binary_clause (int a, int b) { - CADICAL_assert (internal->clause.empty ()); - CADICAL_assert (internal->lrat_chain.empty ()); - CADICAL_assert (internal->lrat); - LOG ("learning tmp binary clause %d %d", a, b); - if (internal->unsat) - return nullptr; - if (a == -b) - return nullptr; - CADICAL_assert (internal->clause.empty ()); - internal->clause.push_back (a); - internal->clause.push_back (b); - if (internal->lrat) { - CADICAL_assert (lrat_chain.size () >= 1); - CADICAL_assert (internal->lrat_chain.empty ()); - } - LOG (lrat_chain, "chain"); - Clause *res = new_tmp_clause (internal->clause); - internal->clause.clear (); - if (internal->lrat) { - lrat_chain.clear (); - } - CADICAL_assert (internal->clause.empty ()); - CADICAL_assert (internal->lrat_chain.empty ()); - LOG (res, "promoted to"); - return res; -} - -Gate *Closure::find_remaining_and_gate (Clause *base_clause, int lhs) { - const int not_lhs = -lhs; - - if (marked (not_lhs) < 2) { - LOG ("skipping no-candidate LHS %d (%d)", lhs, marked (not_lhs)); - return nullptr; - } - - LOG ("trying to find AND gate with remaining LHS %d", (lhs)); - LOG ("negated LHS %d occurs times in %zd binary clauses", (not_lhs), - internal->noccs (-lhs)); - - const size_t arity = lits.size () - 1; - size_t matched = 0; - CADICAL_assert (1 < arity); - - for (auto w : internal->watches (not_lhs)) { - CADICAL_assert (w.binary ()); -#ifdef LOGGING - Clause *c = w.clause; - LOG (c, "checking"); - CADICAL_assert (c->size == 2); - CADICAL_assert (c->literals[0] == not_lhs || c->literals[1] == not_lhs); -#endif - const int other = w.blit; - signed char &mark = marked (other); - if (!mark) - continue; - ++matched; - if (!(mark & 2)) { - lrat_chain_and_gate.push_back (LitClausePair (other, w.clause)); - LOG ("pushing %d -> %zd", other, w.clause->id); - continue; - } - LOG ("marking %d mu4", other); - CADICAL_assert (!(mark & 4)); - mark |= 4; - lrat_chain_and_gate.push_back (LitClausePair (other, w.clause)); - if (internal->lrat) - set_mu4_reason (other, w.clause); - } - - { - auto q = begin (internal->analyzed); - CADICAL_assert (!internal->analyzed.empty ()); - CADICAL_assert (marked (not_lhs) == 3); - for (auto lit : internal->analyzed) { - signed char &mark = marked (lit); - if (lit == not_lhs) { - mark = 1; - continue; - } - - CADICAL_assert ((mark & 3) == 3); - if (mark & 4) { - mark = 3; - *q = lit; - ++q; - LOG ("keeping LHS candidate %d", -lit); - } else { - LOG ("dropping LHS candidate %d", -lit); - mark = 1; - } - } - CADICAL_assert (q != end (internal->analyzed)); - CADICAL_assert (marked (not_lhs) == 1); - internal->analyzed.resize (q - begin (internal->analyzed)); - LOG ("after filtering %zu LHS candidate remain", - internal->analyzed.size ()); - } - - if (matched < arity) { - if (internal->lrat) - lrat_chain_and_gate.clear (); - return nullptr; - } - - if (!internal->lrat) - lrat_chain_and_gate.clear (); - return new_and_gate (base_clause, lhs); -} - -struct congruence_occurrences_rank { - Internal *internal; - congruence_occurrences_rank (Internal *s) : internal (s) {} - typedef uint64_t Type; - Type operator() (int a) { - uint64_t res = internal->noccs (-a); - res <<= 32; - res |= a; - return res; - } -}; - -struct congruence_occurrences_larger { - Internal *internal; - congruence_occurrences_larger (Internal *s) : internal (s) {} - bool operator() (const int &a, const int &b) const { - return congruence_occurrences_rank (internal) (a) < - congruence_occurrences_rank (internal) (b); - } -}; - -void Closure::extract_and_gates_with_base_clause (Clause *c) { - CADICAL_assert (!c->garbage); - CADICAL_assert (lrat_chain.empty ()); - LOG (c, "extracting and gates with clause"); - unsigned size = 0; - const unsigned arity_limit = - min (internal->opts.congruenceandarity, MAX_ARITY); - const unsigned size_limit = arity_limit + 1; - size_t max_negbincount = 0; - lits.clear (); - - for (int lit : *c) { - signed char v = internal->val (lit); - if (v < 0) { - // push_lrat_unit (-lit); - continue; - } - if (v > 0) { - CADICAL_assert (!internal->level); - LOG (c, "found satisfied clause"); - internal->mark_garbage (c); - if (internal->lrat) - lrat_chain.clear (); - return; - } - if (++size > size_limit) { - LOG (c, "clause is actually too large, thus skipping"); - if (internal->lrat) - lrat_chain.clear (); - return; - } - const size_t count = internal->noccs (-lit); - if (!count) { - LOG (c, - "%d negated does not occur in any binary clause, thus skipping", - lit); - if (internal->lrat) - lrat_chain.clear (); - return; - } - - if (count > max_negbincount) - max_negbincount = count; - lits.push_back (lit); - } - - if (size < 3) { - LOG (c, "is actually too small, thus skipping"); - if (internal->lrat) - lrat_chain.clear (); - CADICAL_assert (lrat_chain.empty ()); - return; - } - - const size_t arity = size - 1; - if (max_negbincount < arity) { - LOG (c, - "all literals have less than %lu negated occurrences" - "thus skipping", - arity); - if (internal->lrat) - lrat_chain.clear (); - return; - } - - internal->analyzed.clear (); - size_t reduced = 0; - const size_t clause_size = lits.size (); - for (size_t i = 0; i < clause_size; ++i) { - const int lit = lits[i]; - const unsigned count = internal->noccs (-lit); - marked (-lit) = 1; - set_mu1_reason (-lit, c); - if (count < arity) { - if (reduced < i) { - lits[i] = lits[reduced]; - lits[reduced++] = lit; - } else if (reduced == i) - ++reduced; - } - } - const size_t reduced_size = clause_size - reduced; - CADICAL_assert (reduced_size); - LOG (c, "trying as base arity %lu AND gate", arity); - CADICAL_assert (begin (lits) + reduced_size <= end (lits)); - MSORT (internal->opts.radixsortlim, begin (lits), - begin (lits) + reduced_size, - congruence_occurrences_rank (internal), - congruence_occurrences_larger (internal)); - bool first = true; - unsigned extracted = 0; - - for (size_t i = 0; i < clause_size; ++i) { - CADICAL_assert (lrat_chain.empty ()); - if (internal->unsat) - break; - if (c->garbage) - break; - const int lhs = lits[i]; - LOG ("trying LHS candidate literal %d with %ld negated occurrences", - (lhs), internal->noccs (-lhs)); - - if (first) { - first = false; - CADICAL_assert (internal->analyzed.empty ()); - if (find_first_and_gate (c, lhs) != nullptr) { - CADICAL_assert (lrat_chain.empty ()); - ++extracted; - } - } else if (internal->analyzed.empty ()) { - LOG ("early abort AND gate search"); - break; - } else if (find_remaining_and_gate (c, lhs)) { - CADICAL_assert (lrat_chain.empty ()); - ++extracted; - } - } - - unmark_all (); - LOG (lits, "finish unmarking"); - for (auto lit : lits) { - marked (-lit) = 0; - } - lrat_chain_and_gate.clear (); - if (extracted) - LOG (c, "extracted %u with arity %lu AND base", extracted, arity); -} - -void Closure::reset_and_gate_extraction () { - internal->clear_noccs (); - internal->clear_watches (); -} - -void Closure::extract_and_gates () { - CADICAL_assert (!full_watching); - if (!internal->opts.congruenceand) - return; - START (extractands); - marks.resize (internal->max_var * 2 + 3); - init_and_gate_extraction (); - - const size_t size = internal->clauses.size (); - for (size_t i = 0; i < size && !internal->terminated_asynchronously (); - ++i) { // we can learn new binary clauses, but no for loop - CADICAL_assert (lrat_chain.empty ()); - Clause *c = internal->clauses[i]; - if (c->garbage) - continue; - if (c->size == 2) - continue; - if (c->hyper) - continue; - if (c->redundant) - continue; - extract_and_gates_with_base_clause (c); - CADICAL_assert (lrat_chain.empty ()); - } - - reset_and_gate_extraction (); - STOP (extractands); -} - -/*------------------------------------------------------------------------*/ -// XOR gates - -uint64_t &Closure::new_largecounts (int lit) { - CADICAL_assert (internal->vlit (lit) < gnew_largecounts.size ()); - return gnew_largecounts[internal->vlit (lit)]; -} - -uint64_t &Closure::largecounts (int lit) { - CADICAL_assert (internal->vlit (lit) < glargecounts.size ()); - return glargecounts[internal->vlit (lit)]; -} - -bool parity_lits (const vector &lits) { - unsigned res = 0; - for (auto lit : lits) - res ^= (lit < 0); - return res; -} - -void inc_lits (vector &lits) { - bool carry = true; - for (size_t i = 0; i < lits.size () && carry; ++i) { - int lit = lits[i]; - carry = (lit < 0); - lits[i] = -lit; - } -} - -void Closure::check_ternary (int a, int b, int c) { - CADICAL_assert (internal->clause.empty ()); - if (internal->lrat) - return; - auto &clause = internal->clause; - CADICAL_assert (clause.empty ()); - clause.push_back (a); - clause.push_back (b); - clause.push_back (c); - internal->external->check_learned_clause (); - if (internal->proof) { - const LRAT_ID id = internal->clause_id++; - internal->proof->add_derived_clause (id, false, clause, {}); - internal->proof->delete_clause (id, false, clause); - } - - clause.clear (); -} - -void Closure::check_binary_implied (int a, int b) { - CADICAL_assert (internal->clause.empty ()); - if (internal->lrat) - return; - auto &clause = internal->clause; - CADICAL_assert (clause.empty ()); - clause.push_back (a); - clause.push_back (b); - check_implied (); - clause.clear (); -} - -void Closure::check_implied () { - if (internal->lrat) - return; - internal->external->check_learned_clause (); -} - -void Closure::add_xor_shrinking_proof_chain (Gate *g, int pivot) { - CADICAL_assert (internal->clause.empty ()); - CADICAL_assert (clause.empty ()); - if (!internal->proof) - return; - LOG (g, "starting XOR shrinking proof chain"); - vector first; - vector newclauses; - if (internal->lrat) { - simplify_and_sort_xor_lrat_clauses (g->pos_lhs_ids, first, g->lhs, - pivot); - gate_sort_lrat_reasons (first, pivot, g->lhs); - } - - auto &clause = internal->clause; - - const int lhs = g->lhs; - clause.push_back (-lhs); - for (auto lit : g->rhs) - clause.push_back (lit); - - const bool parity = (lhs > 0); - CADICAL_assert (parity == parity_lits (clause)); - const size_t size = clause.size (); - const unsigned end = 1u << (size - 1); - CADICAL_assert (!internal->lrat || first.size () == 2 * end); -#ifdef LOGGING - for (auto pair : first) { - LOG (pair.clause, "key %d", pair.current_lit); - } -#endif - // TODO Florian adjust indices of first depending on order... - // - for (unsigned i = 0; i != end; ++i) { - while (i && parity != parity_lits (clause)) - inc_lits (clause); - LOG (clause, "xor shrinking clause"); - if (!internal->lrat) { - clause.push_back (pivot); - check_and_add_to_proof_chain (clause); - clause.pop_back (); - clause.push_back (-pivot); - check_and_add_to_proof_chain (clause); - clause.pop_back (); - } - if (internal->lrat) { - CADICAL_assert (lrat_chain.empty ()); - lrat_chain.push_back (first[2 * i].clause->id); - lrat_chain.push_back (first[2 * i + 1].clause->id); - } - if (clause.size () > 1) { - if (internal->lrat) { - Clause *c = new_tmp_clause (clause); - newclauses.push_back (LitClausePair (0, c)); - lrat_chain.clear (); - } else { - check_and_add_to_proof_chain (clause); - } - } - if (clause.size () == 1) - return; - inc_lits (clause); - } - g->pos_lhs_ids.swap (newclauses); - - clause.clear (); -} - -void Closure::check_xor_gate_implied (Gate const *const g) { - CADICAL_assert (internal->clause.empty ()); - CADICAL_assert (g->tag == Gate_Type::XOr_Gate); - if (internal->lrat) { - return; - } - const int lhs = g->lhs; - LOG (g, "checking implied"); - auto &clause = internal->clause; - CADICAL_assert (clause.empty ()); - for (auto other : g->rhs) { - CADICAL_assert (other > 0); - clause.push_back (other); - } - clause.push_back (-lhs); - const unsigned arity = g->arity (); - const unsigned end = 1u << arity; - const bool parity = (lhs > 0); - - for (unsigned i = 0; i != end; ++i) { - while (i && parity_lits (clause) != parity) - inc_lits (clause); - internal->external->check_learned_clause (); - if (internal->proof) { - internal->proof->add_derived_clause (internal->clause_id, false, - clause, {}); - internal->proof->delete_clause (internal->clause_id, false, clause); - } - inc_lits (clause); - } - clause.clear (); -} - -Gate *Closure::find_xor_lits (const vector &rhs) { - CADICAL_assert (is_sorted (begin (rhs), end (rhs), - sort_literals_by_var_smaller (internal))); - return find_gate_lits (rhs, Gate_Type::XOr_Gate); -} - -Gate *Closure::find_xor_gate (Gate *g) { - CADICAL_assert (g->tag == Gate_Type::XOr_Gate); - CADICAL_assert (is_sorted (begin (g->rhs), end (g->rhs), - sort_literals_by_var_smaller (internal))); - return find_gate_lits (g->rhs, Gate_Type::XOr_Gate); -} - -void Closure::reset_xor_gate_extraction () { internal->clear_occs (); } - -bool Closure::normalize_ite_lits_gate (Gate *g) { - auto &rhs = g->rhs; - CADICAL_assert (rhs.size () == 3); - if (internal->lrat) - check_correct_ite_flags (g); - LOG (rhs, "RHS = "); - if (rhs[0] < 0) { - rhs[0] = -rhs[0]; - std::swap (rhs[1], rhs[2]); - if (internal->lrat) { - CADICAL_assert (g->pos_lhs_ids.size () == 4); - std::swap (g->pos_lhs_ids[0], g->pos_lhs_ids[2]); - std::swap (g->pos_lhs_ids[1], g->pos_lhs_ids[3]); - const int8_t flag = g->degenerated_ite; - const int8_t plus_then = flag & NO_PLUS_THEN; - const int8_t neg_then = flag & NO_NEG_THEN; - const int8_t plus_else = flag & NO_PLUS_ELSE; - const int8_t neg_else = flag & NO_NEG_ELSE; - g->degenerated_ite = (plus_then ? Special_ITE_GATE::NO_PLUS_ELSE - : Special_ITE_GATE::NORMAL) | - (neg_then ? Special_ITE_GATE::NO_NEG_ELSE - : Special_ITE_GATE::NORMAL) | - (plus_else ? Special_ITE_GATE::NO_PLUS_THEN - : Special_ITE_GATE::NORMAL) | - (neg_else ? Special_ITE_GATE::NO_NEG_THEN - : Special_ITE_GATE::NORMAL); - CADICAL_assert (g->pos_lhs_ids[0].current_lit == rhs[1]); - CADICAL_assert (g->pos_lhs_ids[2].current_lit == rhs[2]); - if (internal->lrat) - check_correct_ite_flags (g); - } - } - if (rhs[1] > 0) - return false; - if (internal->lrat) - check_correct_ite_flags (g); - rhs[1] = -rhs[1]; - rhs[2] = -rhs[2]; - LOG (rhs, "RHS = "); - if (internal->lrat) { - CADICAL_assert (g->pos_lhs_ids.size () == 4); - std::swap (g->pos_lhs_ids[0], g->pos_lhs_ids[1]); - std::swap (g->pos_lhs_ids[2], g->pos_lhs_ids[3]); - const int8_t flag = g->degenerated_ite; - const int8_t plus_then = flag & NO_PLUS_THEN; - const int8_t neg_then = flag & NO_NEG_THEN; - const int8_t plus_else = flag & NO_PLUS_ELSE; - const int8_t neg_else = flag & NO_NEG_ELSE; - g->degenerated_ite = (plus_then ? Special_ITE_GATE::NO_NEG_THEN - : Special_ITE_GATE::NORMAL) | - (neg_then ? Special_ITE_GATE::NO_PLUS_THEN - : Special_ITE_GATE::NORMAL) | - (plus_else ? Special_ITE_GATE::NO_NEG_ELSE - : Special_ITE_GATE::NORMAL) | - (neg_else ? Special_ITE_GATE::NO_PLUS_ELSE - : Special_ITE_GATE::NORMAL); - CADICAL_assert (g->pos_lhs_ids[0].current_lit == rhs[1]); - CADICAL_assert (g->pos_lhs_ids[2].current_lit == rhs[2]); - // incorrect as we have not negated the LHS yet! - // check_correct_ite_flags (g); - } - - LOG (g->rhs, "g/RHS = "); - return true; -} - -#ifndef CADICAL_NDEBUG -bool is_tautological_ite_gate (Gate *g) { - CADICAL_assert (g->tag == Gate_Type::ITE_Gate); - CADICAL_assert (g->rhs.size () == 3); - const int cond_lit = g->rhs[0]; - const int then_lit = g->rhs[1]; - const int else_lit = g->rhs[2]; - return cond_lit == then_lit || cond_lit == else_lit; -} -#endif - -Gate *Closure::find_ite_gate (Gate *g, bool &negate_lhs) { - negate_lhs = normalize_ite_lits_gate (g); - LOG (g, "post normalize"); - return find_gate_lits (g->rhs, Gate_Type::ITE_Gate, g); -} - -LRAT_ID Closure::check_and_add_to_proof_chain (vector &clause) { - internal->external->check_learned_clause (); - const LRAT_ID id = ++internal->clause_id; - if (internal->proof) { - if (internal->lrat) { - CADICAL_assert (internal->lrat_chain.empty ()); - CADICAL_assert (lrat_chain.size () >= 1); - } - internal->proof->add_derived_clause (id, true, clause, lrat_chain); - lrat_chain.clear (); - } - return id; -} - -void Closure::add_clause_to_chain (std::vector unsimplified, - LRAT_ID id) { - const uint32_t id2_higher = (id >> 32); - const uint32_t id2_lower = (uint32_t) (id & (LRAT_ID) (uint32_t) (-1)); - CADICAL_assert (id == ((LRAT_ID) id2_higher << 32) + (LRAT_ID) id2_lower); - chain.push_back (id2_higher); - chain.push_back (id2_lower); - LOG (unsimplified, "pushing to chain"); - chain.insert (end (chain), begin (unsimplified), end (unsimplified)); - chain.push_back (0); -} - -LRAT_ID Closure::simplify_and_add_to_proof_chain (vector &unsimplified, - LRAT_ID delete_id) { - vector &clause = internal->clause; - CADICAL_assert (clause.empty ()); -#ifndef CADICAL_NDEBUG - for (auto lit : unsimplified) { - CADICAL_assert (!(marked (lit) & 4)); - } -#endif - - bool trivial = false; - for (auto lit : unsimplified) { - signed char &lit_mark = marked (lit); - if (lit_mark & 4) - continue; - signed char ¬_lit_mark = marked (-lit); - if (not_lit_mark & 4) { - trivial = true; - break; - } - lit_mark |= 4; - clause.push_back (lit); - } - for (auto lit : clause) { - signed char &mark = marked (lit); - CADICAL_assert (mark & 4); - mark &= ~4u; - } - - LRAT_ID id = 0; - if (!trivial) { - if (delete_id) { - if (internal->proof) { - internal->proof->delete_clause (delete_id, true, clause); - lrat_chain.clear (); - } - } else { - id = check_and_add_to_proof_chain (clause); - add_clause_to_chain (clause, id); - } - } else { - LOG ("skipping trivial proof"); - lrat_chain.clear (); - } - clause.clear (); - return id; -} -/*------------------------------------------------------------------------*/ -void Closure::add_ite_turned_and_binary_clauses (Gate *g) { - if (!internal->proof) - return; - if (internal->lrat) - return; - LOG ("starting ITE turned AND supporting binary clauses"); - CADICAL_assert (unsimplified.empty ()); - CADICAL_assert (chain.empty ()); - int not_lhs = -g->lhs; - unsimplified.push_back (not_lhs); - unsimplified.push_back (g->rhs[0]); - simplify_and_add_to_proof_chain (unsimplified); - unsimplified.pop_back (); - unsimplified.push_back (g->rhs[1]); - simplify_and_add_to_proof_chain (unsimplified); - unsimplified.clear (); -} -void Closure::simplify_unit_xor_lrat_clauses ( - const vector &source, int lhs) { - CADICAL_assert (internal->lrat); - for (auto pair : source) { - compute_rewritten_clause_lrat_simple (pair.clause, lhs); - if (lrat_chain.size ()) - break; - } - CADICAL_assert (clause.size () == 1); -} -void Closure::simplify_and_sort_xor_lrat_clauses ( - const vector &source, vector &target, - int lhs, int except2, bool flip) { - CADICAL_assert (internal->lrat); - for (auto pair : source) { - Clause *c = produce_rewritten_clause_lrat (pair.clause, lhs); - if (c) { - target.push_back (LitClausePair (0, c)); - } - } - gate_sort_lrat_reasons (target, lhs, except2, flip); -} -void Closure::add_xor_matching_proof_chain ( - Gate *g, int lhs1, const vector &clauses2, int lhs2, - vector &to_lrat, vector &back_lrat) { - if (lhs1 == lhs2) - return; - if (!internal->proof) - return; - CADICAL_assert (unsimplified.empty ()); - unsimplified = g->rhs; - vector first; - vector second; - if (internal->lrat) { - simplify_and_sort_xor_lrat_clauses (g->pos_lhs_ids, first, lhs1); - simplify_and_sort_xor_lrat_clauses (clauses2, second, lhs2, 0, 1); - g->pos_lhs_ids = first; - } - LOG ("starting XOR matching proof"); - // for lrat - vector first_ids; - vector second_ids; - for (auto pair : first) { - bool first = pair.current_lit & 1; - int rest = pair.current_lit >> 1; - rest &= ~(1 << (g->rhs.size () - 1)); - if (first == (lhs1 > 0)) { - first_ids.push_back (LitIdPair (rest, pair.clause->id)); - } else { - second_ids.push_back (LitIdPair (rest, pair.clause->id)); - } - LOG (pair.clause, "key %d", pair.current_lit); - } - for (auto pair : second) { - bool first = pair.current_lit & 1; - int rest = pair.current_lit >> 1; - rest &= ~(1 << (g->rhs.size () - 1)); - if (first == (lhs2 < 0)) { - first_ids.push_back (LitIdPair (rest, pair.clause->id)); - } else { - second_ids.push_back (LitIdPair (rest, pair.clause->id)); - } - LOG (pair.clause, "key %d", pair.current_lit); - } - // TODO Florian: resort and ids after every round - do { - vector first_tmp; - vector second_tmp; - CADICAL_assert (!unsimplified.empty ()); - unsimplified.pop_back (); - const size_t size = unsimplified.size (); - CADICAL_assert (size < 32); - const size_t off = 1u << size; - for (size_t i = 0; i != off; ++i) { - int32_t n = 0; - if (internal->lrat) { - n = number_from_xor_reason_reversed (unsimplified); - CADICAL_assert (lrat_chain.empty ()); - for (auto pair : first_ids) { - if (pair.lit == n) - lrat_chain.push_back (pair.id); - } - CADICAL_assert (lrat_chain.size () == 2); - } - unsimplified.push_back (-lhs1); - unsimplified.push_back (lhs2); - const LRAT_ID id1 = simplify_and_add_to_proof_chain (unsimplified); - unsimplified.resize (unsimplified.size () - 2); - if (internal->lrat) { - int32_t rest = n &= ~(1 << (unsimplified.size () - 1)); - first_tmp.push_back (LitIdPair (rest, id1)); - n = number_from_xor_reason_reversed (unsimplified); - lrat_chain.clear (); - for (auto pair : second_ids) { - if (pair.lit == n) - lrat_chain.push_back (pair.id); - } - CADICAL_assert (lrat_chain.size () == 2); - } - unsimplified.push_back (lhs1); - unsimplified.push_back (-lhs2); - const LRAT_ID id2 = simplify_and_add_to_proof_chain (unsimplified); - unsimplified.resize (unsimplified.size () - 2); - if (internal->lrat) { - lrat_chain.clear (); - int32_t rest = n &= ~(1 << (unsimplified.size () - 1)); - second_tmp.push_back (LitIdPair (rest, id2)); - } - inc_lits (unsimplified); - } - if (internal->lrat) { - first_ids.swap (first_tmp); - second_ids.swap (second_tmp); - } - } while (!unsimplified.empty ()); - if (internal->lrat) { - CADICAL_assert (first_ids.size () == 1); - CADICAL_assert (second_ids.size () == 1); - to_lrat.push_back (first_ids.back ().id); - back_lrat.push_back (second_ids.back ().id); - } - CADICAL_assert (!internal->lrat || to_lrat.size () == 1); - CADICAL_assert (!internal->lrat || back_lrat.size () == 1); - LOG ("finished XOR matching proof"); - CADICAL_assert (unsimplified.empty ()); -} - -// this function needs to either put the clauses from -// lrat_chain_and_gate into g->pos_neg_ids or clear it or do something with -// it if you merge gates. -Gate *Closure::new_xor_gate (const vector &glauses, - int lhs) { - rhs.clear (); - - for (auto lit : lits) { - if (lhs != lit && -lhs != lit) { - CADICAL_assert (lit > 0); - rhs.push_back (lit); - } - } - CADICAL_assert (rhs.size () + 1 == lits.size ()); - sort_literals_by_var (rhs); - Gate *g = find_xor_lits (this->rhs); - if (g) { - check_xor_gate_implied (g); - std::vector reasons_implication, reasons_back; - add_xor_matching_proof_chain (g, g->lhs, glauses, lhs, - reasons_implication, reasons_back); - if (merge_literals_lrat (g->lhs, lhs, reasons_implication, - reasons_back)) { - ++internal->stats.congruence.xors; - } - delete_proof_chain (); - CADICAL_assert (internal->unsat || chain.empty ()); - } else { - g = new Gate; - g->lhs = lhs; - g->tag = Gate_Type::XOr_Gate; - g->rhs = {rhs}; - g->garbage = false; - g->indexed = true; - g->shrunken = false; - g->hash = hash_lits (nonces, g->rhs); - for (auto pair : glauses) - g->pos_lhs_ids.push_back (pair); - table.insert (g); - ++internal->stats.congruence.gates; -#ifdef LOGGING - g->id = fresh_id++; -#endif - LOG (g, "creating new"); - check_xor_gate_implied (g); - for (auto lit : g->rhs) { - connect_goccs (g, lit); - } - } - return g; -} -uint32_t -Closure::number_from_xor_reason_reversed (const std::vector &rhs) { - uint32_t n = 0; - CADICAL_assert (is_sorted (rhs.rbegin (), rhs.rend (), - sort_literals_by_var_smaller_except (internal, 0, 0))); - CADICAL_assert (rhs.size () <= 32); - for (auto r = rhs.rbegin (); r != rhs.rend (); r++) { - int lit = *r; - n *= 2; - n += !(lit > 0); - } - return n; -} - -uint32_t Closure::number_from_xor_reason (const std::vector &rhs, - int lhs, int except, bool flip) { - uint32_t n = 0; - CADICAL_assert (is_sorted ( - begin (rhs), end (rhs), - sort_literals_by_var_smaller_except (internal, lhs, except))); - (void) lhs, (void) except; - CADICAL_assert (rhs.size () <= 32); - for (auto lit : rhs) { - n *= 2; - n += !(lit > 0) ^ flip; - flip = 0; - } - return n; -} - -// this is how I planned to sort it and produce the number -// Look at this first -void Closure::gate_sort_lrat_reasons (LitClausePair &litId, int lhs, - int except2, bool flip) { - CADICAL_assert (clause.empty ()); - std::copy (begin (*litId.clause), end (*litId.clause), - back_inserter (clause)); - sort_literals_by_var_except (clause, lhs, except2); - litId.current_lit = number_from_xor_reason (clause, lhs, except2, flip); - clause.clear (); -} - -struct smaller_pair_first_rank { - typedef size_t Type; - Type operator() (const LitClausePair &a) { return a.current_lit; } -}; - -// this is how I planned to sort it and produce the number -// Look at this first -void Closure::gate_sort_lrat_reasons (std::vector &xs, - int lhs, int except2, bool flip) { - CADICAL_assert (clause.empty ()); - CADICAL_assert (!xs.empty ()); - for (auto &litId : xs) { - gate_sort_lrat_reasons (litId, lhs, except2, flip); - } - - rsort (begin (xs), end (xs), smaller_pair_first_rank ()); - -#ifndef CADICAL_NDEBUG - std::for_each (begin (xs), end (xs), [&xs] (const LitClausePair &x) { - CADICAL_assert (x.clause->size == xs[1].clause->size); - }); -#endif -} - -void Closure::init_xor_gate_extraction (std::vector &candidates) { - const unsigned arity_limit = internal->opts.congruencexorarity; - CADICAL_assert (arity_limit < 32); // we use unsigned int. - const unsigned size_limit = arity_limit + 1; - glargecounts.resize (2 * internal->vsize, 0); - - for (auto c : internal->clauses) { - LOG (c, "considering clause for XOR"); - if (c->redundant) - continue; - if (c->garbage) - continue; - if (c->size < 3) - continue; - unsigned size = 0; - for (auto lit : *c) { - const signed char v = internal->val (lit); - if (v < 0) - continue; - if (v > 0) { - LOG (c, "satisfied by %d", lit); - internal->mark_garbage (c); - goto CONTINUE_COUNTING_NEXT_CLAUSE; - } - if (size == size_limit) - goto CONTINUE_COUNTING_NEXT_CLAUSE; - ++size; - } - - if (size < 3) - continue; - for (auto lit : *c) { - if (internal->val (lit)) - continue; - ++largecounts (lit); - } - - LOG (c, "considering clause for XOR as candidate"); - candidates.push_back (c); - CONTINUE_COUNTING_NEXT_CLAUSE:; - } - - LOG ("considering %zd out of %zd", candidates.size (), - internal->irredundant ()); - const unsigned rounds = internal->opts.congruencexorcounts; -#ifdef LOGGING - const size_t original_size = candidates.size (); -#endif - LOG ("resizing glargecounts to size %zd", glargecounts.size ()); - for (unsigned round = 0; round < rounds; ++round) { - LOG ("round %d of XOR extraction", round); - size_t removed = 0; - gnew_largecounts.resize (2 * internal->vsize); - unsigned cand_size = candidates.size (); - size_t j = 0; - for (size_t i = 0; i < cand_size; ++i) { - Clause *c = candidates[i]; - LOG (c, "considering"); - unsigned size = 0; - for (auto lit : *c) { - if (!internal->val (lit)) - ++size; - } - CADICAL_assert (3 <= size); - CADICAL_assert (size <= size_limit); - const unsigned arity = size - 1; - const unsigned needed_clauses = 1u << (arity - 1); - for (auto lit : *c) { - if (largecounts (lit) < needed_clauses) { - LOG (c, "not enough occurrences, so ignoring"); - removed++; - goto CONTINUE_WITH_NEXT_CANDIDATE_CLAUSE; - } - } - for (auto lit : *c) - if (!internal->val (lit)) - new_largecounts (lit)++; - candidates[j++] = candidates[i]; - - CONTINUE_WITH_NEXT_CANDIDATE_CLAUSE:; - } - candidates.resize (j); - glargecounts = std::move (gnew_largecounts); - gnew_largecounts.clear (); - LOG ("moving counts %zd", glargecounts.size ()); - if (!removed) - break; - - LOG ("after round %d, %zd (%ld %%) remain", round, candidates.size (), - candidates.size () / (1 + original_size) * 100); - } - - for (auto c : candidates) { - for (auto lit : *c) - internal->occs (lit).push_back (c); - } -} - -Clause *Closure::find_large_xor_side_clause (std::vector &lits) { - unsigned least_occurring_literal = 0; - unsigned count_least_occurring = UINT_MAX; - const size_t size_lits = lits.size (); -#if defined(LOGGING) || !defined(CADICAL_NDEBUG) - const unsigned arity = size_lits - 1; -#endif -#ifndef CADICAL_NDEBUG - const unsigned count_limit = 1u << (arity - 1); -#endif - LOG (lits, "trying to find arity %u XOR side clause", arity); - for (auto lit : lits) { - CADICAL_assert (!internal->val (lit)); - marked (lit) = 1; - unsigned count = largecount (lit); - CADICAL_assert (count_limit <= count); - if (count >= count_least_occurring) - continue; - count_least_occurring = count; - least_occurring_literal = lit; - } - Clause *res = 0; - CADICAL_assert (least_occurring_literal); - LOG ("searching XOR side clause watched by %d#%u", - least_occurring_literal, count_least_occurring); - LOG ("searching for size %ld", size_lits); - for (auto c : internal->occs (least_occurring_literal)) { - LOG (c, "checking"); - CADICAL_assert (c->size != 2); // TODO kissat has break - if (c->garbage) - continue; - if ((size_t) c->size < size_lits) - continue; - size_t found = 0; - for (auto other : *c) { - const signed char value = internal->val (other); - if (value < 0) - continue; - if (value > 0) { - LOG (c, "found satisfied %d in", other); - internal->mark_garbage (c); - CADICAL_assert (c->garbage); - break; - } - if (marked (other)) - found++; - else { - LOG ("not marked %d", other); - found = 0; - break; - } - } - if (found == size_lits && !c->garbage) { - res = c; - break; - } else { - LOG ("too few literals"); - } - } - for (auto lit : lits) - marked (lit) = 0; - if (res) - LOG (res, "found matching XOR side"); - else - LOG ("no matching XOR side clause found"); - return res; -} - -void Closure::extract_xor_gates_with_base_clause (Clause *c) { - LOG (c, "checking clause"); - lits.clear (); - int smallest = 0; - int largest = 0; - const unsigned arity_limit = internal->opts.congruencexorarity; - const unsigned size_limit = arity_limit + 1; - unsigned negated = 0, size = 0; - bool first = true; - for (auto lit : *c) { - const signed char v = internal->val (lit); - if (v < 0) - continue; - if (v > 0) { - internal->mark_garbage (c); - return; - } - if (size == size_limit) { - LOG (c, "size limit reached"); - return; - } - - if (first) { - largest = smallest = lit; - first = false; - } else { - CADICAL_assert (smallest); - CADICAL_assert (largest); - if (internal->vlit (lit) < internal->vlit (smallest)) { - LOG ("new smallest %d", lit); - smallest = lit; - } - if (internal->vlit (lit) > internal->vlit (largest)) { - if (largest < 0) { - LOG (c, "not largest %d (largest: %d) occurs negated in XOR base", - lit, largest); - return; - } - largest = lit; - } - } - if (lit < 0 && internal->vlit (lit) < internal->vlit (largest)) { - LOG (c, "negated literal %d not largest in XOR base", lit); - return; - } - if (lit < 0 && negated++) { - LOG (c, "more than one negated literal in XOR base"); - return; - } - lits.push_back (lit); - ++size; - } - CADICAL_assert (size == lits.size ()); - if (size < 3) { - LOG (c, "short XOR base clause"); - return; - } - - LOG ("double checking if possible"); - const unsigned arity = size - 1; - const unsigned needed_clauses = 1u << (arity - 1); - for (auto lit : lits) { - for (int sign = 0; sign != 2; ++sign, lit = -lit) { - unsigned count = largecount (lit); - if (count >= needed_clauses) - continue; - LOG (c, - "literal %d in XOR base clause only occurs %u times in large " - "clause thus skipping", - lit, count); - return; - } - } - - LOG ("checking for XOR side clauses"); - CADICAL_assert (smallest && largest); - const unsigned end = 1u << arity; - CADICAL_assert (negated == parity_lits (lits)); - unsigned found = 0; - vector glauses; - glauses.push_back (LitClausePair (0, c)); - for (unsigned i = 0; i != end; ++i) { - while (i && parity_lits (lits) != negated) - inc_lits (lits); - if (i) { - // the clause must be stored - // you can use `lrat_chain_and_gate` for this - Clause *d = find_large_xor_side_clause (lits); - if (!d) - return; - CADICAL_assert (!d->redundant); - glauses.push_back (LitClausePair (i, d)); - } else - CADICAL_assert (!c->redundant); - inc_lits (lits); - ++found; - } - - while (parity_lits (lits) != negated) - inc_lits (lits); - LOG (lits, "found all needed %u matching clauses:", found); - CADICAL_assert (found == 1u << arity); - if (negated) { - auto p = begin (lits); - int lit; - while ((lit = *p) > 0) - p++; - LOG ("flipping RHS literal %d", (lit)); - *p = -lit; - } - LOG (lits, "normalized negations"); - unsigned extracted = 0; - for (auto lhs : lits) { - if (!negated) - lhs = -lhs; - Gate *g = new_xor_gate (glauses, lhs); - if (g) - extracted++; - if (internal->unsat) - break; - } - if (!extracted) - LOG ("no arity %u XOR gate extracted", arity); -} -void Closure::extract_xor_gates () { - CADICAL_assert (!full_watching); - if (!internal->opts.congruencexor) - return; - START (extractxors); - LOG ("starting extracting XOR"); - std::vector candidates = {}; - init_xor_gate_extraction (candidates); - for (auto c : candidates) { - if (internal->unsat) - break; - if (c->garbage) - continue; - extract_xor_gates_with_base_clause (c); - } - reset_xor_gate_extraction (); - STOP (extractxors); -} - -/*------------------------------------------------------------------------*/ -void Closure::find_units () { - size_t units = 0; - for (auto v : internal->vars) { - RESTART: - if (!internal->flags (v).active ()) - continue; - for (int sgn = -1; sgn < 1; sgn += 2) { - int lit = v * sgn; - for (auto w : internal->watches (lit)) { - if (!w.binary ()) - continue; // todo check that binaries first - const int other = w.blit; - if (marked (-other)) { - LOG (w.clause, "binary clause %d %d and %d %d give unit %d", lit, - other, lit, -other, lit); - ++units; - if (internal->lrat) { - lrat_chain.push_back (w.clause->id); - lrat_chain.push_back (marked_mu1 (-other).clause->id); - } - bool failed = !learn_congruence_unit (lit); - unmark_all (); - if (failed) - return; - else - goto RESTART; - } - if (marked (other)) - continue; - marked (other) = 1; - set_mu1_reason (other, w.clause); - internal->analyzed.push_back (other); - } - unmark_all (); - } - CADICAL_assert (internal->analyzed.empty ()); - } - LOG ("found %zd units", units); -} - -void Closure::find_equivalences () { - CADICAL_assert (!internal->unsat); - - for (auto v : internal->vars) { - RESTART: - if (!internal->flags (v).active ()) - continue; - int lit = v; - for (auto w : internal->watches (lit)) { - if (!w.binary ()) - break; - CADICAL_assert (w.size == 2); - const int other = w.blit; - if (internal->vlit (lit) > internal->vlit (other)) - continue; - if (marked (other)) - continue; - internal->analyzed.push_back (other); - marked (other) = true; - set_mu1_reason (other, w.clause); - } - - if (internal->analyzed.empty ()) - continue; - - for (auto w : internal->watches (-lit)) { - if (!w.binary ()) - break; // binary clauses are first - const int other = w.blit; - if (internal->vlit (-lit) > internal->vlit (other)) - continue; - CADICAL_assert (-lit != other); - LOG ("binary clause %d %d", -lit, other); - if (marked (-other)) { - int lit_repr = find_representative (lit); - int other_repr = find_representative (other); - LOG ("found equivalence %d %d with %d and %d as the representative", - lit, other, lit_repr, other_repr); - if (lit_repr != other_repr) { - // if (internal->lrat) { - // // This cannot work - // // if you have 2 = 1 and 3=4 - // // you cannot add 2=3. You really to connect the - // representatives directly - // // therefore you actually need to learn the clauses 2->3->4 - // and -2->1 and vice-versa eager_representative_id (other) = - // marked_mu1 (-other).clause->id; eager_representative_id - // (-other) = w.clause->id; CADICAL_assert (eager_representative_id - // (other) != -1); LOG ("lrat: %d (%zd) %d (%zd)", other, - // eager_representative_id (other), -other, - // eager_representative_id (-other)); - // } - promote_clause (marked_mu1 (-other).clause); - promote_clause (w.clause); - LOG (w.clause, "merging"); - LOG (marked_mu1 (-other).clause, "with"); - if (merge_literals_equivalence ( - lit, other, - internal->lrat ? marked_mu1 (-other).clause : nullptr, - w.clause)) { - ++internal->stats.congruence.congruent; - } - unmark_all (); - if (internal->unsat) - return; - else - goto RESTART; - } - } - } - unmark_all (); - } - CADICAL_assert (internal->analyzed.empty ()); - LOG ("found %zd equivalences", schedule.size ()); -} - -/*------------------------------------------------------------------------*/ -// Initialization - -void Closure::rewrite_and_gate (Gate *g, int dst, int src, LRAT_ID id1, - LRAT_ID id2) { - if (skip_and_gate (g)) - return; - if (!gate_contains (g, src)) - return; - if (internal->val (src)) { - // In essence the code below does the same thing as simplify_and_gate - // but the necessary LRAT chain are different. - simplify_and_gate (g); - return; - } - CADICAL_assert (src); - CADICAL_assert (dst); - CADICAL_assert (internal->val (src) == internal->val (dst)); - GatesTable::iterator git = (g->indexed ? table.find (g) : end (table)); - LOG (g, "rewriting %d into %d in", src, dst); - int clashing = 0, falsifies = 0; - unsigned dst_count = 0, not_dst_count = 0; - auto q = begin (g->rhs); - for (int &lit : g->rhs) { - if (lit == src) - lit = dst; - if (lit == -g->lhs) { - LOG ("found negated LHS literal %d", lit); - clashing = lit; - g->degenerated_and_neg = true; - break; - } - if (lit == g->lhs) - g->degenerated_and_pos = true; - const signed char val = internal->val (lit); - if (val > 0) { - continue; - } - if (val < 0) { - LOG ("found falsifying literal %d", (lit)); - falsifies = lit; - break; - } - if (lit == dst) { - if (not_dst_count) { - LOG ("clashing literals %d and %d", (-dst), (dst)); - clashing = -dst; - break; - } - if (dst_count++) - continue; - } - if (lit == -dst) { - if (dst_count) { - CADICAL_assert (!not_dst_count); - LOG ("clashing literals %d and %d", (dst), (-dst)); - clashing = -dst; - break; - } - CADICAL_assert (!not_dst_count); - ++not_dst_count; - } - *q++ = lit; - } - LOG (lrat_chain, "lrat chain after rewriting"); - - if (internal->lrat) { // updating reasons in the chain. -#ifdef LOGGING - for (auto litId : g->pos_lhs_ids) { - LOG (litId.clause, "%d ->", litId.current_lit); - } -#endif - // We remove all assigned literals except the falsified literal such - // that we can produce an LRAT chain - size_t i = 0, size = g->pos_lhs_ids.size (); - bool found = false; - CADICAL_assert (!falsifies || !clashing); - const int orig_falsifies = falsifies == dst ? src : falsifies; - const int orig_clashing = - clashing == -dst ? -src : (clashing == dst ? src : clashing); - int keep_clashing = clashing; - LOG ("keeping chain for falsifies: %d aka %d and clashing: %d aka %d", - falsifies, orig_falsifies, clashing, orig_clashing); - for (size_t j = 0; j < size; ++j) { - LOG (g->pos_lhs_ids[j].clause, "looking at %d [%zd %zd] with val %d", - g->pos_lhs_ids[j].current_lit, i, j, - internal->val (g->pos_lhs_ids[i].current_lit)); - g->pos_lhs_ids[i] = g->pos_lhs_ids[j]; - if (keep_clashing && g->pos_lhs_ids[i].current_lit != orig_clashing && - g->pos_lhs_ids[i].current_lit != -orig_clashing && - g->pos_lhs_ids[i].current_lit != keep_clashing && - g->pos_lhs_ids[i].current_lit != -keep_clashing) - continue; - if (internal->val (g->pos_lhs_ids[i].current_lit) && - g->pos_lhs_ids[i].current_lit != src && - g->pos_lhs_ids[i].current_lit != orig_falsifies) - continue; - if (g->pos_lhs_ids[i].current_lit == dst) { - if (!found) - found = true; - else - continue; // we have already one defining clause - } - - LOG ("maybe keeping %d [%zd %zd], src: %d, found: %d", - g->pos_lhs_ids[i].current_lit, i, j, src, found); - if (g->pos_lhs_ids[i].current_lit == src) { - if (!found) - g->pos_lhs_ids[i].current_lit = dst, found = true; - else - continue; // we have already one defining clause - } - LOG ("keeping %d [%zd %zd]", g->pos_lhs_ids[i].current_lit, i, j); - ++i; - } - LOG ("resizing to %zd", i); - CADICAL_assert (i); - g->pos_lhs_ids.resize (i); - } - - if (q != end (g->rhs)) { - g->rhs.resize (q - begin (g->rhs)); - g->shrunken = true; - } - CADICAL_assert (dst_count <= 2); - CADICAL_assert (not_dst_count <= 1); - - std::vector reasons_lrat_src, reasons_lrat_usrc; - shrink_and_gate (g, falsifies, clashing); - LOG (g, "rewritten as"); - CADICAL_assert (!internal->lrat || !g->pos_lhs_ids.empty ()); - // check_and_gate_implied (g); - update_and_gate (g, git, src, dst, id1, id2, falsifies, clashing); - ++internal->stats.congruence.rewritten_ands; -} - -bool Closure::rewrite_gate (Gate *g, int dst, int src, LRAT_ID id1, - LRAT_ID id2) { - switch (g->tag) { - case Gate_Type::And_Gate: - rewrite_and_gate (g, dst, src, id1, id2); - break; - case Gate_Type::XOr_Gate: - rewrite_xor_gate (g, dst, src); - break; - case Gate_Type::ITE_Gate: - rewrite_ite_gate (g, dst, src); - break; - default: - CADICAL_assert (false); - break; - } - CADICAL_assert (internal->unsat || lrat_chain.empty ()); - return !internal->unsat; -} - -bool Closure::rewrite_gates (int dst, int src, LRAT_ID id1, LRAT_ID id2) { - const auto &occs = goccs (src); - for (auto g : occs) { - CADICAL_assert (lrat_chain.empty ()); - if (!rewrite_gate (g, dst, src, id1, id2)) - return false; - else if (!g->garbage && gate_contains (g, dst)) - goccs (dst).push_back (g); - } - goccs (src).clear (); - -#ifndef CADICAL_NDEBUG - for (const auto &occs : gtab) { - for (auto g : occs) { - CADICAL_assert (g); - CADICAL_assert (g->garbage || !gate_contains (g, src)); - } - } -#endif - CADICAL_assert (lrat_chain.empty ()); - return true; -} - -bool Closure::rewriting_lhs (Gate *g, int dst) { - if (dst != g->lhs && dst != -g->lhs) - return false; - mark_garbage (g); - return true; -} - -// update to produce proofs -void Closure::rewrite_xor_gate (Gate *g, int dst, int src) { - if (skip_xor_gate (g)) - return; - if (rewriting_lhs (g, dst)) - return; - if (!gate_contains (g, src)) - return; - LOG (g, "rewriting (%d -> %d)", src, dst); - check_xor_gate_implied (g); - GatesTable::iterator git = (g->indexed ? table.find (g) : end (table)); - size_t j = 0, dst_count = 0; - bool original_dst_negated = (dst < 0); - dst = abs (dst); - unsigned negate = original_dst_negated; - const size_t size = g->rhs.size (); - for (size_t i = 0; i < size; ++i) { - int lit = g->rhs[i]; - CADICAL_assert (lit > 0); - if (lit == src) - lit = dst; - const signed char v = internal->val (lit); - if (v > 0) { - negate ^= true; - } - if (v) - continue; - if (lit == dst) - dst_count++; - LOG ("keeping value %d", lit); - g->rhs[j++] = lit; - } - if (negate) { - LOG ("flipping LHS %d", g->lhs); - g->lhs = -g->lhs; - } - CADICAL_assert (dst_count <= 2); - if (dst_count == 2) { - LOG ("destination found twice, removing"); - size_t k = 0; - for (size_t i = 0; i < j; ++i) { - const int lit = g->rhs[i]; - if (lit != dst) - g->rhs[k++] = g->rhs[i]; - } - CADICAL_assert (k == j - 2); - g->rhs.resize (k); - g->shrunken = true; - CADICAL_assert (is_sorted (begin (g->rhs), end (g->rhs), - sort_literals_by_var_smaller (internal))); - g->hash = hash_lits (nonces, g->rhs); - } else if (j != size) { - g->shrunken = true; - g->rhs.resize (j); - sort_literals_by_var (g->rhs); - g->hash = hash_lits ( - nonces, - g->rhs); // all but one (the dst) is sorted correctly actually - } else { - CADICAL_assert (j == size); - sort_literals_by_var (g->rhs); - } - - CADICAL_assert (clause.empty ()); - // LRAT for add_xor_shrinking_proof_chain - // this should be unnecessary... - // TODO check if really unnecessary - if (dst_count > 1) - add_xor_shrinking_proof_chain (g, dst); - CADICAL_assert (internal->clause.size () <= 1); - update_xor_gate (g, git); - - if (!g->garbage && !internal->unsat && original_dst_negated && - dst_count == 1) { - connect_goccs (g, dst); - } - - check_xor_gate_implied (g); - // TODO stats -} - -// update to produce proofs -void Closure::simplify_xor_gate (Gate *g) { - LOG (g, "simplifying"); - if (skip_xor_gate (g)) - return; - check_xor_gate_implied (g); - unsigned negate = 0; - GatesTable::iterator git = (g->indexed ? table.find (g) : end (table)); - const size_t size = g->rhs.size (); - size_t j = 0; - for (size_t i = 0; i < size; ++i) { - int lit = g->rhs[i]; - CADICAL_assert (lit > 0); - const signed char v = internal->val (lit); - if (v > 0) - negate ^= 1; - if (!v) { - g->rhs[j++] = lit; - } - } - if (negate) { - LOG ("flipping LHS literal %d", (g->lhs)); - g->lhs = -(g->lhs); - } - if (j != size) { - LOG ("shrunken gate"); - g->shrunken = true; - g->rhs.resize (j); - CADICAL_assert (is_sorted (begin (g->rhs), end (g->rhs), - sort_literals_by_var_smaller (internal))); - g->hash = hash_lits (nonces, g->rhs); - } else { - CADICAL_assert (g->hash == hash_lits (nonces, g->rhs)); - } - - check_xor_gate_implied (g); - CADICAL_assert (clause.empty ()); - update_xor_gate (g, git); - LOG (g, "simplified"); - check_xor_gate_implied (g); - internal->stats.congruence.simplified++; - internal->stats.congruence.simplified_xors++; -} - -/*------------------------------------------------------------------------*/ -// propagation of clauses and simplification -void Closure::schedule_literal (int lit) { - const int idx = abs (lit); - if (scheduled[idx]) - return; - scheduled[idx] = true; - schedule.push (lit); - CADICAL_assert (lit != find_representative (lit)); - LOG ("scheduled literal %d", lit); -} - -bool Closure::propagate_unit (int lit) { - LOG ("propagation of congruence unit %d", lit); - if (internal->lrat) - lazy_propagated (lit) = true; - return simplify_gates (lit) && simplify_gates (-lit); -} - -bool Closure::propagate_units () { - while (units != - internal->trail - .size ()) { // units are added during propagation, so reloading - LOG ("propagating %d over gates", internal->trail[units]); - if (!propagate_unit (internal->trail[units++])) - return false; - } - return true; -} - -// The replacement has to be done eagerly, not lazily to make sure that the -// gates are in normalized form. Otherwise, some merges might be missed. -bool Closure::propagate_equivalence (int lit) { - if (internal->val (lit)) - return true; - LOG ("propagating literal %d", lit); - import_lazy_and_find_eager_representative_and_compress_both (lit); - const int repr = find_eager_representative_and_compress (lit); - const LRAT_ID id1 = find_eager_representative_lrat (lit); - const LRAT_ID id2 = find_eager_representative_lrat (-lit); - CADICAL_assert (lrat_chain.empty ()); - return rewrite_gates (repr, lit, id1, id2) && - rewrite_gates (-repr, -lit, id2, id1); -} - -size_t Closure::propagate_units_and_equivalences () { - START (congruencemerge); - size_t propagated = 0; - LOG ("propagating at least %zd units", schedule.size ()); - CADICAL_assert (lrat_chain.empty ()); - while (propagate_units () && !schedule.empty ()) { - CADICAL_assert (!internal->unsat); - CADICAL_assert (lrat_chain.empty ()); - ++propagated; - int lit = schedule.front (); - schedule.pop (); - scheduled[abs (lit)] = false; - if (!propagate_equivalence (lit)) - break; - } - - CADICAL_assert (internal->unsat || schedule.empty ()); - CADICAL_assert (internal->unsat || lrat_chain.empty ()); - - LOG ("propagated %zu congruence units", units); - LOG ("propagated %zu congruence equivalences", propagated); - -#ifndef CADICAL_NDEBUG - if (!internal->unsat) { - for (const auto &occs : gtab) { - for (auto g : occs) { - if (g->garbage) - continue; - CADICAL_assert (g->tag == Gate_Type::ITE_Gate || - g->tag == Gate_Type::XOr_Gate || - !gate_contains (g, -g->lhs)); - // TODO: this would be nice to have! - // CADICAL_assert (g->tag != Gate_Type::ITE_Gate || (g->rhs.size() == 3 - // && g->rhs[1] != -g->lhs && g->rhs[2] != -g->lhs)); - // CADICAL_assert (table.count(g) == 1); - for (auto lit : g->rhs) { - CADICAL_assert (!internal->val (lit)); - CADICAL_assert (representative (lit) == lit); - } - } - } - for (Gate *g : table) { - if (g->garbage) - continue; - if (g->tag == Gate_Type::And_Gate) { - // CADICAL_assert (find_and_lits(g->arity, g->rhs)); - } - } - } -#endif - STOP (congruencemerge); - return propagated; -} - -std::string string_of_gate (Gate_Type t) { - switch (t) { - case Gate_Type::And_Gate: - return "And"; - case Gate_Type::XOr_Gate: - return "XOr"; - case Gate_Type::ITE_Gate: - return "ITE"; - default: - return "buggy"; - } -} - -void Closure::reset_closure () { - scheduled.clear (); - for (Gate *g : table) { - CADICAL_assert (g->indexed); - LOG (g, "deleting"); - if (!g->garbage) - delete g; - } - table.clear (); - - for (auto &occ : gtab) { - occ.clear (); - } - gtab.clear (); - - for (auto gate : garbage) - delete gate; - garbage.clear (); - - if (internal->lrat) { - CADICAL_assert (internal->proof); - for (auto c : extra_clauses) { - CADICAL_assert (!c->garbage); - internal->proof->delete_clause (c); - delete c; - } - extra_clauses.clear (); - } else { - CADICAL_assert (extra_clauses.empty ()); - } -} - -void Closure::reset_extraction () { - full_watching = true; - if (!internal->unsat && !internal->propagate ()) { - internal->learn_empty_clause (); - } - -#if 0 - // remove delete watched clauses from the watch list - for (auto v : internal->vars) { - for (auto sgn = -1; sgn <= 1; sgn += 2) { - const int lit = v * sgn; - auto &watchers = internal->watches (lit); - const size_t size = watchers.size (); - size_t j = 0; - for (size_t i = 0; i != size; ++i) { - const auto w = watchers[i]; - watchers[j] = watchers[i]; - if (!w.clause->garbage) - ++j; - } - watchers.resize(j); - } - } - // watch the remaining non-watched clauses - for (auto c : new_unwatched_binary_clauses) - internal->watch_clause (c); - new_unwatched_binary_clauses.clear(); - for (auto c : internal->clauses) { - if (c->garbage) - continue; - if (c->size != 2) - internal->watch_clause (c); - } -#else // simpler implementation - new_unwatched_binary_clauses.clear (); - internal->clear_watches (); - internal->connect_watches (); -#endif -} - -void Closure::forward_subsume_matching_clauses () { - START (congruencematching); - reset_closure (); - std::vector matchable; - matchable.resize (internal->max_var + 1); - size_t count_matchable = 0; - - for (auto idx : internal->vars) { - if (!internal->flags (idx).active ()) - continue; - const int lit = idx; - const int repr = find_representative (lit); - if (lit == repr) - continue; - const int repr_idx = abs (repr); - if (!matchable[idx]) { - LOG ("matchable %d", idx); - matchable[idx] = true; - count_matchable++; - } - - if (!matchable[repr_idx]) { - LOG ("matchable %d", repr_idx); - matchable[repr_idx] = true; - count_matchable++; - } - } - - LOG ("found %.0f%%", - (double) count_matchable / - (double) (internal->max_var ? internal->max_var : 1)); - std::vector candidates; - auto &analyzed = internal->analyzed; - - for (auto *c : internal->clauses) { - if (c->garbage) - continue; - if (c->redundant) - continue; - if (c->size == 2) - continue; - CADICAL_assert (analyzed.empty ()); - bool contains_matchable = false; - for (auto lit : *c) { - const signed char v = internal->val (lit); - if (v < 0) - continue; - if (v > 0) { - LOG (c, "mark satisfied"); - internal->mark_garbage (c); - break; - } - if (!contains_matchable) { - const int idx = abs (lit); - if (matchable[idx]) - contains_matchable = true; - } - - const int repr = find_representative (lit); - CADICAL_assert (!internal->val (repr)); - if (marked (repr)) - continue; - const int not_repr = -repr; - if (marked (not_repr)) { - LOG (c, "matches both %d and %d", (lit), (not_repr)); - internal->mark_garbage (c); - break; - } - marked (repr) = 1; - analyzed.push_back (repr); - } - - for (auto lit : analyzed) - marked (lit) = 0; - analyzed.clear (); - if (c->garbage) - continue; - if (!contains_matchable) { - LOG ("no matching variable"); - continue; - } - LOG (c, "candidate"); - candidates.push_back (c); - } - - rsort (begin (candidates), end (candidates), smaller_clause_size_rank ()); - size_t tried = 0, subsumed = 0; - internal->init_occs (); - for (auto c : candidates) { - CADICAL_assert (c->size != 2); - // TODO if terminated - ++tried; - if (find_subsuming_clause (c)) { - ++subsumed; - } - } - LOG ("[congruence] subsumed %.0f%%", - (double) subsumed / (double) (tried ? tried : 1)); - STOP (congruencematching); -} - -/*------------------------------------------------------------------------*/ -// Candidate clause 'subsumed' is subsumed by 'subsuming'. We need to copy -// the function because 'congruence' is too early to include the version -// from subsume - -void Closure::subsume_clause (Clause *subsuming, Clause *subsumed) { - // CADICAL_assert (!subsuming->redundant); - // CADICAL_assert (!subsumed->redundant); - auto &stats = internal->stats; - stats.subsumed++; - CADICAL_assert (subsuming->size <= subsumed->size); - LOG (subsumed, "subsumed"); - if (subsumed->redundant) - stats.subred++; - else - stats.subirr++; - if (subsumed->redundant || !subsuming->redundant) { - internal->mark_garbage (subsumed); - return; - } - LOG ("turning redundant subsuming clause into irredundant clause"); - subsuming->redundant = false; - if (internal->proof) - internal->proof->strengthen (subsuming->id); - internal->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'. -} - -bool Closure::find_subsuming_clause (Clause *subsumed) { - CADICAL_assert (!subsumed->garbage); - Clause *subsuming = nullptr; - for (auto lit : *subsumed) { - CADICAL_assert (internal->val (lit) <= 0); - const int repr_lit = find_representative (lit); - const signed char repr_val = internal->val (repr_lit); - CADICAL_assert (repr_val <= 0); - if (repr_val < 0) - continue; - if (marked (repr_lit)) - continue; - CADICAL_assert (!marked (-repr_lit)); - marked (repr_lit) = 1; - } - int least_occuring_lit = 0; - size_t count_least_occurring = INT_MAX; - LOG (subsumed, "trying to forward subsume"); - - for (auto lit : *subsumed) { - const int repr_lit = find_representative (lit); - const size_t count = internal->occs (lit).size (); - CADICAL_assert (count <= UINT_MAX); - if (count < count_least_occurring) { - count_least_occurring = count; - least_occuring_lit = repr_lit; - } - for (auto d : internal->occs (lit)) { - CADICAL_assert (!d->garbage); - CADICAL_assert (subsumed != d); - if (!subsumed->redundant && d->redundant) - continue; - for (auto other : *d) { - const signed char v = internal->val (other); - if (v < 0) - continue; - CADICAL_assert (!v); - const int repr_other = find_representative (other); - if (!marked (repr_other)) - goto CONTINUE_WITH_NEXT_CLAUSE; - LOG ("subsuming due to %d -> %d", other, repr_other); - } - subsuming = d; - goto FOUND_SUBSUMING; - - CONTINUE_WITH_NEXT_CLAUSE:; - } - } - CADICAL_assert (least_occuring_lit); - -FOUND_SUBSUMING: - for (auto lit : *subsumed) { - const int repr_lit = find_representative (lit); - const signed char v = internal->val (lit); - if (!v) - marked (repr_lit) = 0; - } - if (subsuming) { - LOG (subsumed, "subsumed"); - LOG (subsuming, "subsuming"); - subsume_clause (subsuming, subsumed); - ++internal->stats.congruence.subsumed; - return true; - } else { - internal->occs (least_occuring_lit).push_back (subsumed); - return false; - } -} - -/*------------------------------------------------------------------------*/ -static bool skip_ite_gate (Gate *g) { - CADICAL_assert (g->tag == Gate_Type::ITE_Gate); - if (g->garbage) - return true; - return false; -} - -void Closure::produce_ite_merge_then_else_reasons ( - Gate *g, int src, int dst, std::vector &reasons_implication, - std::vector &reasons_back) { - CADICAL_assert (!g->garbage); - if (!internal->lrat) - return; - check_correct_ite_flags (g); - // no merge is happening actually - CADICAL_assert (g->rhs[1] == find_eager_representative(g->rhs[1]) || g->rhs[2] == find_eager_representative(g->rhs[2])); - if (find_eager_representative (g->lhs) == g->rhs[1] || find_eager_representative (g->lhs) == g->rhs[2]) - return; - if ((g->rhs[1] == src && g->lhs == dst && g->rhs[2] == g->lhs) || - (g->rhs[2] == src && g->lhs == dst && g->rhs[1] == g->lhs) || - (g->rhs[1] == -src && g->lhs == -dst && g->rhs[2] == g->lhs) || - (g->rhs[2] == -src && g->lhs == -dst && g->rhs[1] == g->lhs)) - return; - check_ite_lrat_reasons (g, false); - CADICAL_assert (g->rhs.size () == 3); - CADICAL_assert (src == g->rhs[1] || src == g->rhs[2]); - CADICAL_assert (dst == g->rhs[1] || dst == g->rhs[2]); - const int8_t flag = g->degenerated_ite; - CADICAL_assert (!ite_flags_no_then_clauses (flag)); // e = lhs: already merged - CADICAL_assert (!ite_flags_no_else_clauses (flag)); // t = lhs: already merged - produce_rewritten_clause_lrat (g->pos_lhs_ids, g->lhs, false); - if (ite_flags_neg_cond_lhs (flag)) { - LOG ("degenerated case with lhs = -cond"); - LOG (g->pos_lhs_ids[0].clause, "1:"); - LOG (g->pos_lhs_ids[1].clause, "2:"); - reasons_back.push_back (g->pos_lhs_ids[0].clause->id); - reasons_implication.push_back (g->pos_lhs_ids[1].clause->id); - return; - } - if (ite_flags_cond_lhs (flag)) { - LOG ("degenerated case with lhs = cond"); - CADICAL_assert (g->pos_lhs_ids[0].clause); - CADICAL_assert (g->pos_lhs_ids[3].clause); - reasons_back.push_back (g->pos_lhs_ids[3].clause->id); - reasons_implication.push_back (g->pos_lhs_ids[0].clause->id); - return; - } - reasons_implication.push_back (g->pos_lhs_ids[0].clause->id); - reasons_implication.push_back (g->pos_lhs_ids[2].clause->id); - reasons_back.push_back (g->pos_lhs_ids[1].clause->id); - reasons_back.push_back (g->pos_lhs_ids[3].clause->id); -} - -void Closure::rewrite_ite_gate_update_lrat_reasons (Gate *g, int src, - int dst) { - if (!internal->lrat) - return; - LOG (g, "updating lrat from"); - for (auto &litId : g->pos_lhs_ids) { - CADICAL_assert (litId.clause); - if (litId.current_lit == src) - litId.current_lit = dst; - if (litId.current_lit == -src) - litId.current_lit = -dst; - } - check_ite_lrat_reasons (g, false); -} - -bool Closure::rewrite_ite_gate_to_and ( - Gate *g, int src, int dst, size_t idx1, size_t idx2, - int cond_lit_to_learn_if_degenerated) { - CADICAL_assert (internal->lrat_chain.empty ()); - CADICAL_assert (!g->garbage); - LOG (g, "rewriting to proper AND"); - if (internal->val (g->lhs) > 0) { - { - const int lit = g->rhs[0]; - const char v = internal->val (lit); - if (v > 0) { - } else if (!v) { - if (internal->lrat) { - push_id_and_rewriting_lrat_unit (g->pos_lhs_ids[idx1].clause, - Rewrite (), lrat_chain); - } - learn_congruence_unit (cond_lit_to_learn_if_degenerated); - } else { - if (internal->lrat) - push_id_and_rewriting_lrat_unit (g->pos_lhs_ids[idx1].clause, - Rewrite (), lrat_chain); - push_lrat_unit (-lit); - internal->learn_empty_clause (); - return true; - } - } - if (!internal->unsat) { - const int lit = g->rhs[1]; - const char v = internal->val (lit); - CADICAL_assert (dst == g->rhs[0] || dst == g->rhs[1] || -dst == g->rhs[0] || - -dst == g->rhs[1]); - const int other = (dst == g->rhs[0] || dst == g->rhs[1]) - ? dst ^ g->rhs[0] ^ g->rhs[1] - : (-dst) ^ g->rhs[0] ^ g->rhs[1]; - if (v > 0) { - // already set by propagation - return true; - } else if (!v) { - if (internal->lrat) { - push_id_and_rewriting_lrat_unit (g->pos_lhs_ids[idx2].clause, - Rewrite (), lrat_chain); - } - learn_congruence_unit (other); - } else { - if (internal->lrat) { - push_lrat_unit (cond_lit_to_learn_if_degenerated); - push_id_and_rewriting_lrat_unit (g->pos_lhs_ids[idx2].clause, - Rewrite (), lrat_chain); - } - internal->learn_empty_clause (); - return true; - } - } - return true; - } - if (!internal->lrat) - return false; - LOG ("updating flags"); - g->degenerated_and_neg = (g->rhs[1] == -g->lhs || g->rhs[0] == -g->lhs); - g->degenerated_and_pos = (g->rhs[0] == g->lhs || g->rhs[1] == g->lhs); - CADICAL_assert (g->rhs.size () == 3); - CADICAL_assert (g->pos_lhs_ids.size () == 4); - CADICAL_assert (idx1 < g->pos_lhs_ids.size ()); - CADICAL_assert (idx2 < g->pos_lhs_ids.size ()); - int lit = g->pos_lhs_ids[idx2].current_lit, other = g->lhs; - // TODO: remove argument - (void) src; - produce_rewritten_clause_lrat_and_clean (g->pos_lhs_ids, g->lhs, idx1, - idx2, false); - - if ((idx1 == (size_t) -1 || idx2 == (size_t) -1)) { - // degenerated and gate - return false; - } - - CADICAL_assert (idx1 != (size_t) -1); - CADICAL_assert (idx2 != (size_t) -1); - CADICAL_assert (idx1 < g->pos_lhs_ids.size ()); - CADICAL_assert (idx2 < g->pos_lhs_ids.size ()); - Clause *c = g->pos_lhs_ids[idx1].clause; - CADICAL_assert (c->size == 2); - Clause *d = g->pos_lhs_ids[idx2].clause; - CADICAL_assert (c != d); - CADICAL_assert (c); - CADICAL_assert (d); - g->pos_lhs_ids.erase (std::remove_if (begin (g->pos_lhs_ids), - end (g->pos_lhs_ids), - [d] (const LitClausePair &p) { - return p.clause == d || !p.clause; - }), - end (g->pos_lhs_ids)); - CADICAL_assert (g->pos_lhs_ids.size () == 2); - CADICAL_assert (lit); - CADICAL_assert (other); - CADICAL_assert (lit != dst); - CADICAL_assert (other != dst); - CADICAL_assert (lit != other); - lrat_chain.push_back (c->id); - lrat_chain.push_back (d->id); - Clause *e = learn_binary_tmp_or_full_clause (lit, -other); - CADICAL_assert (e); - - auto long_clause = - std::find_if (begin (g->pos_lhs_ids), end (g->pos_lhs_ids), - [] (LitClausePair l) { return l.clause->size == 3; }); - CADICAL_assert (long_clause != end (g->pos_lhs_ids)); - LOG (long_clause->clause, "new long clause"); - g->neg_lhs_ids.push_back (*long_clause); - g->pos_lhs_ids.erase (long_clause); - - CADICAL_assert (g->pos_lhs_ids.size () == 1); - - (void) maybe_promote_tmp_binary_clause (g->pos_lhs_ids[0].clause); - g->pos_lhs_ids.push_back ({lit, e}); -#ifndef CADICAL_NDEBUG - for (auto litId : g->pos_lhs_ids) { - bool found = false; - CADICAL_assert (litId.clause); - for (auto other : *litId.clause) { - found = (find_eager_representative (other) == litId.current_lit); - if (found) - break; - } - CADICAL_assert (found); - } - for (auto id : g->pos_lhs_ids) { - LOG (id.clause, "clause after rewriting:"); - CADICAL_assert (id.clause->size == 2); - } - -#endif - return false; -} - -void Closure::produce_ite_merge_lhs_then_else_reasons ( - Gate *g, std::vector &reasons_implication, - std::vector &reasons_back, std::vector &reasons_unit, - bool rewritting_then, bool &learn_units) { - - const size_t idx1 = rewritting_then ? 0 : 2; - const size_t idx2 = idx1 + 1; - const size_t other_idx1 = rewritting_then ? 2 : 0; - const size_t other_idx2 = other_idx1 + 1; - const int cond_lit = g->rhs[0]; - const int lit_to_merge = g->rhs[rewritting_then ? 2 : 1]; - const int other_lit = g->rhs[rewritting_then ? 1 : 2]; - const int repr_cond_lit = find_eager_representative (g->rhs[0]); - const int repr_lit_to_merge = find_eager_representative (lit_to_merge); - const int repr_other_lit = find_eager_representative (other_lit); - const int repr_lhs = find_eager_representative(g->lhs); - if (!internal->proof) - return; - - - LOG ("cond: %d, merging %d and rewriting to %d", cond_lit, lit_to_merge, - other_lit); - if (internal->lrat) { - CADICAL_assert (internal->lrat); - CADICAL_assert (g->pos_lhs_ids.size () == 4); - - if (repr_lhs == -repr_other_lit) { - LOG ("special case: %s=%s, checking if other: %s %s", LOGLIT (g->lhs), - LOGLIT (-lit_to_merge), LOGLIT (cond_lit), LOGLIT (other_lit)); - CADICAL_assert (repr_lit_to_merge != -repr_lhs); // should have been rewritten before - - if (rewritting_then && repr_cond_lit == repr_lhs) { - LOG ("t=-lhs/c=lhs"); - learn_units = true; - // is a unit - push_id_and_rewriting_lrat_unit (g->pos_lhs_ids[0].clause, - Rewrite (), lrat_chain); - unsimplified.push_back (-cond_lit); - LRAT_ID id_unit = simplify_and_add_to_proof_chain (unsimplified); - reasons_unit = {id_unit}; - // don't bother finding out which one is used - reasons_implication.push_back (id_unit); - g->pos_lhs_ids[3].clause = produce_rewritten_clause_lrat ( - g->pos_lhs_ids[3].clause, g->lhs, false); - CADICAL_assert (g->pos_lhs_ids[3].clause); - reasons_implication.push_back (g->pos_lhs_ids[3].clause->id); - unsimplified.clear (); - return; - } - if (!rewritting_then && repr_cond_lit == repr_lhs) { - LOG ("e=-lhs/c=lhs"); - learn_units = true; - // is a unit - push_id_and_rewriting_lrat_unit (g->pos_lhs_ids[3].clause, - Rewrite (), lrat_chain); - unsimplified.push_back (cond_lit); - LRAT_ID id_unit = simplify_and_add_to_proof_chain (unsimplified); - reasons_unit = {id_unit}; - // don't bother finding out which one is used - reasons_implication.push_back (id_unit); - g->pos_lhs_ids[0].clause = produce_rewritten_clause_lrat ( - g->pos_lhs_ids[0].clause, g->lhs, false); - CADICAL_assert (g->pos_lhs_ids[0].clause); - reasons_implication.push_back (g->pos_lhs_ids[0].clause->id); - unsimplified.clear (); - return; - } - if (!rewritting_then && repr_cond_lit == -repr_lhs) { - LOG ("e=-lhs/c=-lhs"); - learn_units = true; - // TODO: this function does not work to produce units for this case - // c LOG 0 rewriting 4 by 3 in gate[42] (arity: 3) -3 := ITE 3 7 - // ... - // c LOG 0 clause[50] 4 -3 - // c LOG 0 clause[44] 5 3 - // c LOG 0 clause[2] -3 -4 -5 - // the first two are rewriting, but they are not ordered properly - // and we need the '5' clause to come after - push_id_and_rewriting_lrat_unit (g->pos_lhs_ids[2].clause, - Rewrite (), lrat_chain); - unsimplified.push_back (cond_lit); - LRAT_ID id_unit = simplify_and_add_to_proof_chain (unsimplified); - reasons_unit = {id_unit}; - g->pos_lhs_ids[1].clause = produce_rewritten_clause_lrat ( - g->pos_lhs_ids[1].clause, g->lhs, false); - CADICAL_assert (g->pos_lhs_ids[1].clause); - - // don't bother finding out which one is used - reasons_implication.push_back (id_unit); - reasons_implication.push_back (g->pos_lhs_ids[1].clause->id); - unsimplified.clear (); - return; - } - if (rewritting_then && repr_cond_lit == -repr_lhs) { - LOG ("t=-lhs/c=-lhs"); - learn_units = true; - push_id_and_rewriting_lrat_unit (g->pos_lhs_ids[1].clause, - Rewrite (), lrat_chain); - unsimplified.push_back (-cond_lit); - LRAT_ID id_unit = simplify_and_add_to_proof_chain (unsimplified); - reasons_unit = {id_unit}; - g->pos_lhs_ids[2].clause = produce_rewritten_clause_lrat ( - g->pos_lhs_ids[2].clause, g->lhs, false); - CADICAL_assert (g->pos_lhs_ids[2].clause); - - reasons_implication.push_back (id_unit); - reasons_implication.push_back (g->pos_lhs_ids[2].clause->id); - unsimplified.clear (); - return; - } - if (rewritting_then && repr_lit_to_merge == repr_lhs) { - LOG ("t=-lhs/e=lhs from rewriting then"); - learn_units = true; - g->pos_lhs_ids[idx1].clause = produce_rewritten_clause_lrat ( - g->pos_lhs_ids[idx1].clause, g->lhs, false); - g->pos_lhs_ids[idx2].clause = produce_rewritten_clause_lrat ( - g->pos_lhs_ids[idx2].clause, g->lhs, false); - CADICAL_assert (g->pos_lhs_ids[idx1].clause); - CADICAL_assert (g->pos_lhs_ids[idx2].clause); - lrat_chain.push_back (g->pos_lhs_ids[idx1].clause->id); - lrat_chain.push_back (g->pos_lhs_ids[idx2].clause->id); - unsimplified.push_back (-cond_lit); - LRAT_ID id_unit = simplify_and_add_to_proof_chain (unsimplified); - reasons_unit = {id_unit}; - unsimplified.clear (); - - return; - } - if (!rewritting_then && repr_lit_to_merge == repr_lhs) { - LOG ("t=-lhs/e=lhs from rewriting else"); - learn_units = true; - g->pos_lhs_ids[idx1].clause = produce_rewritten_clause_lrat ( - g->pos_lhs_ids[idx1].clause, g->lhs, false); - g->pos_lhs_ids[idx2].clause = produce_rewritten_clause_lrat ( - g->pos_lhs_ids[idx2].clause, g->lhs, false); - CADICAL_assert (g->pos_lhs_ids[idx1].clause); - CADICAL_assert (g->pos_lhs_ids[idx2].clause); - lrat_chain.push_back (g->pos_lhs_ids[idx1].clause->id); - lrat_chain.push_back (g->pos_lhs_ids[idx2].clause->id); - unsimplified.push_back (cond_lit); - LRAT_ID id_unit = simplify_and_add_to_proof_chain (unsimplified); - reasons_unit = {id_unit}; - unsimplified.clear (); - return; - } - - if (other_lit == repr_lhs) { - LOG ("TODO FIX ME t=-lhs/e=lhs"); - learn_units = true; - // in the other direction we are merging a literal with itself - g->pos_lhs_ids[idx1].clause = produce_rewritten_clause_lrat ( - g->pos_lhs_ids[idx1].clause, g->lhs, false); - g->pos_lhs_ids[idx2].clause = produce_rewritten_clause_lrat ( - g->pos_lhs_ids[idx2].clause, g->lhs, false); - CADICAL_assert (g->pos_lhs_ids[idx1].clause); - CADICAL_assert (g->pos_lhs_ids[idx2].clause); - reasons_unit.push_back (g->pos_lhs_ids[idx1].clause->id); - reasons_unit.push_back (g->pos_lhs_ids[idx2].clause->id); - return; - } - // if (other_lit == -g->lhs) { - // CADICAL_assert (false); - // } - // if (other_lit == g->lhs) { - // CADICAL_assert (false); - // } - } - - LOG ("normal path"); - produce_rewritten_clause_lrat (g->pos_lhs_ids, g->lhs, false); - CADICAL_assert (g->pos_lhs_ids.size () == 4); - - reasons_unit.push_back (g->pos_lhs_ids[idx1].clause->id); - reasons_unit.push_back (g->pos_lhs_ids[idx2].clause->id); - - // already merged: only unit is important - if (!rewritting_then && repr_lhs == repr_lit_to_merge) { - return; - } - - reasons_implication.push_back (g->pos_lhs_ids[other_idx1].clause->id); - reasons_implication.push_back (g->pos_lhs_ids[idx1].clause->id); - reasons_implication.push_back (g->pos_lhs_ids[idx2].clause->id); - - reasons_back.push_back (g->pos_lhs_ids[other_idx2].clause->id); - reasons_back.push_back (g->pos_lhs_ids[idx1].clause->id); - reasons_back.push_back (g->pos_lhs_ids[idx2].clause->id); - } else { - LOG ("learn extra clauses XXXXXXXXXXXXXXXXXXXXXXXXX"); - const int lhs = g->lhs; - const int cond = g->rhs[0]; - if (rewritting_then) { - unsimplified.push_back (-cond); - unsimplified.push_back (lhs); - simplify_and_add_to_proof_chain (unsimplified); - unsimplified.push_back (-cond); - unsimplified.push_back (-lhs); - simplify_and_add_to_proof_chain (unsimplified); - } else { - unsimplified.push_back (cond); - unsimplified.push_back (-lhs); - simplify_and_add_to_proof_chain (unsimplified); - unsimplified.push_back (cond); - unsimplified.push_back (lhs); - simplify_and_add_to_proof_chain (unsimplified); - } - unsimplified.clear (); - } -} - -void Closure::rewrite_ite_gate (Gate *g, int dst, int src) { - CADICAL_assert (unsimplified.empty ()); - if (skip_ite_gate (g)) - return; - if (!gate_contains (g, src)) - return; - LOG (g, "rewriting %d by %d in", src, dst); - CADICAL_assert (!g->shrunken); - CADICAL_assert (g->rhs.size () == 3); - CADICAL_assert (!internal->lrat || g->pos_lhs_ids.size () == 4); - auto &rhs = g->rhs; - const int lhs = g->lhs; - const int cond = g->rhs[0]; - const int then_lit = g->rhs[1]; - const int else_lit = g->rhs[2]; - const int not_lhs = -(lhs); - const int not_dst = -(dst); - const int not_cond = -(cond); - const int not_then_lit = -(then_lit); - const int not_else_lit = -(else_lit); - Gate_Type new_tag = Gate_Type::And_Gate; - - bool garbage = false; - bool shrink = true; - const auto git = g->indexed ? table.find (g) : end (table); - CADICAL_assert (!g->indexed || git != end (table)); - CADICAL_assert (*git == g); - if (internal->val (cond) && internal->val (then_lit) && - internal->val (else_lit)) { // propagation has set all value anyway - LOG (g, "all values are set"); - CADICAL_assert (internal->val (g->lhs)); - garbage = true; - } else if (internal->val (g->lhs) && internal->val (cond)) { - LOG (g, "all values are set 2"); - CADICAL_assert (internal->val (g->lhs)); - garbage = true; - } - // this code is taken one-to-one from kissat - else if (src == cond) { - if (dst == then_lit) { - // then_lit ? then_lit : else_lit - // then_lit & then_lit | !then_lit & else_lit - // then_lit | !then_lit & else_lit - // then_lit | else_lit - // !(!then_lit & !else_lit) - g->lhs = not_lhs; - rhs[0] = not_then_lit; - rhs[1] = not_else_lit; - if (then_lit == lhs || else_lit == lhs) - garbage = true; - else - garbage = rewrite_ite_gate_to_and (g, src, dst, 1, 3, -dst); - } else if (not_dst == then_lit) { - // !then_lit ? then_lit : else_lit - // !then_lit & then_lit | then_lit & else_lit - // then_lit & else_lit - rhs[0] = else_lit; - CADICAL_assert (rhs[1] == then_lit); - garbage = rewrite_ite_gate_to_and (g, src, dst, 0, 2, -dst); - } else if (dst == else_lit) { - // else_list ? then_lit : else_lit - // else_list & then_lit | !else_list & else_lit - // else_list & then_lit - rhs[0] = else_lit; - CADICAL_assert (rhs[1] == then_lit); - garbage = rewrite_ite_gate_to_and (g, src, dst, 2, 0, dst); - } else if (not_dst == else_lit) { - // !else_list ? then_lit : else_lit - // !else_list & then_lit | else_lit & else_lit - // !else_list & then_lit | else_lit - // then_lit | else_lit - // !(!then_lit & !else_lit) - g->lhs = not_lhs; - rhs[0] = not_then_lit; - rhs[1] = not_else_lit; - if (then_lit == lhs || else_lit == lhs) - garbage = true; - else - garbage = rewrite_ite_gate_to_and (g, src, dst, 3, 1, dst); - } else { - shrink = false; - rhs[0] = dst; - rewrite_ite_gate_update_lrat_reasons (g, src, dst); - } - } else if (src == then_lit) { - if (not_dst == g->lhs) { // TODO not in kissat - rhs[1] = dst; - check_ite_implied (g->lhs, cond, then_lit, else_lit); - std::vector reasons_implication, reasons_back, reasons_unit; - LOG ("%d = %d ?", g->lhs, -g->rhs[0]); - bool learn_units_instead_of_equivalence = false; - produce_ite_merge_lhs_then_else_reasons ( - g, reasons_implication, reasons_back, reasons_unit, true, - learn_units_instead_of_equivalence); - if (learn_units_instead_of_equivalence) { // it is too hard to produce - // LRAT chains - // in this case - - if (internal->lrat) - lrat_chain = reasons_unit; - learn_congruence_unit (-cond, true); - if (-else_lit == lhs) { - if (internal->lrat) - lrat_chain = reasons_implication; - learn_congruence_unit (cond == -lhs ? -else_lit : else_lit, false, true); - } else fully_propagate (); - } else { - if (merge_literals_lrat (g->lhs, else_lit, reasons_implication, - reasons_back)) { - ++internal->stats.congruence.unaries; - ++internal->stats.congruence.unary_ites; - } - if (!internal->unsat) { - if (internal->lrat) - lrat_chain = reasons_unit; - learn_congruence_unit (-cond); - } - } - delete_proof_chain (); - garbage = true; - } else if (dst == cond) { - // cond ? cond : else_lit - // cond & cond | !cond & else_lit - // cond | !cond & else_lit - // cond | else_lit - // !(!cond & !else_lit) - g->lhs = not_lhs; - rhs[0] = not_cond; - rhs[1] = not_else_lit; - if (else_lit == lhs || cond == lhs) - garbage = true; - else - garbage = rewrite_ite_gate_to_and (g, src, dst, 1, 3, -cond); - } else if (not_dst == cond) { - // cond ? !cond : else_lit - // cond & !cond | !cond & else_lit - // !cond & else_lit - rhs[0] = not_cond; - rhs[1] = else_lit; - garbage = rewrite_ite_gate_to_and (g, src, dst, 0, 2, -cond); - } else if (dst == else_lit) { - // cond ? else_lit : else_lit - // else_lit - std::vector reasons_implication, reasons_back; - produce_ite_merge_then_else_reasons (g, src, dst, reasons_implication, - reasons_back); - if (merge_literals_lrat (lhs, else_lit, reasons_implication, - reasons_back)) { - ++internal->stats.congruence.unaries; - ++internal->stats.congruence.unary_ites; - } - delete_proof_chain (); - garbage = true; - } else if (not_dst == else_lit) { - // cond ? !else_lit : else_lit - // cond & !else_lit | !cond & else_lit - // cond ^ else_lit - if (g->lhs == cond) { - produce_rewritten_clause_lrat_and_clean (g->pos_lhs_ids, g->lhs, - false); - if (internal->lrat) { - CADICAL_assert (g->pos_lhs_ids.size () == 2); - lrat_chain.push_back (g->pos_lhs_ids[0].clause->id); - lrat_chain.push_back (g->pos_lhs_ids[1].clause->id); - } - learn_congruence_unit (-else_lit); - garbage = true; - } else { - LOG ("changing to xor"); - new_tag = Gate_Type::XOr_Gate; - CADICAL_assert (rhs[0] == cond); - rhs[1] = else_lit; - CADICAL_assert (!internal->lrat || !g->pos_lhs_ids.empty ()); - { -#ifdef LOGGING - for (auto litId : g->pos_lhs_ids) { - LOG (litId.clause, "%d ->", litId.current_lit); - } -#endif - produce_rewritten_clause_lrat_and_clean (g->pos_lhs_ids, g->lhs, - true); -#ifdef LOGGING - for (auto litId : g->pos_lhs_ids) { - LOG (litId.clause, "%d ->", litId.current_lit); - } -#endif - } - } - } else { - shrink = false; - rhs[1] = dst; - rewrite_ite_gate_update_lrat_reasons (g, src, dst); - } - } else { - CADICAL_assert (src == else_lit); - if (not_dst == g->lhs) { // TODO not in kissat - rhs[2] = dst; - std::vector reasons_implication, reasons_back, reasons_unit; - bool learn_units_instead_of_equivalence = false; - produce_ite_merge_lhs_then_else_reasons ( - g, reasons_implication, reasons_back, reasons_unit, false, - learn_units_instead_of_equivalence); - if (learn_units_instead_of_equivalence) { // Too hard to produce LRAT - if (internal->lrat) - lrat_chain = reasons_unit; - learn_congruence_unit (cond, true); - if (then_lit != lhs) { - LOG ("special case, learning %d",cond == -lhs ? -then_lit : then_lit); - if (internal->lrat) - lrat_chain = reasons_implication; - learn_congruence_unit (cond == -lhs ? -then_lit : then_lit, false, true); - } else fully_propagate (); - } else { - if (merge_literals_lrat (lhs, then_lit, reasons_implication, - reasons_back)) { - ++internal->stats.congruence.unaries; - ++internal->stats.congruence.unary_ites; - } - if (!internal->unsat) { - if (internal->lrat) - lrat_chain = reasons_unit; - learn_congruence_unit (cond); - } - } - delete_proof_chain (); - garbage = true; - } else if (dst == cond) { - // cond ? then_lit : cond - // cond & then_lit | !cond & cond - // cond & then_lit - CADICAL_assert (rhs[0] == cond); - CADICAL_assert (rhs[1] == then_lit); - garbage = rewrite_ite_gate_to_and (g, src, dst, 2, 0, cond); - } else if (not_dst == cond) { - // cond ? then_lit : !cond - // cond & then_lit | !cond & !cond - // cond & then_lit | !cond - // then_lit | !cond - // !(!then_lit & cond) - g->lhs = not_lhs; - CADICAL_assert (rhs[0] == cond); - rhs[1] = not_then_lit; - if (then_lit == lhs || cond == lhs) - garbage = true; - else - garbage = rewrite_ite_gate_to_and (g, src, dst, 3, 1, cond); - } else if (dst == then_lit) { - // cond ? then_lit : then_lit - // then_lit - std::vector reasons_implication, reasons_back; - produce_ite_merge_then_else_reasons (g, src, dst, reasons_implication, - reasons_back); - if (merge_literals_lrat (lhs, then_lit, reasons_implication, - reasons_back)) { - ++internal->stats.congruence.unaries; - ++internal->stats.congruence.unary_ites; - } - garbage = true; - } else if (not_dst == then_lit) { - // cond ? then_lit : !then_lit - // cond & then_lit | !cond & !then_lit - // !(cond ^ then_lit) - if (lhs == cond) { - produce_rewritten_clause_lrat_and_clean (g->pos_lhs_ids, not_lhs, - false); - if (internal->lrat) { - CADICAL_assert (g->pos_lhs_ids.size () == 2); - lrat_chain.push_back (g->pos_lhs_ids[0].clause->id); - lrat_chain.push_back (g->pos_lhs_ids[1].clause->id); - } - learn_congruence_unit (then_lit); - garbage = true; - } else if (not_lhs == cond) { - produce_rewritten_clause_lrat_and_clean (g->pos_lhs_ids, not_lhs, - false); - if (internal->lrat) { - CADICAL_assert (g->pos_lhs_ids.size () == 2); - lrat_chain.push_back (g->pos_lhs_ids[0].clause->id); - lrat_chain.push_back (g->pos_lhs_ids[1].clause->id); - } - learn_congruence_unit (-then_lit); - garbage = true; - } else if (not_lhs == then_lit) { - produce_rewritten_clause_lrat_and_clean (g->pos_lhs_ids, not_lhs, - false); - if (internal->lrat) { - CADICAL_assert (g->pos_lhs_ids.size () == 2); - lrat_chain.push_back (g->pos_lhs_ids[0].clause->id); - lrat_chain.push_back (g->pos_lhs_ids[1].clause->id); - } - learn_congruence_unit (cond); - garbage = true; - } else { - new_tag = Gate_Type::XOr_Gate; - g->lhs = not_lhs; - CADICAL_assert (rhs[0] == cond); - CADICAL_assert (rhs[1] == then_lit); - CADICAL_assert (rhs[0] != g->lhs); - CADICAL_assert (rhs[1] != g->lhs); - produce_rewritten_clause_lrat_and_clean (g->pos_lhs_ids, not_lhs, - false); - } - } else { - shrink = false; - rhs[2] = dst; - rewrite_ite_gate_update_lrat_reasons (g, src, dst); - } - } - if (!garbage && !internal->unsat) { - CADICAL_assert (new_tag != Gate_Type::ITE_Gate || g->lhs != -rhs[1]); - CADICAL_assert (new_tag != Gate_Type::ITE_Gate || g->lhs != -rhs[2]); - if (shrink) { - if (new_tag == Gate_Type::XOr_Gate) { - bool negate_lhs = false; - if (rhs[0] < 0) { - rhs[0] = -rhs[0]; - negate_lhs = !negate_lhs; - } - if (rhs[1] < 0) { - rhs[1] = -rhs[1]; - negate_lhs = !negate_lhs; - } - if (negate_lhs) - g->lhs = -g->lhs; - } - if (internal->vlit (rhs[0]) > - internal->vlit ( - rhs[1])) { // unlike kissat, we need to do it after negating - std::swap (rhs[0], rhs[1]); - CADICAL_assert (new_tag != Gate_Type::ITE_Gate); - } - CADICAL_assert (internal->vlit (rhs[0]) < internal->vlit (rhs[1])); - CADICAL_assert (!g->shrunken); - g->shrunken = true; - rhs[2] = 0; - g->tag = new_tag; - rhs.resize (2); - CADICAL_assert (rhs[0] != -rhs[1]); - if (new_tag == Gate_Type::XOr_Gate) { - if (rhs[0] == -g->lhs || rhs[1] == -g->lhs) { - LOG (g, "special XOR:"); - const int unit = rhs[0] ^ -g->lhs ^ rhs[1]; - produce_rewritten_clause_lrat_and_clean (g->pos_lhs_ids, not_lhs, - false); - if (internal->lrat) { - CADICAL_assert (g->pos_lhs_ids.size () == 2); - lrat_chain.push_back (g->pos_lhs_ids[0].clause->id); - lrat_chain.push_back (g->pos_lhs_ids[1].clause->id); - } - learn_congruence_unit (unit); - garbage = true; - } else if (rhs[0] == g->lhs || rhs[1] == g->lhs) { - LOG (g, "special XOR:"); - const int unit = rhs[0] ^ g->lhs ^ rhs[1]; - produce_rewritten_clause_lrat_and_clean (g->pos_lhs_ids, not_lhs, - false); - if (internal->lrat) { - CADICAL_assert (g->pos_lhs_ids.size () == 2); - lrat_chain.push_back (g->pos_lhs_ids[0].clause->id); - lrat_chain.push_back (g->pos_lhs_ids[1].clause->id); - } - learn_congruence_unit (-unit); - garbage = true; - } else { - int i = 0; - bool negated = false; - for (int j = 0; j < 2; ++i, ++j) { - CADICAL_assert (i <= j); - const int lit = rhs[i] = rhs[j]; - const char v = internal->val (lit); - if (v > 0) { - --i; - negated = !negated; - } else if (v < 0) { - --i; - } - } - CADICAL_assert (i <= 2); - rhs.resize (i); - if (negated) { - g->lhs = -g->lhs; - } - if (i != 2) { - LOG (g, "removed units"); - } - if (!i) - garbage = true; - else if (i == 1) { -#ifdef LOGGING - for (auto litId : g->pos_lhs_ids) { - LOG (litId.clause, "%d ->", litId.current_lit); - } -#endif - produce_rewritten_clause_lrat_and_clean (g->pos_lhs_ids, g->lhs); - CADICAL_assert (!internal->lrat || g->pos_lhs_ids.size () == 2); - Clause *c1 = nullptr, *c2 = nullptr; - if (internal->lrat) { - CADICAL_assert (g->pos_lhs_ids[0].clause); - bool rhs_as_src_first = - g->pos_lhs_ids[0].clause->literals[0] == g->lhs || - g->pos_lhs_ids[0].clause->literals[1] == g->lhs; - c1 = (rhs_as_src_first ? g->pos_lhs_ids[0].clause - : g->pos_lhs_ids[1].clause); - c2 = (rhs_as_src_first ? g->pos_lhs_ids[1].clause - : g->pos_lhs_ids[0].clause); - c1 = maybe_promote_tmp_binary_clause (c1); - c2 = maybe_promote_tmp_binary_clause (c2); - } else { - maybe_add_binary_clause (-g->lhs, g->rhs[0]); - maybe_add_binary_clause (g->lhs, -g->rhs[0]); - } - merge_literals_equivalence (g->lhs, g->rhs[0], c1, c2); - garbage = true; - } - } - if (!garbage) { - CADICAL_assert (rhs[0] != g->lhs); - CADICAL_assert (rhs[1] != g->lhs); - CADICAL_assert (rhs[0] != -g->lhs); - CADICAL_assert (rhs[1] != -g->lhs); - } - } - - if (!garbage) { - g->hash = hash_lits (nonces, g->rhs); - LOG (g, "rewritten"); - - if (internal->lrat) { - if (new_tag == Gate_Type::XOr_Gate) { -#ifndef CADICAL_NDEBUG - std::for_each (begin (g->pos_lhs_ids), end (g->pos_lhs_ids), - [g] (LitClausePair l) { - CADICAL_assert ((size_t) l.clause->size == - 1 + g->arity ()); - }); -#endif - } else if (new_tag == Gate_Type::And_Gate) { - // we have to get rid of one clause, two become binaries, and - // becomes ternary - -#if defined(LOGGING) || !defined(CADICAL_NDEBUG) - for (auto id : g->pos_lhs_ids) { - LOG (id.clause, "clause after rewriting:"); - CADICAL_assert (id.clause->size == 2); - } -#endif - CADICAL_assert (g->pos_lhs_ids.size () == 2 || - gate_contains (g, g->lhs)); - CADICAL_assert (g->neg_lhs_ids.size () == 1 || - gate_contains (g, g->lhs)); - CADICAL_assert (g->arity () == 2); -#ifndef CADICAL_NDEBUG - std::for_each ( - begin (g->pos_lhs_ids), end (g->pos_lhs_ids), - [] (LitClausePair l) { CADICAL_assert (l.clause->size == 2); }); -#endif - } else { - CADICAL_assert (false); -#ifdef WIN32 - __assume(false); -#else - __builtin_unreachable (); -#endif - } - } - Gate *h; - if (new_tag == Gate_Type::And_Gate) { - check_and_gate_implied (g); - h = find_and_lits (rhs); - } else { - CADICAL_assert (new_tag == Gate_Type::XOr_Gate); - check_xor_gate_implied (g); - h = find_xor_gate (g); - } - if (h) { - garbage = true; - if (new_tag == Gate_Type::XOr_Gate) { - std::vector reasons_implication, reasons_back; - add_xor_matching_proof_chain (g, g->lhs, h->pos_lhs_ids, h->lhs, - reasons_implication, - reasons_back); - if (merge_literals_lrat (g->lhs, h->lhs, reasons_implication, - reasons_back)) - ++internal->stats.congruence.xors; - } else { - add_ite_turned_and_binary_clauses (g); - std::vector reasons_implication, reasons_back; - if (internal->lrat) - merge_and_gate_lrat_produce_lrat (g, h, reasons_implication, - reasons_back, false); - if (merge_literals_lrat (g->lhs, h->lhs, reasons_implication, - reasons_back)) - ++internal->stats.congruence.ands; - } - delete_proof_chain (); - } else { - garbage = false; - if (g->indexed) - remove_gate (git); - index_gate (g); - CADICAL_assert (g->arity () == 2); - for (auto lit : g->rhs) - if (lit != dst) - if (lit != cond && lit != then_lit && lit != else_lit) - connect_goccs (g, lit); - if (g->tag == Gate_Type::And_Gate && !internal->lrat) - for (auto lit : g->rhs) - maybe_add_binary_clause (-g->lhs, lit); - } - } - } else { - LOG (g, "rewritten"); - if (internal->lrat) - update_ite_flags (g), check_correct_ite_flags(g); - CADICAL_assert (rhs[0] != rhs[1]); - CADICAL_assert (rhs[0] != rhs[2]); - CADICAL_assert (rhs[1] != rhs[2]); - CADICAL_assert (rhs[0] != -(rhs[1])); - CADICAL_assert (rhs[0] != -(rhs[2])); - CADICAL_assert (rhs[1] != -(rhs[2])); - check_ite_gate_implied (g); - check_ite_lrat_reasons (g, false); - bool negate_lhs; - Gate *h = find_ite_gate (g, negate_lhs); - CADICAL_assert (lhs == g->lhs); - CADICAL_assert (not_lhs == -(g->lhs)); - if (negate_lhs) - g->lhs = -lhs; - check_ite_lrat_reasons (g); - if (internal->lrat) - check_correct_ite_flags (g); - if (h) { - garbage = true; - check_ite_gate_implied (g); - check_ite_lrat_reasons (g, false); - check_ite_gate_implied (h); - check_ite_lrat_reasons (h, false); - int normalized_lhs = negate_lhs ? not_lhs : lhs; - std::vector extra_reasons_lit, extra_reasons_ulit; - add_ite_matching_proof_chain (g, h, normalized_lhs, h->lhs, - extra_reasons_lit, - extra_reasons_ulit); - if (merge_literals_lrat (normalized_lhs, h->lhs, extra_reasons_lit, - extra_reasons_ulit)) - ++internal->stats.congruence.ites; - delete_proof_chain (); - CADICAL_assert (internal->unsat || chain.empty ()); - } else { - garbage = false; - if (g->indexed) - remove_gate (git); - LOG (g, "normalized"); - g->hash = hash_lits (nonces, g->rhs); - index_gate (g); - CADICAL_assert (g->arity () == 3); - for (auto lit : g->rhs) - if (lit != dst) - if (lit != cond && lit != then_lit && lit != else_lit) - connect_goccs (g, lit); - } - } - } - if (garbage && !internal->unsat) - mark_garbage (g); - - CADICAL_assert (chain.empty ()); -} - -void Closure::simplify_ite_gate_produce_unit_lrat (Gate *g, int lit, - size_t idx1, - size_t idx2) { - if (!internal->lrat) - return; - // TODO - if (internal->val (lit) > 0) - return; - CADICAL_assert (internal->lrat); - CADICAL_assert (g); - CADICAL_assert (idx1 < g->pos_lhs_ids.size ()); - CADICAL_assert (idx2 < g->pos_lhs_ids.size ()); - CADICAL_assert (g->pos_lhs_ids.size () == 4); - - CADICAL_assert (idx1 != idx2); - Clause *c = g->pos_lhs_ids[idx1].clause; - Clause *d = g->pos_lhs_ids[idx2].clause; - - if (g->lhs == -g->rhs[0]) { - LOG ("special case of LHS=-cond where only one clause in LRAT is needed is needed"); - size_t idx = (internal->val (g->rhs[1]) < 0 ? idx2 : idx1); - c = produce_rewritten_clause_lrat (g->pos_lhs_ids[idx].clause, g->lhs, false, false); - CADICAL_assert (c); - // not possible to do this in a single lrat chain - push_id_and_rewriting_lrat_unit (c, Rewrite (), lrat_chain, true, - Rewrite (), g->lhs); - CADICAL_assert (d); - return; - } - if (g->lhs == g->rhs[0]) { - LOG ("special case of LHS=cond where only one clause in LRAT is needed is needed"); - size_t idx = (internal->val (g->rhs[1]) > 0 ? idx2 : idx1); - c = produce_rewritten_clause_lrat (g->pos_lhs_ids[idx].clause, g->lhs, false, false); - CADICAL_assert (c); - // not possible to do this in a single lrat chain - push_id_and_rewriting_lrat_unit (c, Rewrite (), lrat_chain, true, - Rewrite (), g->lhs); - CADICAL_assert (d); - return; - } - - c = produce_rewritten_clause_lrat (c, g->lhs, true); - if (c) { - lrat_chain.push_back (c->id); - d = produce_rewritten_clause_lrat (d, g->lhs, true); - if (d) - lrat_chain.push_back (d->id); - } else if (!c) { - push_id_and_rewriting_lrat_unit (d, Rewrite (), lrat_chain, true, - Rewrite (), g->lhs); - CADICAL_assert (d); - lrat_chain.push_back (d->id); - } -} - -// TODO merge -void Closure::merge_and_gate_lrat_produce_lrat ( - Gate *g, Gate *h, std::vector &reasons_lrat_src, - std::vector &reasons_lrat_usrc, bool remove_units) { - CADICAL_assert (internal->lrat); - CADICAL_assert (g->tag == Gate_Type::And_Gate); - CADICAL_assert (h->tag == Gate_Type::And_Gate); - CADICAL_assert (g->neg_lhs_ids.size () <= 1); - update_and_gate_build_lrat_chain (g, h, reasons_lrat_src, - reasons_lrat_usrc, remove_units); -} - -// odd copy of rewrite_ite_gate_lrat_and -bool Closure::simplify_ite_gate_to_and (Gate *g, size_t idx1, size_t idx2, - int removed_lit) { - CADICAL_assert (internal->lrat_chain.empty ()); - CADICAL_assert (g->rhs.size () == 3); -#ifdef LOGGING - for (auto litId : g->pos_lhs_ids) { - LOG (litId.clause, "%d ->", litId.current_lit); - } -#endif - if (g->lhs == -g->rhs[0] || g->lhs == -g->rhs[1]) { - if (internal->lrat) { - Clause *c = g->pos_lhs_ids[idx1].clause; - push_id_and_rewriting_lrat_unit (c, Rewrite (), lrat_chain); - CADICAL_assert (!lrat_chain.empty ()); - } - learn_congruence_unit (-g->lhs); - return true; - } - if (!internal->lrat) - return false; - g->degenerated_and_neg = (g->degenerated_and_neg || g->rhs[1] == -g->lhs || g->rhs[0] == -g->lhs); - g->degenerated_and_pos = (g->degenerated_and_pos || g->rhs[0] == g->lhs || g->rhs[1] == g->lhs); - - CADICAL_assert (g->pos_lhs_ids.size () == 4); - CADICAL_assert (idx1 < g->pos_lhs_ids.size ()); - CADICAL_assert (idx2 < g->pos_lhs_ids.size ()); - int lit = g->pos_lhs_ids[idx2].current_lit, other = g->lhs; - size_t new_idx1 = idx1; - size_t new_idx2 = idx2; - produce_rewritten_clause_lrat_and_clean (g->pos_lhs_ids, g->lhs, new_idx1, - new_idx2); - - if (g->pos_lhs_ids.size () == 1) { - LOG (g, "degenerated AND gate"); - const int replacement_lit = (g->rhs[1] == g->lhs ? g->rhs[0] : g->rhs[1]); - for (auto &litId : g->pos_lhs_ids) { - CADICAL_assert (litId.clause); - LOG (litId.clause, "%d ->", litId.current_lit); - if (litId.current_lit == removed_lit) - litId.current_lit = -replacement_lit; - if (litId.current_lit == -removed_lit) - litId.current_lit = replacement_lit; - LOG (litId.clause, "%d ->", litId.current_lit); - // TODO we need a replacement index - CADICAL_assert (std::find (begin (*litId.clause), end (*litId.clause), litId.current_lit) != - end (*litId.clause)); - CADICAL_assert (std::find (begin (g->rhs), end (g->rhs), litId.current_lit) != - end (g->rhs)); - } - return false; - } - CADICAL_assert (new_idx1 < g->pos_lhs_ids.size ()); - CADICAL_assert (new_idx2 < g->pos_lhs_ids.size ()); - Clause *c = g->pos_lhs_ids[new_idx1].clause; - CADICAL_assert (c->size == 2); - CADICAL_assert (new_idx1 != new_idx2); - Clause *d = g->pos_lhs_ids[new_idx2].clause; - CADICAL_assert (c != d); - CADICAL_assert (c); - CADICAL_assert (d); - g->pos_lhs_ids.erase (std::remove_if (begin (g->pos_lhs_ids), - end (g->pos_lhs_ids), - [d] (const LitClausePair &p) { - return p.clause == d; - }), - end (g->pos_lhs_ids)); - CADICAL_assert (g->pos_lhs_ids.size () == 2); - CADICAL_assert (lit); - CADICAL_assert (other); - CADICAL_assert (lit != other); - lrat_chain.push_back (c->id); - lrat_chain.push_back (d->id); - Clause *e = add_tmp_binary_clause (lit, -other); - - auto long_clause = - std::find_if (begin (g->pos_lhs_ids), end (g->pos_lhs_ids), - [] (LitClausePair l) { return l.clause->size == 3; }); - CADICAL_assert (long_clause != end (g->pos_lhs_ids)); - LOG (long_clause->clause, "new long clause"); - g->neg_lhs_ids.push_back (*long_clause); - g->pos_lhs_ids.erase (long_clause); - CADICAL_assert (g->pos_lhs_ids.size () == 1); - g->pos_lhs_ids.push_back ({lit, e}); - - const int first_lit = g->rhs[0]; - const int second_lit = g->rhs[1]; - for (auto &litId : g->pos_lhs_ids) { - CADICAL_assert (litId.clause); - LOG (litId.clause, "%s ->", LOGLIT (litId.current_lit)); - if (internal->val (litId.current_lit)) { - CADICAL_assert (litId.clause->size == 2); - int replacement_lit = 0; - for (int i = 0; i < 2; ++i) { - if (litId.clause->literals[i] == first_lit) { - replacement_lit = first_lit; - break; - } else if (litId.clause->literals[i] == second_lit) { - replacement_lit = second_lit; - break; - } - } - CADICAL_assert (replacement_lit); - - litId.current_lit = replacement_lit; - } else if (litId.current_lit == removed_lit) - litId.current_lit = -g->rhs[0]; - else if (litId.current_lit == -removed_lit) - litId.current_lit = g->rhs[0]; - LOG (litId.clause, "%d ->", litId.current_lit); - // TODO we need a replacement index - CADICAL_assert (std::find (begin (g->rhs), end (g->rhs), litId.current_lit) != - end (g->rhs)); - CADICAL_assert (std::find (begin (*litId.clause), end (*litId.clause), - litId.current_lit) != end (*litId.clause)); - } - return false; -} - -void Closure::merge_ite_gate_same_then_else_lrat ( - std::vector &clauses, - std::vector &reasons_implication, - std::vector &reasons_back) { - CADICAL_assert (clauses.size () == 4); - produce_rewritten_clause_lrat_and_clean (clauses); - CADICAL_assert (clauses.size () == 4); - auto then_imp = clauses[0]; - auto neg_then_imp = clauses[1]; - auto else_imp = clauses[2]; - auto neg_else_imp = clauses[3]; - reasons_implication.push_back (then_imp.clause->id); - reasons_implication.push_back (else_imp.clause->id); - reasons_back.push_back (neg_then_imp.clause->id); - reasons_back.push_back (neg_else_imp.clause->id); -} - -void Closure::simplify_ite_gate_then_else_set ( - Gate *g, std::vector &reasons_implication, - std::vector &reasons_back, size_t idx1, size_t idx2) { - CADICAL_assert (idx1 < g->pos_lhs_ids.size ()); - CADICAL_assert (idx2 < g->pos_lhs_ids.size ()); - Clause *c = g->pos_lhs_ids[idx1].clause; - Clause *d = g->pos_lhs_ids[idx2].clause; - push_id_and_rewriting_lrat_unit (c, Rewrite (), reasons_back, true, - Rewrite (), g->lhs); - push_id_and_rewriting_lrat_unit (d, Rewrite (), reasons_implication, true, - Rewrite (), g->lhs); - LOG (reasons_back, "LRAT"); - LOG (reasons_implication, "LRAT"); - // c = produce_rewritten_clause_lrat (c, g->lhs, false); - // CADICAL_assert (c); - // d = produce_rewritten_clause_lrat (d, g->lhs, false); - // CADICAL_assert (d); - // const int cond = g->rhs[0]; - // CADICAL_assert (internal->val (cond)); - // reasons_implication.push_back(internal->unit_id (internal->val (cond) > - // 0 ? cond : -cond)); reasons_implication.push_back(c->id); - // reasons_back.push_back(internal->unit_id (internal->val (cond) > 0 ? - // cond : -cond)); reasons_back.push_back(d->id); -} - -void Closure::simplify_ite_gate_condition_set ( - Gate *g, std::vector &reasons_lrat, - std::vector &reasons_back_lrat, size_t idx1, size_t idx2) { - CADICAL_assert (internal->lrat); - Clause *c = g->pos_lhs_ids[idx1].clause; - Clause *d = g->pos_lhs_ids[idx2].clause; -#if defined(LOGGING) || !defined(CADICAL_NDEBUG) - const int cond = g->rhs[0]; - CADICAL_assert (internal->val (cond)); - LOG ("cond = %d", cond); -#endif -#ifdef LOGGING - for (auto litid : g->pos_lhs_ids) - LOG (litid.clause, "clause in gate:"); -#endif - push_id_and_rewriting_lrat_unit (c, Rewrite (), reasons_lrat, true, - Rewrite (), -g->lhs); - push_id_and_rewriting_lrat_unit (d, Rewrite (), reasons_back_lrat, true, - Rewrite (), g->lhs); -} - -void Closure::simplify_ite_gate (Gate *g) { - if (skip_ite_gate (g)) - return; - LOG (g, "simplifying"); - CADICAL_assert (g->arity () == 3); - bool garbage = true; - int lhs = g->lhs; - auto &rhs = g->rhs; - const int cond = rhs[0]; - const int then_lit = rhs[1]; - const int else_lit = rhs[2]; - const signed char v_cond = internal->val (cond); - const signed char v_else = internal->val (else_lit); - const signed char v_then = internal->val (then_lit); - std::vector reasons_lrat, reasons_back_lrat; - if (v_cond && v_else && v_then) { // propagation has set all value anyway - LOG (g, "all values are set"); - CADICAL_assert (internal->val (g->lhs)); - garbage = true; - } else if (v_cond > 0) { - if (internal->lrat) - simplify_ite_gate_condition_set (g, reasons_lrat, reasons_back_lrat, - 0, 1); - if (merge_literals_lrat (lhs, then_lit, reasons_lrat, - reasons_back_lrat)) { - ++internal->stats.congruence.unary_ites; - ++internal->stats.congruence.unaries; - } - } else if (v_cond < 0) { - if (internal->lrat) - simplify_ite_gate_condition_set (g, reasons_lrat, reasons_back_lrat, - 2, 3); - if (merge_literals_lrat (lhs, else_lit, reasons_lrat, - reasons_back_lrat)) { - ++internal->stats.congruence.unary_ites; - ++internal->stats.congruence.unaries; - } - } else { - LOG ("then %d: %d; else %d: %d", then_lit, v_then, else_lit, v_else); - std::vector extra_reasons, extra_reasons_back; - CADICAL_assert (v_then || v_else); - if (v_then > 0 && v_else > 0) { - simplify_ite_gate_produce_unit_lrat (g, lhs, 1, 3); - learn_congruence_unit (lhs); - } else if (v_then < 0 && v_else < 0) { - simplify_ite_gate_produce_unit_lrat (g, -lhs, 0, 2); - learn_congruence_unit (-lhs); - } else if (v_then > 0 && v_else < 0) { - if (internal->lrat) - simplify_ite_gate_then_else_set (g, extra_reasons, - extra_reasons_back, 1, 2); - if (merge_literals_lrat (lhs, cond, extra_reasons, - extra_reasons_back)) { - ++internal->stats.congruence.unary_ites; - ++internal->stats.congruence.unaries; - } - } else if (v_then < 0 && v_else > 0) { - if (internal->lrat) - simplify_ite_gate_then_else_set (g, extra_reasons, - extra_reasons_back, 0, 3); - if (merge_literals_lrat (lhs, -cond, extra_reasons_back, - extra_reasons)) { - ++internal->stats.congruence.unary_ites; - ++internal->stats.congruence.unaries; - } - } else { - CADICAL_assert (!!v_then + !!v_else == 1); - auto git = g->indexed ? table.find (g) : end (table); - CADICAL_assert (!g->indexed || git != end (table)); - if (v_then > 0) { - g->lhs = -lhs; - rhs[0] = -cond; - rhs[1] = -else_lit; - simplify_ite_gate_to_and (g, 1, 3, then_lit); - } else if (v_then < 0) { - rhs[0] = -cond; - rhs[1] = else_lit; - simplify_ite_gate_to_and (g, 0, 2, -then_lit); - } else if (v_else > 0) { - g->lhs = -lhs; - rhs[0] = -then_lit; - rhs[1] = cond; - simplify_ite_gate_to_and (g, 3, 1, else_lit); - } else { - CADICAL_assert (v_else < 0); - rhs[0] = then_lit; - rhs[1] = cond; - simplify_ite_gate_to_and (g, 2, 0, -else_lit); - } - if (internal->unsat) - return; - if (internal->vlit (rhs[0]) > internal->vlit (rhs[1])) - std::swap (rhs[0], rhs[1]); - g->shrunken = true; - g->tag = Gate_Type::And_Gate; - rhs.resize (2); - CADICAL_assert (is_sorted (begin (rhs), end (rhs), - sort_literals_by_var_smaller (internal))); - g->hash = hash_lits (nonces, rhs); - check_and_gate_implied (g); - Gate *h = find_and_lits (rhs); - if (h) { - CADICAL_assert (garbage); - std::vector reasons_lrat, reasons_lrat_back; - if (internal->lrat) - merge_and_gate_lrat_produce_lrat (g, h, reasons_lrat, - reasons_lrat_back, false); - if (merge_literals_lrat (g->lhs, h->lhs, reasons_lrat, - reasons_lrat_back)) { - ++internal->stats.congruence.ites; - } - } else { - remove_gate (git); - index_gate (g); - garbage = false; - g->hash = hash_lits (nonces, g->rhs); - for (auto lit : rhs) - if (lit != cond && lit != then_lit && lit != else_lit) { - connect_goccs (g, lit); - } - - if (rhs[0] == -g->lhs || rhs[1] == -g->lhs) - simplify_and_gate ( - g); // TODO Kissat does not do that, but it has also no - // checks to verify that it cannot happen... - } - } - } - if (garbage && !internal->unsat) - mark_garbage (g); - ++internal->stats.congruence.simplified; - ++internal->stats.congruence.simplified_ites; -} - -void Closure::add_ite_matching_proof_chain ( - Gate *g, Gate *h, int lhs1, int lhs2, std::vector &reasons1, - std::vector &reasons2) { - check_ite_lrat_reasons (g); - check_ite_lrat_reasons (h, false); - CADICAL_assert (g->lhs == lhs1); - CADICAL_assert (h->lhs == lhs2); - if (lhs1 == lhs2) - return; - if (!internal->proof) - return; - LOG (g, "starting ITE matching proof chain"); - LOG (h, "starting ITE matching proof chain with"); - CADICAL_assert (unsimplified.empty ()); - CADICAL_assert (chain.empty ()); - if (internal->lrat) - check_correct_ite_flags (g); - const auto &rhs = g->rhs; - const int8_t flags_g = g->degenerated_ite; - const int8_t flags_h = h->degenerated_ite; - const int cond = rhs[0]; - LRAT_ID g_then_id = 0, g_neg_then_id = 0, g_neg_else_id = 0; - LRAT_ID h_then_id = 0, h_neg_then_id = 0, h_else_id = 0; - LRAT_ID g_else_id = 0, h_neg_else_id = 0; - const bool degenerated_g_then = ite_flags_no_then_clauses (flags_g); - const bool degenerated_g_else = ite_flags_no_else_clauses (flags_g); - const bool degenerated_h_then = ite_flags_no_then_clauses (flags_h); - const bool degenerated_h_else = ite_flags_no_else_clauses (flags_h); - - const bool degenerated_g_cond = ite_flags_cond_lhs (flags_g); - const bool degenerated_h_cond = ite_flags_cond_lhs (flags_h); - CADICAL_assert (!(degenerated_g_cond && degenerated_h_cond)); - - const bool degenerated_g_not_cond = ite_flags_neg_cond_lhs (flags_g); - const bool degenerated_h_not_cond = ite_flags_neg_cond_lhs (flags_h); - CADICAL_assert (!(degenerated_g_not_cond && degenerated_h_not_cond)); - - if (internal->lrat) { - // the code can produce tautologies in the case that: - // a = (c ? a : e) - // b = (c ? a : e) - // (no clauses with (then) index a/-a) - // but also for - // a = (a ? t : e) - // b = (a ? t : e) - // (no clauses with (then) index -a and (else) index e) - // and same for - // a = (c ? t : a) - // b = (c ? t : a) - // (no clauses with (then) index a and (else) index -e) - LOG (g, "get ids from"); - CADICAL_assert (g->pos_lhs_ids.size () == 4); - auto &g_then_clause = g->pos_lhs_ids[0].clause; - g_then_clause = - g_then_clause ? produce_rewritten_clause_lrat (g_then_clause, g->lhs, false) : nullptr; - if (g_then_clause) - g_then_id = g_then_clause->id; - - auto &g_neg_then_clause = g->pos_lhs_ids[1].clause; - g_neg_then_clause = - g_neg_then_clause ? produce_rewritten_clause_lrat (g_neg_then_clause, g->lhs, false) : nullptr; - if (g_neg_then_clause) - g_neg_then_id = g_neg_then_clause->id; - - auto &g_else_clause = g->pos_lhs_ids[2].clause; - g_else_clause = - g_else_clause ? produce_rewritten_clause_lrat (g_else_clause, g->lhs, false) : g_else_clause; - if (g_else_clause) - g_else_id = g_else_clause->id; - - auto &g_neg_else_clause = g->pos_lhs_ids[3].clause; - g_neg_else_clause = - g_neg_else_clause ? produce_rewritten_clause_lrat (g_neg_else_clause, g->lhs, false) : nullptr; - if (g_neg_else_clause) - g_neg_else_id = g_neg_else_clause->id; - - LOG (h, "now clauses from"); - CADICAL_assert (h->pos_lhs_ids.size () == 4); - auto &h_then_clause = h->pos_lhs_ids[0].clause; - h_then_clause = - h_then_clause ? produce_rewritten_clause_lrat (h_then_clause, h->lhs, false) : nullptr; - if (h_then_clause) - h_then_id = h_then_clause->id; - - auto &h_neg_then_clause = h->pos_lhs_ids[1].clause; - h_neg_then_clause = - h_neg_then_clause ? produce_rewritten_clause_lrat (h_neg_then_clause, h->lhs, false) : nullptr; - if (h_neg_then_clause) - h_neg_then_id = h_neg_then_clause->id; - - auto &h_else_clause = h->pos_lhs_ids[2].clause; - h_else_clause = - h_else_clause ? produce_rewritten_clause_lrat (h_else_clause, h->lhs, false) : nullptr; - if (h_else_clause) - h_else_id = h_else_clause->id; - - auto &h_neg_else_clause = h->pos_lhs_ids[3].clause; - h_neg_else_clause = - h_neg_else_clause ? produce_rewritten_clause_lrat (h_neg_else_clause, h->lhs, false) : nullptr; - if (h_neg_else_clause) - h_neg_else_id = h_neg_else_clause->id; - - } - - if (degenerated_g_cond) { - LOG ("special case: cond = lhs, g degenerated"); - unsimplified.push_back (-lhs1); - unsimplified.push_back (lhs2); - LRAT_ID id1 = 0; - if (internal->lrat) { - lrat_chain.push_back (g_then_id); - lrat_chain.push_back (h_neg_then_id); - } - id1 = simplify_and_add_to_proof_chain (unsimplified); - - unsimplified.clear (); - unsimplified.push_back (lhs1); - unsimplified.push_back (-lhs2); - LRAT_ID id2 = 0; - if (internal->lrat) { - lrat_chain.push_back (g_neg_else_id); - lrat_chain.push_back (h_else_id); - } - id2 = simplify_and_add_to_proof_chain (unsimplified); - - CADICAL_assert (!internal->lrat || id1); - CADICAL_assert (!internal->lrat || id2); - reasons1.push_back (id1); - reasons2.push_back (id2); - unsimplified.clear (); - return; - } - - if (degenerated_h_cond) { - LOG ("special case: cond = lhs, h degenerated"); - unsimplified.push_back (lhs1); // potentially lhs1 == lhs2 - unsimplified.push_back (-lhs2); - LRAT_ID id1 = 0; - if (internal->lrat) { - lrat_chain.push_back (h_then_id); - lrat_chain.push_back (g_neg_then_id); - } - id1 = simplify_and_add_to_proof_chain (unsimplified); - - unsimplified.clear (); - unsimplified.push_back (-lhs1); - unsimplified.push_back (lhs2); - LRAT_ID id2 = 0; - if (internal->lrat) { - lrat_chain.push_back (h_neg_else_id); - lrat_chain.push_back (g_else_id); - } - id2 = simplify_and_add_to_proof_chain (unsimplified); - - CADICAL_assert (!internal->lrat || id1); - CADICAL_assert (!internal->lrat || id2); - reasons2.push_back (id1); - reasons1.push_back (id2); - unsimplified.clear (); - return; - } - - if (degenerated_g_not_cond) { - LOG ("special case: cond = -lhs, g degenerated"); - unsimplified.push_back (lhs1); - unsimplified.push_back (-lhs2); - LRAT_ID id1 = 0; - if (internal->lrat) { - CADICAL_assert (g_neg_then_id && h_then_id && g_else_id && h_neg_else_id); - lrat_chain.push_back (g_neg_then_id); - lrat_chain.push_back (h_then_id); - } - id1 = simplify_and_add_to_proof_chain (unsimplified); - - unsimplified.clear (); - unsimplified.push_back (-lhs1); - unsimplified.push_back (lhs2); - LRAT_ID id2 = -1; - - if (internal->lrat) { - lrat_chain.push_back (g_else_id); - lrat_chain.push_back (h_neg_else_id); - } - id2 = simplify_and_add_to_proof_chain (unsimplified); - CADICAL_assert (!internal->lrat || id1); - CADICAL_assert (!internal->lrat || id2); - reasons2.push_back (id1); - reasons1.push_back (id2); - unsimplified.clear (); - return; - } - - if (degenerated_h_not_cond) { - LOG ("special case: cond = -lhs, h degenerated"); - unsimplified.push_back (lhs1); - unsimplified.push_back (-lhs2); - LRAT_ID id1 = 0; - if (internal->lrat) { - CADICAL_assert (g_neg_then_id && h_then_id && g_else_id && h_neg_else_id); - lrat_chain.push_back (h_neg_then_id); - lrat_chain.push_back (g_then_id); - } - id1 = simplify_and_add_to_proof_chain (unsimplified); - - unsimplified.clear (); - unsimplified.push_back (-lhs1); - unsimplified.push_back (lhs2); - LRAT_ID id2 = -1; - - if (internal->lrat) { - lrat_chain.push_back (h_else_id); - lrat_chain.push_back (g_neg_else_id); - } - id2 = simplify_and_add_to_proof_chain (unsimplified); - CADICAL_assert (!internal->lrat || id1); - CADICAL_assert (!internal->lrat || id2); - reasons2.push_back (id1); - reasons1.push_back (id2); - unsimplified.clear (); - return; - } - - LOG ("normal path"); - CADICAL_assert (!internal->lrat || degenerated_g_then || - (g_then_id && g_neg_then_id)); - CADICAL_assert (!internal->lrat || degenerated_g_else || - (g_else_id && g_neg_else_id)); - CADICAL_assert (!internal->lrat || degenerated_h_then || - (h_then_id && h_neg_then_id)); - CADICAL_assert (!internal->lrat || degenerated_h_else || - (h_else_id && h_neg_else_id)); - CADICAL_assert (!internal->lrat || g_then_id || h_neg_then_id); - CADICAL_assert (!internal->lrat || g_neg_then_id || h_then_id); - unsimplified.push_back (-lhs1); - unsimplified.push_back (lhs2); - unsimplified.push_back (-cond); - LRAT_ID id1 = 0; - if (degenerated_g_then || degenerated_h_then) { - id1 = degenerated_g_then ? h_neg_then_id : g_then_id; - } else { - if (internal->lrat) { - lrat_chain.push_back (g_then_id); - lrat_chain.push_back (h_neg_then_id); - } - id1 = simplify_and_add_to_proof_chain (unsimplified); - } - unsimplified.pop_back (); - unsimplified.push_back (cond); - - LRAT_ID id2 = 0; - if (degenerated_g_else || degenerated_h_else) { - id2 = degenerated_g_else ? h_neg_else_id : g_else_id; - } else { - if (internal->lrat) { - lrat_chain.push_back (h_neg_else_id); - lrat_chain.push_back (g_else_id); - } - id2 = simplify_and_add_to_proof_chain (unsimplified); - } - unsimplified.pop_back (); - - unsimplified.clear (); - unsimplified.push_back (lhs1); - unsimplified.push_back (-lhs2); - unsimplified.push_back (-cond); - CADICAL_assert (lrat_chain.empty ()); - - LRAT_ID id3 = 0; - if (degenerated_g_then || degenerated_h_then) { - id3 = degenerated_g_then ? h_then_id : g_neg_then_id; - } else { - if (internal->lrat) { - // lrat_chain.push_back (g_then_id); - // lrat_chain.push_back (h_neg_then_id); - lrat_chain.push_back (g_neg_then_id); - lrat_chain.push_back (h_then_id); - } - id3 = simplify_and_add_to_proof_chain (unsimplified); - } - unsimplified.pop_back (); - unsimplified.push_back (cond); - CADICAL_assert (lrat_chain.empty ()); - - LRAT_ID id4 = 0; - if (degenerated_g_else || degenerated_h_else) { - id4 = degenerated_g_else ? h_else_id : g_neg_else_id; - } else { - if (internal->lrat) { - lrat_chain.push_back (g_neg_else_id); - lrat_chain.push_back (h_else_id); - } - id4 = simplify_and_add_to_proof_chain (unsimplified); - } - unsimplified.pop_back (); - - if (internal->lrat) { - CADICAL_assert (!internal->lrat || (id1 && id2 && id3 && id4)); - reasons1.push_back (id1), reasons1.push_back (id2); - reasons2.push_back (id3), reasons2.push_back (id4); - } - - unsimplified.clear (); - - LOG ("finished ITE matching proof chain"); -} - -Gate *Closure::new_ite_gate (int lhs, int cond, int then_lit, int else_lit, - std::vector &&clauses) { - CADICAL_assert (chain.empty ()); - if (else_lit == -then_lit) { - if (then_lit < 0) - LOG ("skipping ternary XOR %d := %d ^ %d", lhs, cond, -then_lit); - else - LOG ("skipping ternary XOR %d := %d ^ %d", -lhs, cond, then_lit); - return nullptr; - } - if (else_lit == then_lit) { - LOG ("found trivial ITE gate %d := %d ? %d : %d", (lhs), (cond), - (then_lit), (else_lit)); - std::vector reasons_implication, reasons_back; - if (internal->lrat) { - merge_ite_gate_same_then_else_lrat (clauses, reasons_implication, - reasons_back); - } - if (merge_literals_lrat (lhs, then_lit, reasons_implication, - reasons_back)) - ++internal->stats.congruence.trivial_ite; - return 0; - } - - rhs.clear (); - rhs.push_back (cond); - rhs.push_back (then_lit); - rhs.push_back (else_lit); - LOG ("ITE gate %d = %d ? %d : %d", lhs, cond, then_lit, else_lit); - - bool negate_lhs = false; - Gate *g = new Gate; - g->lhs = lhs; - g->tag = Gate_Type::ITE_Gate; - g->rhs = {rhs}; - g->pos_lhs_ids = clauses; -#ifdef LOGGING - g->id = -1; -#endif - Gate *h = find_ite_gate (g, negate_lhs); - if (negate_lhs) - lhs = -lhs; - g->lhs = lhs; - check_ite_gate_implied (g); - check_ite_lrat_reasons ( - g, false); // due to merges done before during AND gate detection! - - if (h) { - check_ite_gate_implied (h); - std::vector extra_reasons_lit, extra_reasons_ulit; - add_ite_matching_proof_chain (h, g, h->lhs, lhs, extra_reasons_lit, - extra_reasons_ulit); - if (merge_literals_lrat (h, g, h->lhs, lhs, extra_reasons_lit, - extra_reasons_ulit)) { - ++internal->stats.congruence.ites; - LOG ("found merged literals"); - } - delete_proof_chain (); - delete g; - return h; - } else { - // do not sort clauses here obviously! - // sort (begin (g->rhs), end (g->rhs)); - g->garbage = false; - g->indexed = true; - g->shrunken = false; - g->hash = hash_lits (nonces, g->rhs); - table.insert (g); - ++internal->stats.congruence.gates; -#ifdef LOGGING - g->id = fresh_id++; -#endif - LOG (g, "creating new"); - if (internal->lrat) - update_ite_flags (g); - check_ite_gate_implied (g); - for (auto lit : g->rhs) { - connect_goccs (g, lit); - } - } - check_ite_lrat_reasons (g); - return g; -} - -void check_ite_lits_normalized (const std::vector &lits) { - CADICAL_assert (lits[0] > 0); - CADICAL_assert (lits[1] > 0); - CADICAL_assert (lits[0] != lits[1]); - CADICAL_assert (lits[0] != lits[2]); - CADICAL_assert (lits[1] != lits[2]); - CADICAL_assert (lits[0] != -lits[1]); - CADICAL_assert (lits[0] != -lits[2]); - CADICAL_assert (lits[1] != -lits[2]); -#ifdef CADICAL_NDEBUG - (void) lits; -#endif -} - -void Closure::check_ite_implied (int lhs, int cond, int then_lit, - int else_lit) { - if (internal->lrat) - return; - check_ternary (cond, -else_lit, lhs); - check_ternary (cond, else_lit, -lhs); - check_ternary (-cond, -then_lit, lhs); - check_ternary (-cond, then_lit, -lhs); -} - -void Closure::check_contained_module_rewriting (Clause *c, int lit, - bool normalized, - int except) { -#ifndef CADICAL_NDEBUG - if (lit == except) // happens for degenerated cases - except = 0; - const int normalize_lit = - (lit == except ? except : find_eager_representative (lit)); - CADICAL_assert (!normalized || lit == -except || normalize_lit == lit); - bool found = false; - LOG (c, "looking for (normalized) %d in ", lit); - for (auto other : *c) { - const int normalize_other = - (other == except ? except : find_eager_representative (other)); - LOG ("%d -> %d ", other, normalize_other); - CADICAL_assert (!normalized || other == -except || normalize_other == other); - if (normalize_other == normalize_lit) { - found = true; - break; - } - } - CADICAL_assert (found); -#else - (void) c, (void) lit, (void) normalized, (void) except; -#endif -} - -void Closure::check_ite_lrat_reasons (Gate *g, bool normalized) { -#ifndef CADICAL_NDEBUG - CADICAL_assert (g->tag == Gate_Type::ITE_Gate); - if (!internal->lrat) - return; - CADICAL_assert (g->rhs.size () == 3); - CADICAL_assert (is_tautological_ite_gate (g) || g->pos_lhs_ids.size () == 4); - CADICAL_assert (g->neg_lhs_ids.empty ()); - CADICAL_assert (g->pos_lhs_ids.size () == 4); -#else - (void) g, (void) normalized; -#endif -} - -void Closure::check_ite_gate_implied (Gate *g) { - CADICAL_assert (g->tag == Gate_Type::ITE_Gate); - if (internal->lrat) - return; - check_ite_implied (g->lhs, g->rhs[0], g->rhs[1], g->rhs[2]); -} - -void Closure::init_ite_gate_extraction ( - std::vector &candidates) { - std::vector ternary; - glargecounts.resize (2 * internal->vsize, 0); - for (auto c : internal->clauses) { - if (c->garbage) - continue; - if (c->redundant) - continue; - if (c->size < 3) - continue; - unsigned size = 0; - - CADICAL_assert (!c->garbage); - for (auto lit : *c) { - const signed char v = internal->val (lit); - if (v < 0) - continue; - if (v > 0) { - LOG (c, "deleting as satisfied due to %d", lit); - internal->mark_garbage (c); - goto CONTINUE_COUNTING_NEXT_CLAUSE; - } - if (size == 3) - goto CONTINUE_COUNTING_NEXT_CLAUSE; - size++; - } - if (size < 3) - continue; - CADICAL_assert (size == 3); - ternary.push_back (c); - LOG (c, "counting original ITE gate base"); - for (auto lit : *c) { - if (!internal->val (lit)) - ++largecount (lit); - } - CONTINUE_COUNTING_NEXT_CLAUSE:; - } - - for (auto c : ternary) { - CADICAL_assert (!c->garbage); - CADICAL_assert (!c->redundant); - unsigned positive = 0, negative = 0, twice = 0; - for (auto lit : *c) { - if (internal->val (lit)) - continue; - const int count_not_lit = largecount (-lit); - if (!count_not_lit) - goto CONTINUE_WITH_NEXT_TERNARY_CLAUSE; - const unsigned count_lit = largecount (lit); - CADICAL_assert (count_lit); - if (count_lit > 1 && count_not_lit > 1) - ++twice; - if (lit < 0) - ++negative; - else - ++positive; - } - if (twice < 2) - goto CONTINUE_WITH_NEXT_TERNARY_CLAUSE; - CADICAL_assert (c->size != 2); - for (auto lit : *c) - internal->occs (lit).push_back (c); - if (positive && negative) - candidates.push_back (c); - CONTINUE_WITH_NEXT_TERNARY_CLAUSE:; - } - - ternary.clear (); -} - -void Closure::reset_ite_gate_extraction () { - condbin[0].clear (); - condbin[1].clear (); - condeq[0].clear (); - condeq[1].clear (); - glargecounts.clear (); - internal->clear_occs (); -} - -void Closure::copy_conditional_equivalences (int lit, - lit_implications &condbin) { - CADICAL_assert (condbin.empty ()); - for (auto c : internal->occs (lit)) { - CADICAL_assert (c->size != 2); - int first = 0, second = 0; - for (auto other : *c) { - if (internal->val (other)) - continue; - if (other == lit) - continue; - if (!first) - first = other; - else { - CADICAL_assert (!second); - second = other; - } - } - CADICAL_assert (first), CADICAL_assert (second); - lit_implication p (first, second, c); - - if (internal->vlit (first) < internal->vlit (second)) { - CADICAL_assert (p.first == first); - CADICAL_assert (p.second == second); - } else { - CADICAL_assert (internal->vlit (second) < internal->vlit (first)); - p.swap (); - CADICAL_assert (p.first == second); - CADICAL_assert (p.second == first); - } - LOG (c, "literal %d condition binary clause %d %d", lit, first, second); - condbin.push_back (p); - } -} - -bool less_litpair (lit_equivalence p, lit_equivalence q) { - const int a = p.first; - const int b = q.first; - if (a < b) - return true; - if (b > a) - return false; - const int c = p.second; - const int d = q.second; - return (c < d); -} -struct litpair_rank { - CaDiCaL::Internal *internal; - litpair_rank (Internal *i) : internal (i) {} - typedef uint64_t Type; - Type operator() (const lit_implication &a) const { - uint64_t lita = internal->vlit (a.first); - uint64_t litb = internal->vlit (a.second); - return (lita << 32) + litb; - } -}; - -struct litpair_smaller { - CaDiCaL::Internal *internal; - litpair_smaller (Internal *i) : internal (i) {} - bool operator() (const lit_implication &a, - const lit_implication &b) const { - const auto s = litpair_rank (internal) (a); - const auto t = litpair_rank (internal) (b); - return s < t; - } -}; - -lit_implications::const_iterator -Closure::find_lit_implication_second_literal ( - int lit, lit_implications::const_iterator begin, - lit_implications::const_iterator end) { - LOG ("searching for %d in", lit); - for (auto it = begin; it != end; ++it) - LOG ("%d [%d]", it->first, it->second); - lit_implications::const_iterator found = std::lower_bound ( - begin, end, lit_implication{lit, lit}, - [] (const lit_implication &a, const lit_implication &b) { - return a.second < b.second; - }); -#ifndef CADICAL_NDEBUG - auto found2 = std::binary_search ( - begin, end, lit_implication{lit, lit}, - [] (const lit_implication &a, const lit_implication &b) { - return a.second < b.second; - }); -#endif - - if (found < end && found->second == lit) { - CADICAL_assert (found2 == (found < end)); - return found; - } - return end; -} - -void Closure::search_condeq (int lit, int pos_lit, - lit_implications::const_iterator pos_begin, - lit_implications::const_iterator pos_end, - int neg_lit, - lit_implications::const_iterator neg_begin, - lit_implications::const_iterator neg_end, - lit_equivalences &condeq) { - CADICAL_assert (neg_lit == -pos_lit); - CADICAL_assert (pos_begin < pos_end); - CADICAL_assert (neg_begin < neg_end); - CADICAL_assert (pos_begin->first == pos_lit); - CADICAL_assert (neg_begin->first == neg_lit); - CADICAL_assert (pos_end <= neg_begin || neg_end <= pos_begin); - for (lit_implications::const_iterator p = pos_begin; p != pos_end; p++) { - const int other = p->second; - const int not_other = -other; - const lit_implications::const_iterator other_clause = - find_lit_implication_second_literal (not_other, neg_begin, neg_end); - CADICAL_assert (std::find (begin (*p->clause), end (*p->clause), lit) != - end (*p->clause)); - if (other_clause != neg_end) { - CADICAL_assert (std::find (begin (*other_clause->clause), - end (*other_clause->clause), - neg_lit) != end (*other_clause->clause)); - CADICAL_assert (std::find (begin (*p->clause), end (*p->clause), other) != - end (*p->clause)); - lit_equivalence equivalence (neg_lit, other_clause->clause, other, - p->clause); - if (pos_lit > 0) { - equivalence.negate_both (); - } - if (internal->lrat) - equivalence.check_invariant (); - LOG ("found conditional %d equivalence %d = %d", lit, - equivalence.first, equivalence.second); - CADICAL_assert (equivalence.first > 0); - CADICAL_assert (internal->vlit (equivalence.first) < - internal->vlit (equivalence.second)); - check_ternary (lit, neg_lit, -other); - check_ternary (lit, -neg_lit, other); - condeq.push_back (equivalence); - if (equivalence.second < 0) { - lit_equivalence inverse_equivalence = - equivalence.swap ().negate_both (); - condeq.push_back (inverse_equivalence); - if (internal->lrat) - inverse_equivalence.check_invariant (); - } else { - lit_equivalence inverse_equivalence = equivalence.swap (); - condeq.push_back (inverse_equivalence); - if (internal->lrat) - inverse_equivalence.check_invariant (); - } - } - } -#ifndef LOGGING - (void) lit; -#endif -} - -void Closure::extract_condeq_pairs (int lit, lit_implications &condbin, - lit_equivalences &condeq) { - const lit_implications::const_iterator begin = condbin.cbegin (); - const lit_implications::const_iterator end = condbin.cend (); - lit_implications::const_iterator pos_begin = begin; - int next_lit = 0; - -#ifdef LOGGING - for (const auto &pair : condbin) - LOG ("unsorted conditional %d equivalence %d = %d", lit, pair.first, - pair.second); -#endif - LOG ("searching for first positive literal for lit %d", lit); - for (;;) { - if (pos_begin == end) - return; - next_lit = pos_begin->first; - LOG ("checking %d", next_lit); - if (next_lit > 0) - break; - pos_begin++; - } - - for (;;) { - CADICAL_assert (pos_begin != end); - CADICAL_assert (next_lit == pos_begin->first); - CADICAL_assert (next_lit > 0); - const int pos_lit = next_lit; - lit_implications::const_iterator pos_end = pos_begin + 1; - LOG ("searching for first other literal after finding lit %d", - next_lit); - for (;;) { - if (pos_end == end) - return; - next_lit = pos_end->first; - if (next_lit != pos_lit) - break; - pos_end++; - } - CADICAL_assert (pos_end != end); - CADICAL_assert (next_lit == pos_end->first); - const int neg_lit = -pos_lit; - if (next_lit != neg_lit) { - if (next_lit < 0) { - pos_begin = pos_end + 1; - LOG ("next_lit %d < 0", next_lit); - for (;;) { - if (pos_begin == end) - return; - next_lit = pos_begin->first; - if (next_lit > 0) - break; - pos_begin++; - } - } else - pos_begin = pos_end; - continue; - } - const lit_implications::const_iterator neg_begin = pos_end; - lit_implications::const_iterator neg_end = neg_begin + 1; - while (neg_end != end) { - next_lit = neg_end->first; - if (next_lit != neg_lit) - break; - neg_end++; - } -#ifdef LOGGING - for (lit_implications::const_iterator p = pos_begin; p != pos_end; p++) - LOG ("conditional %d binary clause %d %d with positive %d", (lit), - (p->first), (p->second), (pos_lit)); - for (lit_implications::const_iterator p = neg_begin; p != neg_end; p++) - LOG ("conditional %d binary clause %d %d with negative %d", (lit), - (p->first), (p->second), (neg_lit)); -#endif - const size_t pos_size = pos_end - pos_begin; - const size_t neg_size = neg_end - neg_begin; - if (pos_size <= neg_size) { - LOG ("searching negation of %zu conditional binary clauses " - "with positive %d in %zu conditional binary clauses with %d", - pos_size, (pos_lit), neg_size, (neg_lit)); - search_condeq (lit, pos_lit, pos_begin, pos_end, neg_lit, neg_begin, - neg_end, condeq); - } else { - LOG ("searching negation of %zu conditional binary clauses " - "with negative %d in %zu conditional binary clauses with %d", - neg_size, (neg_lit), pos_size, (pos_lit)); - search_condeq (lit, neg_lit, neg_begin, neg_end, pos_lit, pos_begin, - pos_end, condeq); - } - if (neg_end == end) - return; - CADICAL_assert (next_lit == neg_end->first); - if (next_lit < 0) { - pos_begin = neg_end + 1; - for (;;) { - if (pos_begin == end) - return; - next_lit = pos_begin->first; - if (next_lit > 0) - break; - pos_begin++; - } - } else - pos_begin = neg_end; - } -} - -void Closure::find_conditional_equivalences (int lit, - lit_implications &condbin, - lit_equivalences &condeq) { - CADICAL_assert (condbin.empty ()); - CADICAL_assert (condeq.empty ()); - CADICAL_assert (internal->occs (lit).size () > 1); - copy_conditional_equivalences (lit, condbin); - MSORT (internal->opts.radixsortlim, begin (condbin), end (condbin), - litpair_rank (this->internal), litpair_smaller (this->internal)); - - extract_condeq_pairs (lit, condbin, condeq); - MSORT (internal->opts.radixsortlim, begin (condbin), end (condbin), - litpair_rank (this->internal), litpair_smaller (this->internal)); - -#ifdef LOGGING - for (auto pair : condeq) - LOG ("sorted conditional %d equivalence %d = %d", lit, pair.first, - pair.second); - LOG ("found %zu conditional %d equivalences", condeq.size (), lit); - -#endif -} - -void Closure::merge_condeq (int cond, lit_equivalences &condeq, - lit_equivalences ¬_condeq) { - LOG ("merging cond for literal %d", cond); - auto q = begin (not_condeq); - const auto end_not_condeq = end (not_condeq); - for (auto p : condeq) { - const int lhs = p.first; - const int then_lit = p.second; - if (internal->lrat) - p.check_invariant (); - CADICAL_assert (lhs > 0); - while (q != end_not_condeq && q->first < lhs) - ++q; - while (q != end_not_condeq && q->first == lhs) { - LOG ("looking when %d at p= %d %d", cond, p.first, p.second); - LOG ("looking when %d at %d %d", -cond, q->first, q->second); - lit_equivalence not_cond_pair = *q++; - const int else_lit = not_cond_pair.second; - std::vector clauses; - if (internal->lrat) { - // The then/else literal is the second of the pair, hence the swap - // of the reasons - CADICAL_assert (p.first_clause && p.second_clause); - CADICAL_assert (not_cond_pair.first_clause && not_cond_pair.second_clause); - LOG (p.second_clause, "pairing %d", then_lit); - LOG (p.first_clause, "pairing %d", -then_lit); - LOG (not_cond_pair.second_clause, "pairing %d", else_lit); - LOG (not_cond_pair.first_clause, "pairing %d", -else_lit); - clauses.push_back (LitClausePair (then_lit, p.second_clause)); - clauses.push_back (LitClausePair (-then_lit, p.first_clause)); - clauses.push_back ( - LitClausePair (else_lit, not_cond_pair.second_clause)); - clauses.push_back ( - LitClausePair (-else_lit, not_cond_pair.first_clause)); - if (internal->lrat) - not_cond_pair.check_invariant (); - } - new_ite_gate (lhs, cond, then_lit, else_lit, std::move (clauses)); - if (internal->unsat) - return; - } - } -} - -void Closure::extract_ite_gates_of_literal (int lit) { - LOG ("search for ITE for literal %d ", lit); - find_conditional_equivalences (lit, condbin[0], condeq[0]); - if (!condeq[0].empty ()) { - find_conditional_equivalences (-lit, condbin[1], condeq[1]); - if (!condeq[1].empty ()) { - if (lit < 0) - merge_condeq (-lit, condeq[0], condeq[1]); - else - merge_condeq (lit, condeq[1], condeq[0]); - } - } - - condbin[0].clear (); - condbin[1].clear (); - condeq[0].clear (); - condeq[1].clear (); -} - -void Closure::extract_ite_gates_of_variable (int idx) { - const int lit = idx; - const int not_lit = -idx; - - auto lit_watches = internal->occs (lit); - auto not_lit_watches = internal->occs (not_lit); - const size_t size_lit_watches = lit_watches.size (); - const size_t size_not_lit_watches = not_lit_watches.size (); - if (size_lit_watches <= size_not_lit_watches) { - if (size_lit_watches > 1) - extract_ite_gates_of_literal (lit); - } else { - if (size_not_lit_watches > 1) - extract_ite_gates_of_literal (not_lit); - } -} - -void Closure::extract_ite_gates () { - CADICAL_assert (!full_watching); - if (!internal->opts.congruenceite) - return; - START (extractites); - std::vector candidates; - - init_ite_gate_extraction (candidates); - - for (auto idx : internal->vars) { - if (internal->flags (idx).active ()) { - extract_ite_gates_of_variable (idx); - if (internal->unsat) - break; - } - } - // Kissat has an alternative version MERGE_CONDITIONAL_EQUIVALENCES - reset_ite_gate_extraction (); - STOP (extractites); -} - -/*------------------------------------------------------------------------*/ -void Closure::extract_gates () { - START (extract); - extract_and_gates (); - CADICAL_assert (internal->unsat || lrat_chain.empty ()); - CADICAL_assert (internal->unsat || chain.empty ()); - if (internal->unsat || internal->terminated_asynchronously ()) { - STOP (extract); - return; - } - extract_xor_gates (); - CADICAL_assert (internal->unsat || lrat_chain.empty ()); - CADICAL_assert (internal->unsat || chain.empty ()); - - if (internal->unsat || internal->terminated_asynchronously ()) { - STOP (extract); - return; - } - extract_ite_gates (); - STOP (extract); -} - -/*------------------------------------------------------------------------*/ -// top level function to extract gate -bool Internal::extract_gates () { - if (unsat) - return false; - if (!opts.congruence) - return false; - if (level) - backtrack (); - if (!propagate ()) { - learn_empty_clause (); - return false; - } - if (congruence_delay.bumpreasons.limit) { - LOG ("delaying congruence %" PRId64 " more times", - congruence_delay.bumpreasons.limit); - congruence_delay.bumpreasons.limit--; - return false; - } - - // to remove false literals from clauses - // It makes the technique stronger as long clauses - // can become binary / ternary - // garbage_collection (); - - const int64_t old = stats.congruence.congruent; - const int old_merged = stats.congruence.congruent; - - // congruencebinary is already doing it (and more actually) - if (!internal->opts.congruencebinaries) { - const bool dedup = opts.deduplicate; - opts.deduplicate = true; - mark_duplicated_binary_clauses_as_garbage (); - opts.deduplicate = dedup; - } - ++stats.congruence.rounds; - clear_watches (); - // connect_binary_watches (); - - START_SIMPLIFIER (congruence, CONGRUENCE); - Closure closure (this); - - closure.init_closure (); - CADICAL_assert (unsat || closure.chain.empty ()); - CADICAL_assert (unsat || lrat_chain.empty ()); - closure.extract_binaries (); - CADICAL_assert (unsat || closure.chain.empty ()); - CADICAL_assert (unsat || lrat_chain.empty ()); - closure.extract_gates (); - CADICAL_assert (unsat || closure.chain.empty ()); - CADICAL_assert (unsat || lrat_chain.empty ()); - internal->clear_watches (); - internal->connect_watches (); - closure.reset_extraction (); - - if (!unsat) { - closure.find_units (); - CADICAL_assert (unsat || closure.chain.empty ()); - CADICAL_assert (unsat || lrat_chain.empty ()); - if (!internal->unsat) { - closure.find_equivalences (); - CADICAL_assert (unsat || closure.chain.empty ()); - CADICAL_assert (unsat || lrat_chain.empty ()); - - if (!unsat) { - const int propagated = closure.propagate_units_and_equivalences (); - CADICAL_assert (unsat || closure.chain.empty ()); - if (!unsat && propagated) - closure.forward_subsume_matching_clauses (); - } - } - } - - closure.reset_closure (); - internal->clear_watches (); - internal->connect_watches (); - if (!internal->unsat) { - propagated2 = propagated = 0; - propagate (); - } - CADICAL_assert (closure.new_unwatched_binary_clauses.empty ()); - internal->reset_occs (); - internal->reset_noccs (); - CADICAL_assert (!internal->occurring ()); - CADICAL_assert (lrat_chain.empty ()); - - const int64_t new_merged = stats.congruence.congruent; - -#ifndef CADICAL_QUIET - phase ("congruence-phase", stats.congruence.rounds, "merged %ld literals", - new_merged - old_merged); -#endif - if (!unsat && !internal->propagate ()) - unsat = true; - - STOP_SIMPLIFIER (congruence, CONGRUENCE); - report ('c', !opts.reportall && !(stats.congruence.congruent - old)); -#ifndef CADICAL_NDEBUG - size_t watched = 0; - for (auto v : vars) { - for (auto sgn = -1; sgn <= 1; sgn += 2) { - const int lit = v * sgn; - for (auto w : watches (lit)) { - if (w.binary ()) - CADICAL_assert (!w.clause->garbage); - if (w.clause->garbage) - continue; - ++watched; - LOG (w.clause, "watched"); - } - } - } - LOG ("and now the clauses:"); - size_t nb_clauses = 0; - for (auto c : clauses) { - if (c->garbage) - continue; - LOG (c, "watched"); - ++nb_clauses; - } - CADICAL_assert (watched == nb_clauses * 2); -#endif - CADICAL_assert (!internal->occurring ()); - - if (new_merged == old_merged) { - congruence_delay.bumpreasons.interval++; - } else { - congruence_delay.bumpreasons.interval /= 2; - } - - LOG ("delay congruence internal %" PRId64, - congruence_delay.bumpreasons.interval); - congruence_delay.bumpreasons.limit = - congruence_delay.bumpreasons.interval; - - return new_merged != old_merged; -} - -} // namespace CaDiCaL - -ABC_NAMESPACE_IMPL_END diff --git a/src/sat/cadical/cadical_constrain.cpp b/src/sat/cadical/cadical_constrain.cpp deleted file mode 100644 index 47f14e3d97..0000000000 --- a/src/sat/cadical/cadical_constrain.cpp +++ /dev/null @@ -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::const_iterator end = constraint.end (); - vector::iterator i = constraint.begin (); - for (vector::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 diff --git a/src/sat/cadical/cadical_contract.cpp b/src/sat/cadical/cadical_contract.cpp deleted file mode 100644 index 7485ff4aab..0000000000 --- a/src/sat/cadical/cadical_contract.cpp +++ /dev/null @@ -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 diff --git a/src/sat/cadical/cadical_cover.cpp b/src/sat/cadical/cadical_cover.cpp deleted file mode 100644 index 0d3253e98e..0000000000 --- a/src/sat/cadical/cadical_cover.cpp +++ /dev/null @@ -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 added; // acts as trail - std::vector extend; // extension stack for witness - std::vector covered; // clause literals or added through CLA - std::vector 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 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 diff --git a/src/sat/cadical/cadical_decide.cpp b/src/sat/cadical/cadical_decide.cpp deleted file mode 100644 index 0874cffd74..0000000000 --- a/src/sat/cadical/cadical_decide.cpp +++ /dev/null @@ -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 diff --git a/src/sat/cadical/cadical_decompose.cpp b/src/sat/cadical/cadical_decompose.cpp deleted file mode 100644 index 9081321b4b..0000000000 --- a/src/sat/cadical/cadical_decompose.cpp +++ /dev/null @@ -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 Internal::decompose_analyze_binary_clauses (DFS *dfs, - int from) { - vector 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 &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> &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_delete (dfs); - int *reprs = new int[size_dfs]; - DeferDeleteArray reprs_delete (reprs); - clear_n (reprs, size_dfs); - vector> dfs_chains; - dfs_chains.resize (size_dfs); - if (lrat) { - for (size_t i = 0; i > size_dfs; i++) { - vector 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 work; // depth first search working stack - vector 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 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 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 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 diff --git a/src/sat/cadical/cadical_deduplicate.cpp b/src/sat/cadical/cadical_deduplicate.cpp deleted file mode 100644 index 49d26a5717..0000000000 --- a/src/sat/cadical/cadical_deduplicate.cpp +++ /dev/null @@ -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 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 diff --git a/src/sat/cadical/cadical_definition.cpp b/src/sat/cadical/cadical_definition.cpp deleted file mode 100644 index 7c8fe4fbbb..0000000000 --- a/src/sat/cadical/cadical_definition.cpp +++ /dev/null @@ -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 clauses[2]; - int lit; - vector> 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 &clauses0 = extractor->clauses[0]; - const vector &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_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 &clauses0 = extractor->clauses[0]; - const vector &clauses1 = extractor->clauses[1]; - vector &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 diff --git a/src/sat/cadical/cadical_drattracer.cpp b/src/sat/cadical/cadical_drattracer.cpp deleted file mode 100644 index eb73b4c95a..0000000000 --- a/src/sat/cadical/cadical_drattracer.cpp +++ /dev/null @@ -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 &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 &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 &clause, - const vector &) { - 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 &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 diff --git a/src/sat/cadical/cadical_elim.cpp b/src/sat/cadical/cadical_elim.cpp deleted file mode 100644 index 951dfee742..0000000000 --- a/src/sat/cadical/cadical_elim.cpp +++ /dev/null @@ -1,1178 +0,0 @@ -#include "global.h" - -#include "internal.hpp" - -ABC_NAMESPACE_IMPL_START - -namespace CaDiCaL { - -/*------------------------------------------------------------------------*/ - -// Implements a variant of bounded variable elimination as originally -// described in our SAT'05 paper introducing 'SATeLite'. This is an -// inprocessing version, i.e., it is interleaved with search and triggers -// subsumption and strengthening, blocked and covered clause elimination -// during elimination rounds. It focuses only those variables which -// occurred in removed irredundant clauses since the last time an -// elimination round was run. By bounding the maximum resolvent size we can -// run each elimination round until completion. See the code of 'elim' for -// how elimination rounds are interleaved with blocked clause elimination -// and subsumption (which in turn also calls vivification and transitive -// reduction of the binary implication graph). - -/*------------------------------------------------------------------------*/ - -inline double Internal::compute_elim_score (unsigned lit) { - CADICAL_assert (1 <= lit), CADICAL_assert (lit <= (unsigned) max_var); - const unsigned uidx = 2 * lit; - const double pos = internal->ntab[uidx]; - const double neg = internal->ntab[uidx + 1]; - if (!pos) - return -neg; - if (!neg) - return -pos; - double sum = 0, prod = 0; - if (opts.elimsum) - sum = opts.elimsum * (pos + neg); - if (opts.elimprod) - prod = opts.elimprod * (pos * neg); - return prod + sum; -} - -/*------------------------------------------------------------------------*/ - -bool elim_more::operator() (unsigned a, unsigned b) { - const auto s = internal->compute_elim_score (a); - const auto t = internal->compute_elim_score (b); - if (s > t) - return true; - if (s < t) - return false; - return a > b; -} - -/*------------------------------------------------------------------------*/ - -// Note that the new fast subsumption algorithm implemented in 'subsume' -// does not distinguish between irredundant and redundant clauses and is -// also run during search to strengthen and remove 'sticky' redundant -// clauses but also irredundant ones. So beside learned units during search -// or as consequence of other preprocessors, these subsumption rounds during -// search can remove (irredundant) clauses (and literals), which in turn -// might make new bounded variable elimination possible. This is tested -// in the 'bool ineliminating ()' guard. - -bool Internal::ineliminating () { - - if (!opts.elim) - return false; - if (!preprocessing && !opts.inprocessing) - return false; - if (preprocessing) - CADICAL_assert (lim.preprocessing); - - // Respect (increasing) conflict limit. - // - if (lim.elim >= stats.conflicts) - return false; - - // Wait until there are new units or new removed variables - // (in removed or shrunken irredundant clauses and thus marked). - // - if (last.elim.fixed < stats.all.fixed) - return true; - if (last.elim.marked < stats.mark.elim) - return true; - - // VERBOSE (3, "elim not scheduled due to fixpoint"); - return false; -} - -/*------------------------------------------------------------------------*/ - -// Update the global elimination schedule after adding or removing a clause. - -void Internal::elim_update_added_clause (Eliminator &eliminator, - Clause *c) { - CADICAL_assert (!c->redundant); - ElimSchedule &schedule = eliminator.schedule; - for (const auto &lit : *c) { - if (!active (lit)) - continue; - occs (lit).push_back (c); - if (frozen (lit)) - continue; - noccs (lit)++; - const int idx = abs (lit); - if (schedule.contains (idx)) - schedule.update (idx); - } -} - -void Internal::elim_update_removed_lit (Eliminator &eliminator, int lit) { - if (!active (lit)) - return; - if (frozen (lit)) - return; - int64_t &score = noccs (lit); - CADICAL_assert (score > 0); - score--; - const int idx = abs (lit); - ElimSchedule &schedule = eliminator.schedule; - if (schedule.contains (idx)) - schedule.update (idx); - else { - LOG ("rescheduling %d for elimination after removing clause", idx); - schedule.push_back (idx); - } -} - -void Internal::elim_update_removed_clause (Eliminator &eliminator, - Clause *c, int except) { - CADICAL_assert (!c->redundant); - for (const auto &lit : *c) { - if (lit == except) - continue; - CADICAL_assert (lit != -except); - elim_update_removed_lit (eliminator, lit); - } -} - -/*------------------------------------------------------------------------*/ - -// Since we do not have watches we have to do our own unit propagation -// during elimination as soon we find a unit clause. This finds new units -// and also marks clauses satisfied by those units as garbage immediately. - -void Internal::elim_propagate (Eliminator &eliminator, int root) { - CADICAL_assert (val (root) > 0); - vector work; - size_t i = 0; - work.push_back (root); - while (i < work.size ()) { - int lit = work[i++]; - LOG ("elimination propagation of %d", lit); - CADICAL_assert (val (lit) > 0); - const Occs &ns = occs (-lit); - for (const auto &c : ns) { - if (c->garbage) - continue; - int unit = 0, satisfied = 0; - for (const auto &other : *c) { - const signed char tmp = val (other); - if (tmp < 0) - continue; - if (tmp > 0) { - satisfied = other; - break; - } - if (unit) - unit = INT_MIN; - else - unit = other; - } - if (satisfied) { - LOG (c, "elimination propagation of %d finds %d satisfied", lit, - satisfied); - elim_update_removed_clause (eliminator, c, satisfied); - mark_garbage (c); - } else if (!unit) { - LOG ("empty clause during elimination propagation of %d", lit); - // need to set conflict = c for lrat - conflict = c; - learn_empty_clause (); - conflict = 0; - break; - } else if (unit != INT_MIN) { - LOG ("new unit %d during elimination propagation of %d", unit, lit); - build_chain_for_units (unit, c, 0); - assign_unit (unit); - work.push_back (unit); - } - } - if (unsat) - break; - const Occs &ps = occs (lit); - for (const auto &c : ps) { - if (c->garbage) - continue; - LOG (c, "elimination propagation of %d produces satisfied", lit); - elim_update_removed_clause (eliminator, c, lit); - mark_garbage (c); - } - } -} - -/*------------------------------------------------------------------------*/ - -// On-the-fly self-subsuming resolution during variable elimination is due -// to HyoJung Han, Fabio Somenzi, SAT'09. Basically while resolving two -// clauses we test the resolvent to be smaller than one of the antecedents. -// If this is the case the pivot can be removed from the antecedent -// on-the-fly and the resolution can be skipped during elimination. - -void Internal::elim_on_the_fly_self_subsumption (Eliminator &eliminator, - Clause *c, int pivot) { - LOG (c, "pivot %d on-the-fly self-subsuming resolution", pivot); - stats.elimotfstr++; - stats.strengthened++; - CADICAL_assert (clause.empty ()); - for (const auto &lit : *c) { - if (lit == pivot) - continue; - const signed char tmp = val (lit); - CADICAL_assert (tmp <= 0); - if (tmp < 0) - continue; - clause.push_back (lit); - } - Clause *r = new_resolved_irredundant_clause (); - elim_update_added_clause (eliminator, r); - clause.clear (); - lrat_chain.clear (); - elim_update_removed_clause (eliminator, c, pivot); - mark_garbage (c); -} - -/*------------------------------------------------------------------------*/ - -// Resolve two clauses on the pivot literal 'pivot', which is assumed to -// occur in opposite phases in 'c' and 'd'. The actual resolvent is stored -// in the temporary global 'clause' if it is not redundant. It is -// considered redundant if one of the clauses is already marked as garbage -// it is root level satisfied, the resolvent is empty, a unit, or produces a -// self-subsuming resolution, which results in the pivot to be removed from -// at least one of the antecedents. - -// Note that current root level assignments are taken into account, i.e., by -// removing root level falsified literals. The function returns true if the -// resolvent is not redundant and for instance has to be taken into account -// during bounded variable elimination. - -// Detected units are immediately assigned and in case the last argument is -// true also propagated eagerly in a elimination specific propagation -// routine, which not only finds units but also updates the schedule. - -// When this function is called during computation of the number of -// non-trivial (or non-satisfied) resolvents we can eagerly propagate units. -// But during actually adding the resolvents this results in problems as we -// found one rare test case '../test/trace/reg0056.trace' (out of billions), -// where the pivot itself was assigned during such a propagation while -// adding resolvents and lead to pushing a clause to the reconstruction -// stack that later flipped the value of the pivot (while all other literals -// in that clause were unit implied too). Not pushing the pivot clauses to -// the reconstruction stack produced a wrong model too. Our fix is to only -// eagerly propagate during computation of the number of resolvents and -// otherwise delay propagation until the end of elimination (which is less -// precise regarding scheduling but very rarely happens). - -bool Internal::resolve_clauses (Eliminator &eliminator, Clause *c, - int pivot, Clause *d, - const bool propagate_eagerly) { - - CADICAL_assert (!c->redundant); - CADICAL_assert (!d->redundant); - - stats.elimres++; - - if (c->garbage || d->garbage) - return false; - if (c->size > d->size) { - pivot = -pivot; - swap (c, d); - } - - CADICAL_assert (!level); - CADICAL_assert (clause.empty ()); - - int satisfied = 0; // Contains this satisfying literal. - int tautological = 0; // Clashing literal if tautological. - - int s = 0; // Actual literals from 'c'. - int t = 0; // Actual literals from 'd'. - - // First determine whether the first antecedent is satisfied, add its - // literals to 'clause' and mark them (except for 'pivot'). - // - for (const auto &lit : *c) { - if (lit == pivot) { - s++; - continue; - } - CADICAL_assert (lit != -pivot); - const signed char tmp = val (lit); - if (tmp > 0) { - satisfied = lit; - break; - } else if (tmp < 0) { - if (!lrat) - continue; - 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); - continue; - } else - mark (lit), clause.push_back (lit), s++; - } - if (satisfied) { - LOG (c, "satisfied by %d antecedent", satisfied); - elim_update_removed_clause (eliminator, c, satisfied); - mark_garbage (c); - clause.clear (); - lrat_chain.clear (); - clear_analyzed_literals (); - unmark (c); - return false; - } - - // Then determine whether the second antecedent is satisfied, add its - // literal to 'clause' and check whether a clashing literal is found, such - // that the resolvent would be tautological. - // - for (const auto &lit : *d) { - if (lit == -pivot) { - t++; - continue; - } - CADICAL_assert (lit != pivot); - signed char tmp = val (lit); - if (tmp > 0) { - satisfied = lit; - break; - } else if (tmp < 0) { - if (!lrat) - continue; - 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); - continue; - } else if ((tmp = marked (lit)) < 0) { - tautological = lit; - break; - } else if (!tmp) - clause.push_back (lit), t++; - else - CADICAL_assert (tmp > 0), t++; - } - - clear_analyzed_literals (); - unmark (c); - const int64_t size = clause.size (); - - if (lrat) { - lrat_chain.push_back (d->id); - lrat_chain.push_back (c->id); - } - - if (satisfied) { - LOG (d, "satisfied by %d antecedent", satisfied); - elim_update_removed_clause (eliminator, d, satisfied); - mark_garbage (d); - clause.clear (); - lrat_chain.clear (); - return false; - } - - LOG (c, "first antecedent"); - LOG (d, "second antecedent"); - - if (tautological) { - clause.clear (); - LOG ("resolvent tautological on %d", tautological); - lrat_chain.clear (); - return false; - } - - if (!size) { - clause.clear (); - LOG ("empty resolvent"); - learn_empty_clause (); // already clears lrat_chain. - return false; - } - - if (size == 1) { - int unit = clause[0]; - LOG ("unit resolvent %d", unit); - clause.clear (); - assign_unit (unit); // already clears lrat_chain. - if (propagate_eagerly) - elim_propagate (eliminator, unit); - return false; - } - - LOG (clause, "resolvent"); - CADICAL_assert (!lrat || !lrat_chain.empty ()); - - // Double self-subsuming resolution. The clauses 'c' and 'd' are - // identical except for the pivot which occurs in different phase. The - // resolvent subsumes both antecedents. - - if (s > size && t > size) { - CADICAL_assert (s == size + 1); - CADICAL_assert (t == size + 1); - clause.clear (); - // LRAT is c + d (+ eventual units) - elim_on_the_fly_self_subsumption (eliminator, c, pivot); - LOG (d, "double pivot %d on-the-fly self-subsuming resolution", -pivot); - stats.elimotfsub++; - stats.subsumed++; - elim_update_removed_clause (eliminator, d, -pivot); - mark_garbage (d); - return false; - } - - // Single self-subsuming resolution: The pivot can be removed from 'c', - // which is implemented by adding a clause which is the same as 'c' but - // with 'pivot' removed and then marking 'c' as garbage. - - if (s > size) { - CADICAL_assert (s == size + 1); - clause.clear (); - // LRAT is c + d (+ eventual units) - elim_on_the_fly_self_subsumption (eliminator, c, pivot); - return false; - } - - // Same single self-subsuming resolution situation, but only for 'd'. - - if (t > size) { - CADICAL_assert (t == size + 1); - clause.clear (); - // LRAT is c + d (+ eventual units) -> same. - elim_on_the_fly_self_subsumption (eliminator, d, -pivot); - return false; - } - if (propagate_eagerly) - lrat_chain.clear (); - return true; -} - -/*------------------------------------------------------------------------*/ - -// 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::elim_resolvents_are_bounded (Eliminator &eliminator, - int pivot) { - const bool substitute = !eliminator.gates.empty (); - const bool resolve_gates = eliminator.definition_unit; - if (substitute) - LOG ("trying to substitute %d", pivot); - - stats.elimtried++; - - CADICAL_assert (!unsat); - CADICAL_assert (active (pivot)); - - const Occs &ps = occs (pivot); - const Occs &ns = occs (-pivot); - const int64_t pos = ps.size (); - const int64_t neg = ns.size (); - if (!pos || !neg) - return lim.elimbound >= 0; - const int64_t bound = pos + neg + lim.elimbound; - - LOG ("checking number resolvents on %d bounded by " - "%" PRId64 " = %" PRId64 " + %" PRId64 " + %" PRId64, - pivot, bound, pos, neg, lim.elimbound); - - // 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_gates && substitute && c->gate == d->gate) - continue; - stats.elimrestried++; - 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.elimclslim) { - 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::elim_add_resolvents (Eliminator &eliminator, - int pivot) { - - const bool substitute = !eliminator.gates.empty (); - const bool resolve_gates = eliminator.definition_unit; - if (substitute) { - LOG ("substituting pivot %d by resolving with %zd gate clauses", pivot, - eliminator.gates.size ()); - stats.elimsubst++; - } - switch (eliminator.gatetype) { - case EQUI: - stats.eliminated_equi++; - break; - case AND: - stats.eliminated_and++; - break; - case ITE: - stats.eliminated_ite++; - break; - case XOR: - stats.eliminated_xor++; - break; - case DEF: - stats.eliminated_def++; - break; - default: - CADICAL_assert (eliminator.gatetype == NO); - } - - 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_gates && substitute && c->gate == d->gate) - 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); -} - -/*------------------------------------------------------------------------*/ - -// Remove clauses with 'pivot' and '-pivot' by marking them as garbage and -// push them on the extension stack. - -void Internal::mark_eliminated_clauses_as_garbage ( - Eliminator &eliminator, int pivot, bool &deleted_binary_clause) { - CADICAL_assert (!unsat); - - LOG ("marking irredundant clauses with %d as garbage", pivot); - - const int64_t substitute = eliminator.gates.size (); - if (substitute) - LOG ("pushing %" PRId64 " gate clauses on extension stack", substitute); -#ifndef CADICAL_NDEBUG - int64_t pushed = 0; -#endif - Occs &ps = occs (pivot); - for (const auto &c : ps) { - if (c->garbage) - continue; - CADICAL_assert (!c->redundant); - if (!substitute || c->gate) { - if (proof) - proof->weaken_minus (c); - if (c->size == 2) - deleted_binary_clause = true; - external->push_clause_on_extension_stack (c, pivot); -#ifndef CADICAL_NDEBUG - pushed++; -#endif - } - mark_garbage (c); - elim_update_removed_clause (eliminator, c, pivot); - } - erase_occs (ps); - - LOG ("marking irredundant clauses with %d as garbage", -pivot); - - Occs &ns = occs (-pivot); - for (const auto &d : ns) { - if (d->garbage) - continue; - CADICAL_assert (!d->redundant); - if (!substitute || d->gate) { - if (proof) - proof->weaken_minus (d); - if (d->size == 2) - deleted_binary_clause = true; - external->push_clause_on_extension_stack (d, -pivot); -#ifndef CADICAL_NDEBUG - pushed++; -#endif - } - mark_garbage (d); - elim_update_removed_clause (eliminator, d, -pivot); - } - erase_occs (ns); - - if (substitute) - CADICAL_assert (pushed <= substitute); - - // Unfortunately, we can not use the trick by Niklas Soerensson anymore, - // which avoids saving all clauses on the extension stack. This would - // break our new incremental 'restore' logic. -} - -/*------------------------------------------------------------------------*/ - -// Try to eliminate 'pivot' by bounded variable elimination. -void Internal::try_to_eliminate_variable (Eliminator &eliminator, int pivot, - bool &deleted_binary_clause) { - - if (!active (pivot)) - return; - CADICAL_assert (!frozen (pivot)); - - // First flush garbage clauses. - // - int64_t pos = flush_occs (pivot); - int64_t neg = flush_occs (-pivot); - - 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); - - if (pos && neg > opts.elimocclim) { - LOG ("too many occurrences thus not eliminated %d", pivot); - CADICAL_assert (!eliminator.schedule.contains (abs (pivot))); - return; - } - - 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 (pos) - find_gate_clauses (eliminator, pivot); - - if (!unsat && !val (pivot)) { - if (elim_resolvents_are_bounded (eliminator, pivot)) { - LOG ("number of resolvents on %d are bounded", pivot); - elim_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); -} - -/*------------------------------------------------------------------------*/ - -void Internal:: - mark_redundant_clauses_with_eliminated_variables_as_garbage () { - for (const auto &c : clauses) { - if (c->garbage || !c->redundant) - continue; - bool clean = true; - for (const auto &lit : *c) { - Flags &f = flags (lit); - if (f.eliminated ()) { - clean = false; - break; - } - if (f.pure ()) { - clean = false; - break; - } - } - if (!clean) - mark_garbage (c); - } -} - -/*------------------------------------------------------------------------*/ - -// 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::elim_round (bool &completed, bool &deleted_binary_clause) { - - CADICAL_assert (opts.elim); - CADICAL_assert (!unsat); - - START_SIMPLIFIER (elim, ELIM); - stats.elimrounds++; - - int64_t marked_before = last.elim.marked; - last.elim.marked = stats.mark.elim; - 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 ("elim-round", stats.elimrounds, - "limit of %" PRId64 " resolutions", delta); - - resolution_limit = stats.elimres + delta; - } else { - PHASE ("elim-round", stats.elimrounds, "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 ("elim-round", stats.elimrounds, - "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_eliminate_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 (); - - if (!completed) - last.elim.marked = marked_before; - - PHASE ("elim-round", stats.elimrounds, - "tried to eliminate %" PRId64 " variables %.0f%% (%zd remain)", - tried, percent (tried, scheduled), schedule.size ()); - - schedule.erase (); - - // Collect potential literal clause instantiation pairs, which needs full - // occurrence lists and thus we have it here before resetting them. - // - Instantiator instantiator; - if (!unsat && !terminated_asynchronously () && opts.instantiate) - collect_instantiation_candidates (instantiator); - - 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; -#ifndef CADICAL_QUIET - int64_t resolutions = stats.elimres - old_resolutions; - PHASE ("elim-round", stats.elimrounds, - "eliminated %d variables %.0f%% in %" PRId64 " resolutions", - eliminated, percent (eliminated, scheduled), resolutions); -#endif - - last.elim.subsumephases = stats.subsumephases; - const int units = stats.all.fixed - old_fixed; - report ('e', !opts.reportall && !(eliminated + units)); - STOP_SIMPLIFIER (elim, ELIM); - - if (!unsat && !terminated_asynchronously () && - instantiator) // Do we have candidate pairs? - instantiate (instantiator); - - return eliminated; // non-zero if successful -} - -/*------------------------------------------------------------------------*/ - -// Increase elimination bound (additional clauses allowed during variable -// elimination), which is triggered if elimination with last bound completed -// (including no new subsumptions). This was pioneered by GlueMiniSAT in -// the SAT Race 2015 and then picked up Chanseok Oh in his COMinisatPS -// solver, which in turn is used in the Maple series of SAT solvers. -// The bound is no increased if the maximum bound is reached. - -void Internal::increase_elimination_bound () { - - if (lim.elimbound >= opts.elimboundmax) - return; - - if (lim.elimbound < 0) - lim.elimbound = 0; - else if (!lim.elimbound) - lim.elimbound = 1; - else - lim.elimbound *= 2; - - if (lim.elimbound > opts.elimboundmax) - lim.elimbound = opts.elimboundmax; - - PHASE ("elim-phase", stats.elimphases, - "new elimination bound %" PRId64 "", lim.elimbound); - - // Now reschedule all active variables for elimination again. - // -#ifdef LOGGING - int count = 0; -#endif - for (auto idx : vars) { - if (!active (idx)) - continue; - if (flags (idx).elim) - continue; - mark_elim (idx); -#ifdef LOGGING - count++; -#endif - } - LOG ("marked %d variables as elimination candidates", count); - - report ('^'); -} - -void Internal::init_citten () { - if (!opts.elimdef) - return; - CADICAL_assert (!citten); - citten = cadical_kitten_init (); -} - -void Internal::reset_citten () { - if (citten) { - cadical_kitten_release (citten); - citten = 0; - } -} - -/*------------------------------------------------------------------------*/ - -void Internal::elim (bool update_limits) { - - if (unsat) - return; - if (level) - backtrack (); - if (!propagate ()) { - learn_empty_clause (); - return; - } - - stats.elimphases++; - PHASE ("elim-phase", stats.elimphases, - "starting at most %d elimination rounds", opts.elimrounds); - - if (external_prop) { - CADICAL_assert (!level); - private_steps = true; - } - -#ifndef CADICAL_QUIET - int old_active_variables = active (); - int old_eliminated = stats.all.eliminated; -#endif - - // Make sure there was a complete subsumption phase since last - // elimination including vivification etc. - // - if (last.elim.subsumephases == stats.subsumephases) - subsume (); - - reset_watches (); // saves lots of memory - - init_citten (); - - // 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 - elim_round (round_complete, deleted_binary_clause); - - if (!round_complete) { - PHASE ("elim-phase", stats.elimphases, "last round %d incomplete %s", - round, eliminated ? "but successful" : "and unsuccessful"); - CADICAL_assert (!phase_complete); - break; - } - - if (round++ >= opts.elimrounds) { - PHASE ("elim-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; - if (block ()) - continue; - if (cover ()) - continue; - - // Was not able to generate new variable elimination candidates after - // variable elimination round, neither through subsumption, nor blocked, - // nor covered clause elimination. - // - PHASE ("elim-phase", stats.elimphases, - "no new variable elimination candidates"); - - CADICAL_assert (round_complete); - phase_complete = true; - } - - if (phase_complete) { - stats.elimcompleted++; - PHASE ("elim-phase", stats.elimphases, - "fully completed elimination %" PRId64 - " at elimination bound %" PRId64 "", - stats.elimcompleted, lim.elimbound); - } else { - PHASE ("elim-phase", stats.elimphases, - "incomplete elimination %" PRId64 - " at elimination bound %" PRId64 "", - stats.elimcompleted + 1, lim.elimbound); - } - - reset_citten (); - 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 (); - } - } - - // If we ran variable elimination until completion we increase the - // variable elimination bound and reschedule elimination of all variables. - // - if (phase_complete) - increase_elimination_bound (); - -#ifndef CADICAL_QUIET - eliminated = stats.all.eliminated - old_eliminated; - PHASE ("elim-phase", stats.elimphases, "eliminated %d variables %.2f%%", - eliminated, percent (eliminated, old_active_variables)); -#endif - - if (external_prop) { - CADICAL_assert (!level); - private_steps = false; - } - - if (!update_limits) - return; - - int64_t delta = scale (opts.elimint * (stats.elimphases + 1)); - lim.elim = stats.conflicts + delta; - - PHASE ("elim-phase", stats.elimphases, - "new limit at %" PRId64 " conflicts after %" PRId64 " conflicts", - lim.elim, delta); - - last.elim.fixed = stats.all.fixed; -} - -} // namespace CaDiCaL - -ABC_NAMESPACE_IMPL_END diff --git a/src/sat/cadical/cadical_elimfast.cpp b/src/sat/cadical/cadical_elimfast.cpp deleted file mode 100644 index 90dacf8c0a..0000000000 --- a/src/sat/cadical/cadical_elimfast.cpp +++ /dev/null @@ -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 diff --git a/src/sat/cadical/cadical_ema.cpp b/src/sat/cadical/cadical_ema.cpp deleted file mode 100644 index e8faf5d763..0000000000 --- a/src/sat/cadical/cadical_ema.cpp +++ /dev/null @@ -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 diff --git a/src/sat/cadical/cadical_extend.cpp b/src/sat/cadical/cadical_extend.cpp deleted file mode 100644 index c549e90fa2..0000000000 --- a/src/sat/cadical/cadical_extend.cpp +++ /dev/null @@ -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 (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 &c, const vector &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 (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 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 (*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 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 (*(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 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 diff --git a/src/sat/cadical/cadical_external.cpp b/src/sat/cadical/cadical_external.cpp deleted file mode 100644 index 67d1f918d8..0000000000 --- a/src/sat/cadical/cadical_external.cpp +++ /dev/null @@ -1,1028 +0,0 @@ -#include "global.h" - -#include "internal.hpp" -#include - -ABC_NAMESPACE_IMPL_START - -namespace CaDiCaL { - -External::External (Internal *i) - : internal (i), max_var (0), vsize (0), extended (false), - concluded (false), terminator (0), learner (0), fixed_listener (0), - propagator (0), solution (0), vars (max_var) { - CADICAL_assert (internal); - CADICAL_assert (!internal->external); - internal->external = this; -} - -External::~External () { - if (solution) - delete[] solution; -} - -void External::enlarge (int new_max_var) { - - CADICAL_assert (!extended); - - size_t new_vsize = vsize ? 2 * vsize : 1 + (size_t) new_max_var; - while (new_vsize <= (size_t) new_max_var) - new_vsize *= 2; - LOG ("enlarge external size from %zd to new size %zd", vsize, new_vsize); - vsize = new_vsize; -} - -void External::init (int new_max_var, bool extension) { - CADICAL_assert (!extended); - if (new_max_var <= max_var) - return; - int new_vars = new_max_var - max_var; - int old_internal_max_var = internal->max_var; - int new_internal_max_var = old_internal_max_var + new_vars; - internal->init_vars (new_internal_max_var); - if ((size_t) new_max_var >= vsize) - enlarge (new_max_var); - LOG ("initialized %d external variables", new_vars); - if (!max_var) { - CADICAL_assert (e2i.empty ()); - e2i.push_back (0); - ext_units.push_back (0); - ext_units.push_back (0); - ext_flags.push_back (0); - ervars.push_back (0); - CADICAL_assert (internal->i2e.empty ()); - internal->i2e.push_back (0); - } else { - CADICAL_assert (e2i.size () == (size_t) max_var + 1); - CADICAL_assert (internal->i2e.size () == (size_t) old_internal_max_var + 1); - } - unsigned iidx = old_internal_max_var + 1, eidx; - for (eidx = max_var + 1u; eidx <= (unsigned) new_max_var; - eidx++, iidx++) { - LOG ("mapping external %u to internal %u", eidx, iidx); - CADICAL_assert (e2i.size () == eidx); - e2i.push_back (iidx); - ext_units.push_back (0); - ext_units.push_back (0); - ext_flags.push_back (0); - ervars.push_back (0); - internal->i2e.push_back (eidx); - CADICAL_assert (internal->i2e[iidx] == (int) eidx); - CADICAL_assert (e2i[eidx] == (int) iidx); - } - if (extension) - internal->stats.variables_extension += new_vars; - else - internal->stats.variables_original += new_vars; - if (new_max_var >= (int64_t) is_observed.size ()) - is_observed.resize (1 + (size_t) new_max_var, false); - if (internal->opts.checkfrozen) - if (new_max_var >= (int64_t) moltentab.size ()) - moltentab.resize (1 + (size_t) new_max_var, false); - CADICAL_assert (iidx == (size_t) new_internal_max_var + 1); - CADICAL_assert (eidx == (size_t) new_max_var + 1); - CADICAL_assert (ext_units.size () == (size_t) new_max_var * 2 + 2); - max_var = new_max_var; -} - -/*------------------------------------------------------------------------*/ - -void External::reset_assumptions () { - assumptions.clear (); - internal->reset_assumptions (); -} - -void External::reset_concluded () { - concluded = false; - internal->reset_concluded (); -} - -void External::reset_constraint () { - constraint.clear (); - internal->reset_constraint (); -} - -void External::reset_extended () { - if (!extended) - return; - LOG ("reset extended"); - extended = false; -} - -void External::reset_limits () { internal->reset_limits (); } - -/*------------------------------------------------------------------------*/ - -// when extension is true, elit should be a fresh variable and -// we can set a flag that it is an extension variable. -// This is then used in the API contracts, that extension variables are -// never part of the input -int External::internalize (int elit, bool extension) { - int ilit; - if (elit) { - CADICAL_assert (elit != INT_MIN); - const int eidx = abs (elit); - if (extension && eidx <= max_var) - FATAL ("can not add a definition for an already used variable %d", - eidx); - if (eidx > max_var) { - init (eidx, extension); - } - if (extension) { - CADICAL_assert (ervars.size () > (size_t) eidx); - ervars[eidx] = true; - } - ilit = e2i[eidx]; - if (elit < 0) - ilit = -ilit; - if (!ilit) { - CADICAL_assert (internal->max_var < INT_MAX); - ilit = internal->max_var + 1u; - internal->init_vars (ilit); - e2i[eidx] = ilit; - LOG ("mapping external %d to internal %d", eidx, ilit); - e2i[eidx] = ilit; - internal->i2e.push_back (eidx); - CADICAL_assert (internal->i2e[ilit] == eidx); - CADICAL_assert (e2i[eidx] == ilit); - if (elit < 0) - ilit = -ilit; - } - if (internal->opts.checkfrozen) { - CADICAL_assert (eidx < (int64_t) moltentab.size ()); - if (moltentab[eidx]) - FATAL ("can not reuse molten literal %d", eidx); - } - Flags &f = internal->flags (ilit); - if (f.status == Flags::UNUSED) - internal->mark_active (ilit); - else if (f.status != Flags::ACTIVE && f.status != Flags::FIXED) - internal->reactivate (ilit); - if (!marked (tainted, elit) && marked (witness, -elit)) { - CADICAL_assert (!internal->opts.checkfrozen); - LOG ("marking tainted %d", elit); - mark (tainted, elit); - } - } else - ilit = 0; - return ilit; -} - -void External::add (int elit) { - CADICAL_assert (elit != INT_MIN); - reset_extended (); - - bool forgettable = false; - - if (internal->opts.check && - (internal->opts.checkwitness || internal->opts.checkfailed)) { - - forgettable = - internal->from_propagator && internal->ext_clause_forgettable; - - // Forgettable clauses (coming from the external propagator) are not - // saved into the external 'original' stack. They are stored separately - // in external 'forgettable_original', from where they are deleted when - // the corresponding clause is deleted (actually deleted, not just - // marked as garbage). - if (!forgettable) - original.push_back (elit); - } - - const int ilit = internalize (elit); - CADICAL_assert (!elit == !ilit); - - // The external literals of the new clause must be saved for later - // when the proof is printed during add_original_lit (0) - if (elit && (internal->proof || forgettable)) { - eclause.push_back (elit); - if (internal->lrat) { - // actually find unit of -elit (flips elit < 0) - 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); - } - } - } - - if (!elit && internal->proof && internal->lrat) { - for (const auto &elit : eclause) { - ext_flags[abs (elit)] = false; - } - } - - if (elit) - LOG ("adding external %d as internal %d", elit, ilit); - internal->add_original_lit (ilit); - - // Clean-up saved external literals once proof line is printed - if (!elit && (internal->proof || forgettable)) - eclause.clear (); -} - -void External::assume (int elit) { - CADICAL_assert (elit); - reset_extended (); - if (internal->proof) - internal->proof->add_assumption (elit); - assumptions.push_back (elit); - const int ilit = internalize (elit); - CADICAL_assert (ilit); - LOG ("assuming external %d as internal %d", elit, ilit); - internal->assume (ilit); -} - -bool External::flip (int elit) { - CADICAL_assert (elit); - CADICAL_assert (elit != INT_MIN); - CADICAL_assert (!propagator); - - int eidx = abs (elit); - if (eidx > max_var) - return false; - if (marked (witness, elit)) - return false; - int ilit = e2i[eidx]; - if (!ilit) - return false; - bool res = internal->flip (ilit); - if (res && extended) - reset_extended (); - return res; -} - -bool External::flippable (int elit) { - CADICAL_assert (elit); - CADICAL_assert (elit != INT_MIN); - CADICAL_assert (!propagator); - - int eidx = abs (elit); - if (eidx > max_var) - return false; - if (marked (witness, elit)) - return false; - int ilit = e2i[eidx]; - if (!ilit) - return false; - return internal->flippable (ilit); -} - -bool External::failed (int elit) { - CADICAL_assert (elit); - CADICAL_assert (elit != INT_MIN); - int eidx = abs (elit); - if (eidx > max_var) - return 0; - int ilit = e2i[eidx]; - if (!ilit) - return 0; - if (elit < 0) - ilit = -ilit; - return internal->failed (ilit); -} - -void External::constrain (int elit) { - if (constraint.size () && !constraint.back ()) { - LOG (constraint, "replacing previous constraint"); - reset_constraint (); - } - CADICAL_assert (elit != INT_MIN); - reset_extended (); - const int ilit = internalize (elit); - CADICAL_assert (!elit == !ilit); - if (elit) - LOG ("adding external %d as internal %d to constraint", elit, ilit); - else if (!elit && internal->proof) { - internal->proof->add_constraint (constraint); - } - constraint.push_back (elit); - internal->constrain (ilit); -} - -bool External::failed_constraint () { - return internal->failed_constraint (); -} - -void External::phase (int elit) { - CADICAL_assert (elit); - CADICAL_assert (elit != INT_MIN); - const int ilit = internalize (elit); - internal->phase (ilit); -} - -void External::unphase (int elit) { - CADICAL_assert (elit); - CADICAL_assert (elit != INT_MIN); - int eidx = abs (elit); - if (eidx > max_var) { - UNUSED: - LOG ("resetting forced phase of unused external %d ignored", elit); - return; - } - int ilit = e2i[eidx]; - if (!ilit) - goto UNUSED; - if (elit < 0) - ilit = -ilit; - internal->unphase (ilit); -} - -/*------------------------------------------------------------------------*/ - -// External propagation related functions -// -// Note that when an already assigned variable is added as observed, the -// solver will backtrack to undo this assignment. -// -void External::add_observed_var (int elit) { - if (!propagator) { - LOG ("No connected propagator that could observe the variable, " - "observed flag is not set."); - return; - } - - CADICAL_assert (elit); - CADICAL_assert (elit != INT_MIN); - reset_extended (); // tainting! - - int eidx = abs (elit); - if (eidx <= max_var && - (marked (witness, elit) || marked (witness, -elit))) { - LOG ("Error, only clean variables are allowed to become observed."); - CADICAL_assert (false); - - // TODO: here needs to come the taint and restore of the newly - // observed variable. Restore_clauses must be called before continue. - // LOG ("marking tainted %d", elit); - // mark (tainted, elit); - // mark (tainted, -elit); - // restore_clauses ... - } - - if (eidx >= (int64_t) is_observed.size ()) - is_observed.resize (1 + (size_t) eidx, false); - - if (is_observed[eidx]) - return; - - LOG ("marking %d as externally watched", eidx); - - // Will do the necessary internalization - freeze (elit); - is_observed[eidx] = true; - - int ilit = internalize (elit); - // internal add-observed-var backtracks to a lower decision level to - // unassign the variable in case it was already assigned previously (but - // not on the current level) - internal->add_observed_var (ilit); - - if (propagator->is_lazy) - return; - - // In case this variable was already assigned (e.g. via unit clause) and - // got compacted to map to another (not observed) variable, it can not be - // unnasigned so it must be notified explicitly now. (-> Can lead to - // repeated fixed assignment notifications, in case it was unobserved and - // observed again. But a repeated notification is less error-prone than - // never notifying an assignment.) - const int tmp = fixed (elit); - if (!tmp) - return; - int unit = tmp < 0 ? -elit : elit; - - LOG ("notify propagator about fixed assignment upon observe for %d", - unit); - - // internal add-observed-var had to backtrack to root-level already - CADICAL_assert (!internal->level); - - std::vector assigned = {unit}; - propagator->notify_assignment (assigned); -} - -void External::remove_observed_var (int elit) { - if (!propagator) { - LOG ("No connected propagator that could have watched the variable"); - return; - } - int eidx = abs (elit); - - if (eidx > max_var) - return; - - if (is_observed[eidx]) { - // Follow opposite order of add_observed_var, first remove internal - // is_observed - int ilit = e2i[eidx]; // internalize (elit); - internal->remove_observed_var (ilit); - - is_observed[eidx] = false; - melt (elit); - LOG ("unmarking %d as externally watched", eidx); - } -} - -void External::reset_observed_vars () { - // Shouldn't be called if there is no connected propagator - CADICAL_assert (propagator); - reset_extended (); - - internal->notified = 0; - LOG ("reset notified counter to 0"); - - if (!is_observed.size ()) - return; - - CADICAL_assert (!max_var || (size_t) max_var + 1 == is_observed.size ()); - - for (auto elit : vars) { - int eidx = abs (elit); - CADICAL_assert (eidx <= max_var); - if (is_observed[eidx]) { - int ilit = internalize (elit); - internal->remove_observed_var (ilit); - LOG ("unmarking %d as externally watched", eidx); - is_observed[eidx] = false; - melt (elit); - } - } -} - -bool External::observed (int elit) { - CADICAL_assert (elit); - CADICAL_assert (elit != INT_MIN); - int eidx = abs (elit); - if (eidx > max_var) - return false; - if (eidx >= (int) is_observed.size ()) - return false; - - return is_observed[eidx]; -} - -bool External::is_witness (int elit) { - CADICAL_assert (elit); - CADICAL_assert (elit != INT_MIN); - int eidx = abs (elit); - if (eidx > max_var) - return false; - return (marked (witness, elit) || marked (witness, -elit)); -} - -bool External::is_decision (int elit) { - CADICAL_assert (elit); - CADICAL_assert (elit != INT_MIN); - int eidx = abs (elit); - if (eidx > max_var) - return false; - - int ilit = internalize (elit); - return internal->is_decision (ilit); -} - -void External::force_backtrack (size_t new_level) { - if (!propagator) { - LOG ("No connected propagator that could force backtracking"); - return; - } - LOG ("force backtrack to level %zd", new_level); - internal->force_backtrack (new_level); -} - -/*------------------------------------------------------------------------*/ - -int External::propagate_assumptions () { - int res = internal->propagate_assumptions (); - if (res == 10 && !extended) - extend (); // Call solution reconstruction - check_solve_result (res); - reset_limits (); - return res; -} - -void External::implied (std::vector &trailed) { - std::vector ilit_implicants; - internal->implied (ilit_implicants); - - // Those implied literals must be filtered out that are witnesses - // on the reconstruction stack -> no inplace externalize is possible. - // (Internal does not see these marks, so no earlier filter is - // possible.) - - trailed.clear(); - - for (const auto &ilit : ilit_implicants) { - CADICAL_assert (ilit); - const int elit = internal->externalize (ilit); - const int eidx = abs (elit); - const bool is_extension_var = ervars[eidx]; - if (!marked (tainted, elit) && !is_extension_var) { - trailed.push_back (elit); - } - } -} - -void External::conclude_unknown () { - if (!internal->proof || concluded) - return; - concluded = true; - - vector trail; - implied (trail); - internal->proof->conclude_unknown (trail); -} - -/*------------------------------------------------------------------------*/ - -// Internal checker if 'solve' claims the formula to be satisfiable. - -void External::check_satisfiable () { - LOG ("checking satisfiable"); - if (!extended) - extend (); - if (internal->opts.checkwitness) - check_assignment (&External::ival); - if (internal->opts.checkassumptions && !assumptions.empty ()) - check_assumptions_satisfied (); - if (internal->opts.checkconstraint && !constraint.empty ()) - check_constraint_satisfied (); -} - -// Internal checker if 'solve' claims formula to be unsatisfiable. - -void External::check_unsatisfiable () { - LOG ("checking unsatisfiable"); - if (!internal->opts.checkfailed) - return; - if (!assumptions.empty () || !constraint.empty ()) - check_failing (); -} - -// Check result of 'solve' to be correct. - -void External::check_solve_result (int res) { - if (!internal->opts.check) - return; - if (res == 10) - check_satisfiable (); - if (res == 20) - check_unsatisfiable (); -} - -// Prepare checking that completely molten literals are not used as argument -// of 'add' or 'assume', which is invalid under freezing semantics. This -// case would be caught by our 'restore' implementation so is only needed -// for checking the deprecated 'freeze' semantics. - -void External::update_molten_literals () { - if (!internal->opts.checkfrozen) - return; - CADICAL_assert ((size_t) max_var + 1 == moltentab.size ()); -#ifdef LOGGING - int registered = 0, molten = 0; -#endif - for (auto lit : vars) { - if (moltentab[lit]) { - LOG ("skipping already molten literal %d", lit); -#ifdef LOGGING - molten++; -#endif - } else if (frozen (lit)) - LOG ("skipping currently frozen literal %d", lit); - else { - LOG ("new molten literal %d", lit); - moltentab[lit] = true; -#ifdef LOGGING - registered++; - molten++; -#endif - } - } - LOG ("registered %d new molten literals", registered); - LOG ("reached in total %d molten literals", molten); -} - -int External::solve (bool preprocess_only) { - reset_extended (); - update_molten_literals (); - int res = internal->solve (preprocess_only); - check_solve_result (res); - reset_limits (); - return res; -} - -void External::terminate () { internal->terminate (); } - -int External::lookahead () { - reset_extended (); - update_molten_literals (); - int ilit = internal->lookahead (); - const int elit = - (ilit && ilit != INT_MIN) ? internal->externalize (ilit) : 0; - LOG ("lookahead internal %d external %d", ilit, elit); - return elit; -} - -CaDiCaL::CubesWithStatus External::generate_cubes (int depth, - int min_depth = 0) { - reset_extended (); - update_molten_literals (); - reset_limits (); - auto cubes = internal->generate_cubes (depth, min_depth); - auto externalize = [this] (int ilit) { - const int elit = ilit ? internal->externalize (ilit) : 0; - MSG ("lookahead internal %d external %d", ilit, elit); - return elit; - }; - auto externalize_map = [this, externalize] (std::vector cube) { - (void) this; - MSG ("Cube : "); - std::for_each (begin (cube), end (cube), externalize); - }; - std::for_each (begin (cubes.cubes), end (cubes.cubes), externalize_map); - - return cubes; -} - -/*------------------------------------------------------------------------*/ - -void External::freeze (int elit) { - reset_extended (); - int ilit = internalize (elit); - unsigned eidx = vidx (elit); - if (eidx >= frozentab.size ()) - frozentab.resize (eidx + 1, 0); - unsigned &ref = frozentab[eidx]; - if (ref < UINT_MAX) { - ref++; - LOG ("external variable %d frozen once and now frozen %u times", eidx, - ref); - } else - LOG ("external variable %d frozen but remains frozen forever", eidx); - internal->freeze (ilit); -} - -void External::melt (int elit) { - reset_extended (); - int ilit = internalize (elit); - unsigned eidx = vidx (elit); - CADICAL_assert (eidx < frozentab.size ()); - unsigned &ref = frozentab[eidx]; - CADICAL_assert (ref > 0); - if (ref < UINT_MAX) { - if (!--ref) { - if (observed (elit)) { - ref++; - LOG ("external variable %d is observed, can not be completely " - "molten", - eidx); - } else - LOG ("external variable %d melted once and now completely melted", - eidx); - } else - LOG ("external variable %d melted once but remains frozen %u times", - eidx, ref); - } else - LOG ("external variable %d melted but remains frozen forever", eidx); - internal->melt (ilit); -} - -/*------------------------------------------------------------------------*/ - -void External::check_assignment (int (External::*a) (int) const) { - - // First check all assigned and consistent. - // - for (auto idx : vars) { - if (!(this->*a) (idx)) - FATAL ("unassigned variable: %d", idx); - int value_idx = (this->*a) (idx); - int value_neg_idx = (this->*a) (-idx); - if (value_idx == idx) - CADICAL_assert (value_neg_idx == idx); - else { - CADICAL_assert (value_idx == -idx); - CADICAL_assert (value_neg_idx == -idx); - } - if (value_idx != value_neg_idx) - FATAL ("inconsistently assigned literals %d and %d", idx, -idx); - } - - // Then check that all (saved) original clauses are satisfied. - // - bool satisfied = false; - const auto end = original.end (); - auto start = original.begin (), i = start; -#ifndef CADICAL_QUIET - int64_t count = 0; -#endif - for (; i != end; i++) { - int lit = *i; - if (!lit) { - if (!satisfied) { - fatal_message_start (); - fputs ("unsatisfied clause:\n", stderr); - for (auto j = start; j != i; j++) - fprintf (stderr, "%d ", *j); - fputc ('0', stderr); - fatal_message_end (); - } - satisfied = false; - start = i + 1; -#ifndef CADICAL_QUIET - count++; -#endif - } else if (!satisfied && (this->*a) (lit) == lit) - satisfied = true; - } - - bool presence_flag; - // Check those forgettable external clauses that are still present, but - // only if the external propagator is still connected (otherwise solution - // reconstruction is allowed to touch the previously observed variables so - // there is no guarantee that the final model will satisfy these clauses.) - for (const auto &forgettables : forgettable_original) { - if (!propagator) - break; - presence_flag = true; - satisfied = false; -#ifndef CADICAL_QUIET - count++; -#endif - std::vector literals; - for (const auto lit : forgettables.second) { - if (presence_flag) { - // First integer is a Boolean flag, not a literal - if (!lit) { - // Deleted clauses can be ignored, they count as satisfied - satisfied = true; - break; - } - presence_flag = false; - continue; - } - - if ((this->*a) (lit) == lit) { - satisfied = true; - break; - } - } - - if (!satisfied) { - fatal_message_start (); - fputs ("unsatisfied external forgettable clause:\n", stderr); - for (size_t j = 1; j < forgettables.second.size (); j++) - fprintf (stderr, "%d ", forgettables.second[j]); - fputc ('0', stderr); - fatal_message_end (); - } - } -#ifndef CADICAL_QUIET - VERBOSE (1, "satisfying assignment checked on %" PRId64 " clauses", - count); -#endif -} - -/*------------------------------------------------------------------------*/ - -void External::check_assumptions_satisfied () { - for (const auto &lit : assumptions) { - // Not 'signed char' !!!! - const int tmp = ival (lit); - if (tmp != lit) - FATAL ("assumption %d falsified", lit); - if (!tmp) - FATAL ("assumption %d unassigned", lit); - } - VERBOSE (1, "checked that %zd assumptions are satisfied", - assumptions.size ()); -} - -void External::check_constraint_satisfied () { - for (const auto lit : constraint) { - if (ival (lit) == lit) { - VERBOSE (1, "checked that constraint is satisfied"); - return; - } - } - FATAL ("constraint not satisfied"); -} - -void External::check_failing () { - Solver *checker = new Solver (); - DeferDeletePtr delete_checker (checker); - checker->prefix ("checker "); -#ifdef LOGGING - if (internal->opts.log) - checker->set ("log", true); -#endif - - for (const auto lit : assumptions) { - if (!failed (lit)) - continue; - LOG ("checking failed literal %d in core", lit); - checker->add (lit); - checker->add (0); - } - if (failed_constraint ()) { - LOG (constraint, "checking failed constraint"); - for (const auto lit : constraint) - checker->add (lit); - } else if (constraint.size ()) - LOG (constraint, "constraint satisfied and ignored"); - - // Add original clauses as last step, failing () and failed_constraint () - // might add more external clauses (due to lazy explanation) - for (const auto lit : original) - checker->add (lit); - - // Add every forgettable external clauses - for (const auto &forgettables : forgettable_original) { - bool presence_flag = true; - for (const auto lit : forgettables.second) { - if (presence_flag) { - // First integer is a Boolean flag, not a literal, ignore it here - presence_flag = false; - continue; - } - checker->add (lit); - } - checker->add (0); - } - - int res = checker->solve (); - if (res != 20) - FATAL ("failed assumptions do not form a core"); - delete_checker.free (); - VERBOSE (1, "checked that %zd failing assumptions form a core", - assumptions.size ()); -} - -/*------------------------------------------------------------------------*/ - -// Traversal of unit clauses is implemented here. - -// In principle we want to traverse the clauses of the simplified formula -// only, particularly eliminated variables should be completely removed. -// This poses the question what to do with unit clauses. Should they be -// considered part of the simplified formula or of the witness to construct -// solutions for the original formula? Ideally they should be considered -// to be part of the witness only, i.e., as they have been simplified away. - -// Therefore we distinguish frozen and non-frozen units during clause -// traversal. Frozen units are treated as unit clauses while non-frozen -// units are treated as if they were already eliminated and put on the -// extension stack as witness clauses. - -// Furthermore, eliminating units during 'compact' could be interpreted as -// variable elimination, i.e., just add the resolvents (remove falsified -// literals), then drop the clauses with the unit, and push the unit on the -// extension stack. This is of course only OK if the user did not freeze -// that variable (even implicitly during assumptions). - -// Thanks go to Fahiem Bacchus for asking why there is a necessity to -// distinguish these two cases (frozen and non-frozen units). The answer is -// that it is not strictly necessary, and this distinction could be avoided -// by always treating units as remaining unit clauses, thus only using the -// first of the two following functions and dropping the 'if (!frozen (idx)) -// continue;' check in it. This has however the down-side that those units -// are still in the simplified formula and only as units. I would not -// consider such a formula as really being 'simplified'. On the other hand -// if the user explicitly freezes a literal, then it should continue to be -// in the simplified formula during traversal. So also only using the -// second function is not ideal. - -// There is however a catch where this solution breaks down (in the sense of -// producing less optimal results - that is keeping units in the formula -// which better would be witness clauses). The problem is with compact -// preprocessing which removes eliminated but also fixed internal variables. -// One internal unit (fixed) variable is kept and all the other external -// literals which became unit are mapped to that internal literal (negated -// or positively). Compact is called non-deterministically from the point -// of the user and thus there is no control on when this happens. If -// compact happens those external units are mapped to a single internal -// literal now and then all share the same 'frozen' counter. So if the -// user freezes one of them all in essence get frozen, which in turn then -// makes a difference in terms of traversing such a unit as unit clause or -// as unit witness. - -bool External::traverse_all_frozen_units_as_clauses (ClauseIterator &it) { - if (internal->unsat) - return true; - - vector clause; - - for (auto idx : vars) { - if (!frozen (idx)) - continue; - const int tmp = fixed (idx); - if (!tmp) - continue; - int unit = tmp < 0 ? -idx : idx; - clause.push_back (unit); - if (!it.clause (clause)) - return false; - clause.clear (); - } - - return true; -} - -bool External::traverse_all_non_frozen_units_as_witnesses ( - WitnessIterator &it) { - if (internal->unsat) - return true; - - vector clause_and_witness; - for (auto idx : vars) { - if (frozen (idx)) - continue; - const int tmp = fixed (idx); - if (!tmp) - continue; - int unit = tmp < 0 ? -idx : idx; - const int ilit = e2i[idx] * (tmp < 0 ? -1 : 1); - // heurstically add + max_var to the id to avoid reusing ids - const int64_t id = internal->lrat ? internal->unit_id (ilit) : 1; - CADICAL_assert (id); - clause_and_witness.push_back (unit); - if (!it.witness (clause_and_witness, clause_and_witness, id + max_var)) - return false; - clause_and_witness.clear (); - } - - return true; -} - -/*------------------------------------------------------------------------*/ - -void External::copy_flags (External &other) const { - const vector &this_ftab = internal->ftab; - vector &other_ftab = other.internal->ftab; - const unsigned limit = min (max_var, other.max_var); - for (unsigned eidx = 1; eidx <= limit; eidx++) { - const int this_ilit = e2i[eidx]; - if (!this_ilit) - continue; - const int other_ilit = other.e2i[eidx]; - if (!other_ilit) - continue; - if (!internal->active (this_ilit)) - continue; - if (!other.internal->active (other_ilit)) - continue; - CADICAL_assert (this_ilit != INT_MIN); - CADICAL_assert (other_ilit != INT_MIN); - const Flags &this_flags = this_ftab[abs (this_ilit)]; - Flags &other_flags = other_ftab[abs (other_ilit)]; - this_flags.copy (other_flags); - } -} - -/*------------------------------------------------------------------------*/ - -void External::export_learned_empty_clause () { - CADICAL_assert (learner); - if (learner->learning (0)) { - LOG ("exporting learned empty clause"); - learner->learn (0); - } else - LOG ("not exporting learned empty clause"); -} - -void External::export_learned_unit_clause (int ilit) { - CADICAL_assert (learner); - if (learner->learning (1)) { - LOG ("exporting learned unit clause"); - const int elit = internal->externalize (ilit); - CADICAL_assert (elit); - learner->learn (elit); - learner->learn (0); - } else - LOG ("not exporting learned unit clause"); -} - -void External::export_learned_large_clause (const vector &clause) { - CADICAL_assert (learner); - size_t size = clause.size (); - CADICAL_assert (size <= (unsigned) INT_MAX); - if (learner->learning ((int) size)) { - LOG ("exporting learned clause of size %zu", size); - for (auto ilit : clause) { - const int elit = internal->externalize (ilit); - CADICAL_assert (elit); - learner->learn (elit); - } - learner->learn (0); - } else - LOG ("not exporting learned clause of size %zu", size); -} - -} // namespace CaDiCaL - -ABC_NAMESPACE_IMPL_END diff --git a/src/sat/cadical/cadical_external_propagate.cpp b/src/sat/cadical/cadical_external_propagate.cpp deleted file mode 100644 index 215170c8b1..0000000000 --- a/src/sat/cadical/cadical_external_propagate.cpp +++ /dev/null @@ -1,1232 +0,0 @@ -#include "global.h" - -#include "internal.hpp" - -ABC_NAMESPACE_IMPL_START - -namespace CaDiCaL { - -/*----------------------------------------------------------------------------*/ -// -// Mark a variable as an observed one. It can be a new variable. It is -// assumed to be clean (not eliminated by previous simplifications). -// -void Internal::add_observed_var (int ilit) { - int idx = vidx (ilit); - if (idx >= (int64_t) relevanttab.size ()) - relevanttab.resize (1 + (size_t) idx, 0); - unsigned &ref = relevanttab[idx]; - if (ref < UINT_MAX) { - ref++; - LOG ("variable %d is observed %u times", idx, ref); - } else - LOG ("variable %d remains observed forever", idx); - // TODO: instead of actually backtracking, it would be enough to notify - // backtrack and re-play again every levels' notification to the - // propagator - if (val (ilit) && level && !fixed (ilit)) { - // The variable is already assigned, but we can not send a notification - // about it because it happened on an earlier decision level. - // To not break the stack-like view of the trail, we simply backtrack to - // undo this unnotifiable assignment. - const int assignment_level = var (ilit).level; - backtrack (assignment_level - 1); - } else if (level && fixed (ilit)) { - backtrack (0); - } -} - -/*----------------------------------------------------------------------------*/ -// -// Removing an observed variable should happen only once it is ensured -// that there is no unexplained propagation in the implication -// graph involving this variable. -// -void Internal::remove_observed_var (int ilit) { - if (!fixed (ilit) && level) { - backtrack (); - } - - CADICAL_assert (fixed (ilit) || !level); - - const int idx = vidx (ilit); - CADICAL_assert ((size_t) idx < relevanttab.size ()); - unsigned &ref = relevanttab[idx]; - CADICAL_assert (fixed (ilit) || ref > 0); - if (fixed (ilit)) - ref = 0; - else if (ref < UINT_MAX) { - if (!--ref) { - LOG ("variable %d is not observed anymore", idx); - } else - LOG ("variable %d is unobserved once but remains observed %u times", - ilit, ref); - } else - LOG ("variable %d remains observed forever", idx); -} - -/*----------------------------------------------------------------------------*/ -// -// Supposed to be used only by mobical. -// -bool Internal::observed (int ilit) const { - CADICAL_assert ((size_t) vidx (ilit) < relevanttab.size ()); - return relevanttab[vidx (ilit)] > 0; -} - -/*----------------------------------------------------------------------------*/ -// -// Check for unexplained propagations upon disconnecting external propagator -// -void Internal::set_tainted_literal () { - if (!opts.ilb) { - return; - } - for (auto idx : vars) { - if (!val (idx)) - continue; - if (var (idx).reason != external_reason) - continue; - if (!tainted_literal) { - tainted_literal = idx; - continue; - } - CADICAL_assert (val (tainted_literal)); - if (var (idx).level < var (tainted_literal).level) { - tainted_literal = idx; - } - } -} - -void Internal::renotify_trail_after_ilb () { - if (!external_prop || external_prop_is_lazy || !trail.size () || - !opts.ilb) { - return; - } - LOG ("notify external propagator about new assignments (after ilb)"); -#ifndef CADICAL_NDEBUG - LOG ("(decision level: %d, trail size: %zd, notified %zd)", level, - trail.size (), notified); -#endif - renotify_full_trail (); -} - -void Internal::renotify_trail_after_local_search () { - if (!external_prop || external_prop_is_lazy || !trail.size ()) { - return; - } - LOG ("notify external propagator about new assignments (after local " - "search)"); -#ifndef CADICAL_NDEBUG - LOG ("(decision level: %d, trail size: %zd, notified %zd)", level, - trail.size (), notified); -#endif - renotify_full_trail (); -} - -// It repeats ALL assignments of the trail, so the already notified -// root-level assignments will be notified multiple times. - -void Internal::renotify_full_trail () { - const size_t end_of_trail = trail.size (); - if (level) { - notified = 0; // TODO: save the last notified root-level position - // somewhere and use it here - notify_backtrack (0); - } - std::vector assigned; - - int prev_max_level = 0; - int current_level = 0; - int propagator_level = 0; - - while (notified < end_of_trail) { - int ilit = trail[notified++]; - // In theory, 0 ilit can happen due to pseudo-decision levels - if (!ilit) - current_level = prev_max_level + 1; - else - current_level = var (ilit).level; - - if (current_level > propagator_level) { - if (assigned.size ()) - external->propagator->notify_assignment (assigned); - while (current_level > propagator_level) { - external->propagator->notify_new_decision_level (); - propagator_level++; - } - assigned.clear (); - } - // Current level can be smaller than prev_max_level due to chrono - if (current_level > prev_max_level) - prev_max_level = current_level; - - if (!observed (ilit)) - continue; - - int elit = externalize (ilit); // TODO: double-check tainting - CADICAL_assert (elit); - // Fixed variables might get mapped (during compact) to another - // non-observed but fixed variable. - // This happens on root level, so notification about their assignment is - // already done. - CADICAL_assert (external->observed (elit) || fixed (ilit)); - if (!external->ervars[abs (elit)]) - assigned.push_back (elit); - } - if (assigned.size ()) - external->propagator->notify_assignment (assigned); - assigned.clear (); - - // In case there are some left over empty levels on the top of the trail, - // the external propagtor must be notified about them so the levels are - // synced - while (level > propagator_level) { - external->propagator->notify_new_decision_level (); - propagator_level++; - } - - return; -} - -/*----------------------------------------------------------------------------*/ -// -// Check if the variable is assigned by decision. -// -bool Internal::is_decision (int ilit) { - if (!level || fixed (ilit) || !val (ilit)) - return false; - - const int idx = vidx (ilit); - Var &v = var (idx); -#ifndef CADICAL_NDEBUG - LOG (v.reason, - "is_decision: i%d (current level: %d, is_fixed: %d, v.level: %d, " - "is_external_reason: %d, v.reason: )", - ilit, level, fixed (ilit), v.level, v.reason == external_reason); -#endif - if (!v.level || v.reason) - return false; - CADICAL_assert (!v.reason); - return true; -} - -void Internal::force_backtrack (size_t new_level) { - if (!forced_backt_allowed || level <= 0 || new_level >= (size_t) level) - return; - -#ifndef CADICAL_NDEBUG - LOG ("external propagator forces backtrack to decision level" - "%zd (from level %d)", - new_level, level); -#endif - backtrack (new_level); -} - -/*----------------------------------------------------------------------------*/ -// -// Call external propagator to check if there is a literal to be propagated. -// The reason of the propagation is not necessarily asked at that point. -// -// In case the externally propagated literal is already falsified, the -// reason is asked and conflict analysis starts. In case the externally -// propagated literal is already satisfied, nothing happens. -// -bool Internal::external_propagate () { - if (level) - require_mode (SEARCH); - CADICAL_assert (!unsat); - - size_t before = num_assigned; - bool cb_repropagate_needed = true; - while (cb_repropagate_needed && !conflict && external_prop && - !external_prop_is_lazy && !private_steps) { -#ifndef CADICAL_NDEBUG - LOG ("external propagation starts (decision level: %d, trail size: " - "%zd, notified %zd)", - level, trail.size (), notified); -#endif - cb_repropagate_needed = false; - // external->reset_extended (); //TODO for inprocessing - - notify_assignments (); - - int elit = external->propagator->cb_propagate (); - stats.ext_prop.ext_cb++; - stats.ext_prop.eprop_call++; - while (elit) { - CADICAL_assert (external->is_observed[abs (elit)]); - int ilit = external->e2i[abs (elit)]; - if (elit < 0) - ilit = -ilit; - int tmp = val (ilit); -#ifndef CADICAL_NDEBUG - CADICAL_assert (fixed (ilit) || observed (ilit)); - LOG ("External propagation of e%d (i%d val: %d)", elit, ilit, tmp); -#endif - if (!tmp) { - // variable is not assigned, it can be propagated - if (!level) { - Clause *res = learn_external_reason_clause (ilit, elit); -#ifndef LOGGING - LOG (res, "reason clause of external propagation of %d:", elit); -#endif - (void) res; - } else - search_assign_external (ilit); - stats.ext_prop.eprop_prop++; - - if (unsat || conflict) - break; - propagate (); - if (unsat || conflict) - break; - notify_assignments (); - } else if (tmp < 0) { - LOG ("External propgation of %d is falsified under current trail", - ilit); - stats.ext_prop.eprop_conf++; - int level_before = level; - size_t assigned = num_assigned; - Clause *res = learn_external_reason_clause (ilit, elit); -#ifndef LOGGING - LOG (res, "reason clause of external propagation of %d:", elit); -#endif - (void) res; - bool trail_changed = - (num_assigned != assigned || level != level_before || - propagated < trail.size ()); - - if (unsat || conflict) - break; - - if (trail_changed) { - propagate (); - if (unsat || conflict) - break; - notify_assignments (); - } - } // else (tmp > 0) -> the case of a satisfied literal is ignored - elit = external->propagator->cb_propagate (); - stats.ext_prop.ext_cb++; - stats.ext_prop.eprop_call++; - } - -#ifndef CADICAL_NDEBUG - LOG ("External propagation ends (decision level: %d, trail size: %zd, " - "notified %zd)", - level, trail.size (), notified); -#endif - if (!unsat && !conflict) { - int level_before = level; - size_t assigned = num_assigned; - bool has_external_clause = ask_external_clause (); - // New observed variable might have triggered a backtrack during this - // ask_external_clause call, so we need to propagate before continuing - stats.ext_prop.ext_cb++; - stats.ext_prop.elearn_call++; - - bool trail_changed = - (num_assigned != assigned || level != level_before || - propagated < trail.size ()); - if (trail_changed) { - propagate (); // unsat or conflict will be caught later - if (!unsat || !conflict) - notify_assignments (); - } - -#ifndef CADICAL_NDEBUG - if (has_external_clause) - LOG ("New external clauses are to be added."); - else - LOG ("No external clauses to be added."); -#endif - - while (has_external_clause) { - level_before = level; - assigned = num_assigned; - - add_external_clause (0); - trail_changed = - (num_assigned != assigned || level != level_before || - propagated < trail.size ()); - cb_repropagate_needed = true; - - if (unsat || conflict) { - cb_repropagate_needed = false; - break; - } - - if (trail_changed) { - propagate (); - if (unsat || conflict) { - cb_repropagate_needed = false; - break; - } - - notify_assignments (); - } - has_external_clause = ask_external_clause (); - stats.ext_prop.ext_cb++; - stats.ext_prop.elearn_call++; - } - } -#ifndef CADICAL_NDEBUG - LOG ("External clause addition ends on decision level %d at trail " - "size " - "%zd (notified %zd)", - level, trail.size (), notified); -#endif - } - if (before < num_assigned) - did_external_prop = true; - return !conflict; -} - -/*----------------------------------------------------------------------------*/ -// -// Helper function, calls 'cb_has_external_clause', while maintains the -// related redundancy type of the clause. -// - -bool Internal::ask_external_clause () { - ext_clause_forgettable = false; - bool res = - external->propagator->cb_has_external_clause (ext_clause_forgettable); - - return res; -} -/*----------------------------------------------------------------------------*/ -// -// Literals of the externally learned clause must be reordered based on the -// assignment levels of the literals. -// -void Internal::move_literals_to_watch () { - if (clause.size () < 2) - return; - if (!level) - return; - - for (int i = 0; i < 2; i++) { - int highest_position = i; - int highest_literal = clause[i]; - - int highest_level = var (highest_literal).level; - int highest_value = val (highest_literal); - - for (size_t j = i + 1; j < clause.size (); j++) { - const int other = clause[j]; - const int other_level = var (other).level; - const int other_value = val (other); - - if (other_value < 0) { - if (highest_value >= 0) - continue; - if (other_level <= highest_level) - continue; - } else if (other_value > 0) { - if (highest_value > 0 && other_level >= highest_level) - continue; - } else { - if (highest_value >= 0) - continue; - } - - highest_position = j; - highest_literal = other; - highest_level = other_level; - highest_value = other_value; - } -#ifndef CADICAL_NDEBUG - LOG ("highest position: %d highest level: %d highest value: %d", - highest_position, highest_level, highest_value); -#endif - - if (highest_position == i) - continue; - if (highest_position > i) { - std::swap (clause[i], clause[highest_position]); - } - } -} - -/*----------------------------------------------------------------------------*/ -// -// Reads out from the external propagator the lemma/proapgation reason -// clause literal by literal. In case propagated_elit is 0, it is about an -// external clause via 'cb_add_external_clause_lit'. Otherwise, it is about -// learning the reason of 'propagated_elit' via 'cb_add_reason_clause_lit'. -// The learned clause is simplified by the current root-level assignment -// (i.e. root-level falsified literals are removed, root satisfied clauses -// are skipped). Duplicate literals are removed, tauotologies are detected -// and skipped. It always adds the original (un-simplified) external clause -// to the proof as an input clause and -// the simplified version of it (except exceptions below) as a derived -// clause. -// -// In case the external clause, after simplifications, is satisfied, no -// clause is constructed, and the function returns 0. In case the external -// clause, after simplifications, is empty, no clause is constructed, unsat -// is set true and the function returns 0. In case the external clause, -// after simplifications, is unit, no clause is constructed, -// 'Internal::clause' has the unit literal (without 0) and the function -// returns 0. -// -// In every other cases a new clause is constructed and the pointer is in -// newest_clause -// -void Internal::add_external_clause (int propagated_elit, - bool no_backtrack) { - CADICAL_assert (original.empty ()); - int elit = 0; - - if (propagated_elit) { - // Propagation reason clauses are by default assumed to be forgettable - // irredundant. In case they would be unforgettably important, the - // propagator can add them as an explicit unforgettable external clause - // or set 'are_reasons_forgettable' to false. - ext_clause_forgettable = external->propagator->are_reasons_forgettable; -#ifndef CADICAL_NDEBUG - LOG ("add external reason of propagated lit: %d", propagated_elit); -#endif - elit = external->propagator->cb_add_reason_clause_lit (propagated_elit); - } else - elit = external->propagator->cb_add_external_clause_lit (); - - // we need to be build a new LRAT chain if we are already in the middle of - // the analysis (like during failed assumptions) - LOG (lrat_chain, "lrat chain before"); - std::vector lrat_chain_ext = std::move (lrat_chain); - lrat_chain.clear (); - clause.clear (); - - // Read out the external lemma into original and simplify it into clause - CADICAL_assert (clause.empty ()); - CADICAL_assert (original.empty ()); - - CADICAL_assert (!force_no_backtrack); - CADICAL_assert (!from_propagator); - force_no_backtrack = no_backtrack; - from_propagator = true; - while (elit) { - CADICAL_assert (external->is_observed[abs (elit)]); - external->add (elit); - if (propagated_elit) - elit = - external->propagator->cb_add_reason_clause_lit (propagated_elit); - else - elit = external->propagator->cb_add_external_clause_lit (); - } - external->add (elit); - CADICAL_assert (original.empty ()); - CADICAL_assert (clause.empty ()); - force_no_backtrack = false; - from_propagator = false; - lrat_chain = std::move (lrat_chain_ext); - LOG (lrat_chain, "lrat chain after"); -} - -/*----------------------------------------------------------------------------*/ -// -// Recursively calls 'learn_external_reason_clause' to explain every -// backward reachable externally propagated literal starting from 'ilit'. -// -void Internal::explain_reason (int ilit, Clause *reason, int &open) { - if (!opts.exteagerreasons) - return; -#ifndef CADICAL_NDEBUG - LOG (reason, "explain_reason of %d (open: %d)", ilit, open); -#endif - CADICAL_assert (reason); - CADICAL_assert (reason != external_reason); - for (const auto &other : *reason) { - if (other == ilit) - continue; - Flags &f = flags (other); - if (f.seen) - continue; - Var &v = var (other); - if (!v.level) - continue; - CADICAL_assert (val (other) < 0); - CADICAL_assert (v.level <= level); - if (v.reason == external_reason) { - v.reason = learn_external_reason_clause (-other, 0, true); - } - if (v.level && v.reason) { - f.seen = true; - open++; - } - } -} - -/*----------------------------------------------------------------------------*/ -// -// In case external propagation was used, the reason clauses of the relevant -// propagations must be learned lazily before/during conflict analysis. -// While conflict analysis needs to analyze only the current level, lazy -// clause learning must check every clause on every level that is backward -// reachable from the conflicting clause to guarantee that the assignment -// levels of the variables are accurate. So this explanation round is -// separated from the conflict analysis, thereby guranteeing that the flags -// and datastructures can be properly used later. -// -// This function must be called before the conflict analysis, in order to -// guarantee that every relevant reason clause is indeed learned already and -// to be sure that the levels of assignments are set correctly. -// -// Later TODO: experiment with bounded explanation (explain only those -// literals that are directly used during conflict analysis + -// minimizing/shrinking). The assignment levels are then only -// over-approximated. -// -void Internal::explain_external_propagations () { - CADICAL_assert (conflict); - CADICAL_assert (clause.empty ()); - - Clause *reason = conflict; - std::vector seen_lits; - int open = 0; // Seen but not explained literal - - explain_reason (0, reason, open); // marks conflict clause lits as seen - int i = trail.size (); // Start at end-of-trail - while (i > 0) { - const int lit = trail[--i]; - if (!flags (lit).seen) - continue; - seen_lits.push_back (lit); - Var &v = var (lit); - if (!v.level) - continue; - if (v.reason) { - open--; - explain_reason (lit, v.reason, open); - } - if (!open) - break; - } - CADICAL_assert (!open); - - if (!opts.exteagerrecalc) { - for (auto lit : seen_lits) { - Flags &f = flags (lit); - f.seen = false; - } -#ifndef CADICAL_NDEBUG - for (auto idx : vars) { - CADICAL_assert (!flags (idx).seen); - } -#endif - } - - // Traverse now in the opposite direction (from lower to higher levels) - // and calculate the actual assignment level for the seen assignments. - for (auto it = seen_lits.rbegin (); it != seen_lits.rend (); ++it) { - const int lit = *it; - Flags &f = flags (lit); - Var &v = var (lit); - if (v.reason) { - int real_level = 0; - for (const auto &other : *v.reason) { - if (other == lit) - continue; - int tmp = var (other).level; - if (tmp > real_level) - real_level = tmp; - } - if (v.level && !real_level) { - build_chain_for_units (lit, v.reason, 1); - learn_unit_clause (lit); - lrat_chain.clear (); - v.reason = 0; - } - CADICAL_assert (v.level >= real_level); - if (v.level > real_level) { - v.level = real_level; - } - } - f.seen = false; - } - -#if 0 // has been fuzzed extensively - for (auto idx : vars) { - CADICAL_assert (!flags (idx).seen); - } -#endif -} - -/*----------------------------------------------------------------------------*/ -// -// Learns the reason clause of the propagation of ilit from the -// external propagator via 'add_external_clause'. -// In case of falsified propagation steps, if the propagated literal is -// already fixed to the opposite value, externalize will not necessarily -// give back the original elit (but an equivalent one). To avoid that, in -// falsified propagation cases the propagated elit is added as a second -// argument. -// -Clause *Internal::learn_external_reason_clause (int ilit, - int falsified_elit, - bool no_backtrack) { - CADICAL_assert (external->propagator); - // we cannot modify clause during analysis - auto clause_tmp = std::move (clause); - - CADICAL_assert (clause.empty ()); - CADICAL_assert (original.empty ()); - - stats.ext_prop.eprop_expl++; - - int elit = 0; - if (!falsified_elit) { - CADICAL_assert (!fixed (ilit)); - elit = externalize (ilit); - } else - elit = falsified_elit; - - LOG ("ilit: %d, elit: %d", ilit, elit); - add_external_clause (elit, no_backtrack); - -#ifndef CADICAL_NDEBUG - if (!falsified_elit && newest_clause) { - // Check if external propagation is correct wrt to the topological order - // defined by the trail. In case it is a falsified external propagation - // step, the order does not matter, the reason simply supposed to be a - // falsified clause. - const int propagated_ilit = ilit; - for (auto const reason_ilit : *newest_clause) { - CADICAL_assert (var (reason_ilit).trail <= var (propagated_ilit).trail); - } - } -#endif - - clause = std::move (clause_tmp); - return newest_clause; -} - -/*----------------------------------------------------------------------------*/ -// -// Helper function to be able to call learn_external_reason_clause when the -// internal clause is already used in the caller side (for example during -// proof checking). These calls are assumed to be without a falsified elit. -// Dont use it in general instead of learn_external_reason_clause because it -// does not support the corner cases where a literal remains in clause. -// -Clause *Internal::wrapped_learn_external_reason_clause (int ilit) { - Clause *res; - std::vector chain_tmp{std::move (lrat_chain)}; - lrat_chain.clear (); - if (clause.empty ()) { - res = learn_external_reason_clause (ilit, 0, true); - } else { - std::vector clause_tmp{std::move (clause)}; - clause.clear (); - res = learn_external_reason_clause (ilit, 0, true); - // The learn_external_reason clause can leave a literal in clause when - // there is a falsified elit arg. Here it is not allowed to - // happen. - CADICAL_assert (clause.empty ()); - - clause = std::move (clause_tmp); - clause_tmp.clear (); - } - CADICAL_assert (lrat_chain.empty ()); - lrat_chain = std::move (chain_tmp); - chain_tmp.clear (); - return res; -} - -/*----------------------------------------------------------------------------*/ -// -// Checks if the new clause forces backtracking, new assignments or conflict -// analysis -// -void Internal::handle_external_clause (Clause *res) { - if (from_propagator) - stats.ext_prop.elearned++; - // at level 0 we have to do nothing... - if (!level) - return; - if (!res) { - if (from_propagator) - stats.ext_prop.elearn_prop++; - // new unit clause. For now just backtrack. - CADICAL_assert (!force_no_backtrack); - CADICAL_assert (level); - // if (!opts.chrono) { - backtrack (); - // } - return; - } - if (from_propagator) - stats.ext_prop.elearned++; - CADICAL_assert (res->size >= 2); - const int pos0 = res->literals[0]; - const int pos1 = res->literals[1]; - if (force_no_backtrack) { - CADICAL_assert (val (pos1) < 0); - CADICAL_assert (val (pos0) >= 0); - return; - // TODO: maybe fix levels - } - const int l1 = var (pos1).level; - if (val (pos0) < 0) { // conflicting or propagating clause - CADICAL_assert (0 < l1 && l1 <= var (pos0).level); - if (!opts.chrono) { - backtrack (l1); - } - if (val (pos0) < 0) { - conflict = res; - if (!from_propagator) { - // its better to backtrack instead of analyze - backtrack (l1 - 1); - conflict = 0; - CADICAL_assert (!val (pos0) && !val (pos1)); - } - } else { - search_assign_driving (pos0, res); - } - if (from_propagator) - stats.ext_prop.elearn_conf++; - return; - } - if (val (pos1) < 0 && !val (pos0)) { // propagating clause - if (!opts.chrono) { - backtrack (l1); - } - search_assign_driving (pos0, res); - if (from_propagator) - stats.ext_prop.elearn_conf++; - return; - } -} - -/*----------------------------------------------------------------------------*/ -// -// Asks the external propagator if the current solution is OK -// by calling 'cb_check_found_model (model)'. -// -// The checked model is built up after everything is restored -// from the reconstruction stack and every variable is reactivated -// and so it is not just simply the trail (i.e. it can be expensive). -// -// If the external propagator approves the model, the function -// returns true. -// -// If the propagator does not approve the model, the solver asks -// the propagator to add an external clause. -// This external clause, however, does NOT have to be falsified by -// the current model. The possible cases and reactions are described -// below in the function. The possible states after that function: -// - A solution was found and accepted by the external propagator -// - A conflicting clause was learned from the external propagator -// - The empty clause was learned due to something new learned from -// the external propagator. -// -// In case only new variables were introduced, but no new clauses were -// added, the function will return without a conflict to the outer CDCL -// loop, where the new (not yet satisfied) variables are recognized and -// the search continues. -bool Internal::external_check_solution () { - if (!external_prop) - return true; - - bool trail_changed = true; - bool added_new_clauses = false; - while (trail_changed || added_new_clauses) { - notify_assignments (); - if (!satisfied ()) - break; - trail_changed = false; // to be on the safe side - added_new_clauses = false; - LOG ("Final check by external propagator is invoked."); - stats.ext_prop.echeck_call++; - external->reset_extended (); - external->extend (); - - std::vector etrail; - - // Here the variables must be filtered by external->is_observed, - // because fixed variables are internally not necessarily observed - // anymore. - for (int idx = 1; idx <= external->max_var; idx++) { - if (!external->is_observed[idx]) - continue; - const int lit = external->ival (idx); - etrail.push_back (lit); -#ifndef CADICAL_NDEBUG -#ifdef LOGGING - bool p = external->vals[idx]; - LOG ("evals[%d]: %d ival(%d): %d", idx, p, idx, lit); -#endif -#endif - } - - bool is_consistent = - external->propagator->cb_check_found_model (etrail); - stats.ext_prop.ext_cb++; - if (is_consistent) { - LOG ("Found solution is approved by external propagator."); - return true; - } - - bool has_external_clause = ask_external_clause (); - - stats.ext_prop.ext_cb++; - stats.ext_prop.elearn_call++; - - if (has_external_clause) - LOG ( - "Found solution triggered new clauses from external propagator."); - - while (has_external_clause) { - int level_before = level; - size_t assigned = num_assigned; - add_external_clause (0); - bool trail_changed = - (num_assigned != assigned || level != level_before || - propagated < trail.size ()); - added_new_clauses = true; - // - // There are many possible scenarios here: - // - Learned conflicting clause: return to CDCL loop (conflict true) - // - Learned conflicting unit clause that after backtrack+BCP leads to - // a new complete solution: force the outer loop to check the new - // model (trail_changed is true, but (conflict & unsat) is false) - // - Learned empty clause: return to CDCL loop (unsat true) - // - Learned a non-conflicting unit clause: - // Though it does not invalidate the current solution, the solver - // will backtrack to the root level and will repropagate it. The - // search will start again (saved phases hopefully make it quick), - // but it is needed in order to guarantee that every fixed variable - // is properly handled+notified (important for incremental use - // cases). - // - Otherwise: the solution is considered approved and the CDCL-loop - // can return with res = 10. - // - if (unsat || conflict || trail_changed) - break; - has_external_clause = ask_external_clause (); - stats.ext_prop.ext_cb++; - stats.ext_prop.elearn_call++; - } - LOG ("No more external clause to add."); - if (unsat || conflict) - break; - } - - if (!unsat && conflict) { - const int conflict_level = var (conflict->literals[0]).level; - if (conflict_level != level) { - backtrack (conflict_level); - } - } - - return !conflict; -} - -/*----------------------------------------------------------------------------*/ -// -// Notify the external propagator that an observed variable got assigned. -// -void Internal::notify_assignments () { - if (!external_prop || external_prop_is_lazy || private_steps) - return; - - const size_t end_of_trail = trail.size (); - - if (notified >= end_of_trail) - return; - - LOG ("notify external propagator about new assignments"); - std::vector assigned; - - while (notified < end_of_trail) { - int ilit = trail[notified++]; - if (!observed (ilit)) - continue; - - int elit = externalize (ilit); // TODO: double-check tainting - CADICAL_assert (elit); - if (external->ervars[abs (elit)]) - continue; - // Fixed variables might get mapped (during compact) to another - // non-observed but fixed variable. - // This happens on root level, so notification about their assignment is - // already done. - CADICAL_assert (external->observed (elit) || - (fixed (ilit) && !external->ervars[abs (elit)])); - assigned.push_back (elit); - } - - external->propagator->notify_assignment (assigned); - return; -} - -/*----------------------------------------------------------------------------*/ - -void Internal::connect_propagator () { - if (level) - backtrack (); -} - -/*----------------------------------------------------------------------------*/ -// -// Notify the external propagator that a new decision level is started. -// -void Internal::notify_decision () { - if (!external_prop || external_prop_is_lazy || private_steps) - return; - external->propagator->notify_new_decision_level (); -} - -/*----------------------------------------------------------------------------*/ -// -// Notify the external propagator that backtrack to new_level. -// -void Internal::notify_backtrack (size_t new_level) { - if (!external_prop || external_prop_is_lazy || private_steps) - return; - external->propagator->notify_backtrack (new_level); -} - -/*----------------------------------------------------------------------------*/ -// -// Ask the external propagator if there is a suggested literal as next -// decision. -// -int Internal::ask_decision () { - if (!external_prop || external_prop_is_lazy || private_steps) - return 0; - - CADICAL_assert (!unsat); - CADICAL_assert (!conflict); - notify_assignments (); - int level_before = level; - forced_backt_allowed = true; - int elit = external->propagator->cb_decide (); - forced_backt_allowed = false; - stats.ext_prop.ext_cb++; - - if (level_before != level) { - - propagate (); - CADICAL_assert (!unsat); - CADICAL_assert (!conflict); - notify_assignments (); - - // In case the external propagator forced to backtrack below the - // pseduo decision levels, we must go back to the CDCL loop instead of - // making a decision. - if ((size_t) level < assumptions.size () || - ((size_t) level == assumptions.size () && constraint.size ())) { - return 0; - } - } - - if (!elit) - return 0; - LOG ("external propagator proposes decision: %d", elit); - CADICAL_assert (external->is_observed[abs (elit)]); - if (!external->is_observed[abs (elit)]) - return 0; - - int ilit = external->e2i[abs (elit)]; - if (elit < 0) - ilit = -ilit; - - CADICAL_assert (fixed (ilit) || observed (ilit)); - - LOG ("Asking external propagator for decision returned: %d (internal: " - "%d, fixed: %d, val: %d)", - elit, ilit, fixed (ilit), val (ilit)); - - if (fixed (ilit) || val (ilit)) { - LOG ("Proposed decision variable is already assigned, falling back to " - "internal decision."); - return 0; - } - - return ilit; -} - -/*----------------------------------------------------------------------------*/ -// -// Check if the clause is a forgettable clause coming from the external -// propagator. -// -bool Internal::is_external_forgettable (int64_t id) { - CADICAL_assert (opts.check); - return (external->forgettable_original.find (id) != - external->forgettable_original.end ()); -} - -/*----------------------------------------------------------------------------*/ -// -// When an external forgettable clause is deleted, it is marked in the -// 'forgettable_original' hash, so that the internal model checking can -// ignore it. -// -void Internal::mark_garbage_external_forgettable (int64_t id) { - CADICAL_assert (opts.check); - CADICAL_assert (is_external_forgettable (id)); - - LOG (external->forgettable_original[id], - "forgettable external lemma is deleted:"); - // Mark as removed by flipping the first flag to false. - external->forgettable_original[id][0] = 0; -} - -/*----------------------------------------------------------------------------*/ -// -// Check that the literals in the clause are properly ordered. Used only -// internally for debug purposes. -// -void Internal::check_watched_literal_invariants () { -#ifndef CADICAL_NDEBUG - int v0 = 0; - int v1 = 0; - - if (val (clause[0]) > 0) - v0 = 1; - else if (val (clause[0]) < 0) - v0 = -1; - - if (val (clause[1]) > 0) - v1 = 1; - else if (val (clause[1]) < 0) - v1 = -1; - CADICAL_assert (v0 >= v1); -#endif - if (val (clause[0]) > 0) { - if (val (clause[1]) > 0) { // Case 1: Both literals are satisfied - // They are ordered by lower to higher decision level - CADICAL_assert (var (clause[0]).level <= var (clause[1]).level); - - // Every other literal of the clause is either - // - satisfied at higher level - // - unassigned - // - falsified - for (size_t i = 2; i < clause.size (); i++) - CADICAL_assert (val (clause[i]) <= 0 || - (var (clause[1]).level <= var (clause[i]).level)); - - } else if (val (clause[1]) == - 0) { // Case 2: First satisfied, next unassigned - - // Every other literal of the clause is either - // - unassigned - // - falsified - for (size_t i = 2; i < clause.size (); i++) - CADICAL_assert (val (clause[i]) <= 0); - - } else { // Case 3: First satisfied, next falsified -> could have been a - // reason of a previous propagation - // Every other literal of the clause is falsified but at a lower - // decision level - for (size_t i = 2; i < clause.size (); i++) - CADICAL_assert (val (clause[i]) < 0 && - (var (clause[1]).level >= var (clause[i]).level)); - } - } else if (val (clause[0]) == 0) { - if (val (clause[1]) == 0) { // Case 4: Both literals are unassigned - - // Every other literal of the clause is either - // - unassigned - // - falsified - for (size_t i = 2; i < clause.size (); i++) - CADICAL_assert (val (clause[i]) <= 0); - - } else { // Case 5: First unassigned, next falsified -> PROPAGATE - // Every other literal of the clause is falsified but at a lower - // decision level - for (size_t i = 2; i < clause.size (); i++) - CADICAL_assert (val (clause[i]) < 0 && - (var (clause[1]).level >= var (clause[i]).level)); - } - } else { - CADICAL_assert (val (clause[0]) < 0 && - val (clause[1]) < 0); // Case 6: Both literals are falsified - - // They are ordered by higher to lower decision level - CADICAL_assert (var (clause[0]).level >= var (clause[1]).level); - - // Every other literal of the clause is falsified, but at a lower level - for (size_t i = 2; i < clause.size (); i++) - CADICAL_assert (val (clause[i]) < 0 && - (var (clause[1]).level >= var (clause[i]).level)); - } -} - -#ifndef CADICAL_NDEBUG - -/*----------------------------------------------------------------------------*/ -// -// An expensive function that can be used for deep-debug trail-related -// issues in mobical. Do not use it unless it is really unavoidable. -// -// eq_class contains all the merged external literals that are currently -// compacted to the internal literal of trail[0] and return true. -// -// In case trail[0] does not exists or is not on the root level, the -// function returns false (indicating that there was no merger literal -// found). -// -bool Internal::get_merged_literals (std::vector &eq_class) { - eq_class.clear (); - - if (!trail.size ()) - return false; - - int ilit = trail[0]; - size_t lit_level = var (ilit).level; - - if (!lit_level) { - // Collect all the variables that are merged and mapped to that ilit - size_t e2i_size = external->e2i.size (); - int ivar = abs (ilit); - for (size_t i = 0; i < e2i_size; i++) { - int other = abs (external->e2i[i]); - if (other == ivar) { - if (external->e2i[i] == ilit) - eq_class.push_back (i); - else - eq_class.push_back (-1 * i); - } - } - - return true; - } - - return false; -} - -/*----------------------------------------------------------------------------*/ -// -// Collect all external variables that are FIXED internally. Again an -// expensive function that should be called only for debugging in mobical. -// -// Do not use it unless it is really unavoidable. -// -void Internal::get_all_fixed_literals (std::vector &fixed_lits) { - fixed_lits.clear (); - if (!trail.size ()) - return; - - int e2i_size = external->e2i.size (); - int ilit; - for (int eidx = 1; eidx < e2i_size; eidx++) { - ilit = external->e2i[eidx]; - if (ilit && !external->ervars[eidx]) { - Flags &f = flags (ilit); - if (f.status == Flags::FIXED) { - fixed_lits.push_back (vals[abs (ilit)] * eidx); - } - } - } -} -#endif - -} // namespace CaDiCaL - -ABC_NAMESPACE_IMPL_END diff --git a/src/sat/cadical/cadical_factor.cpp b/src/sat/cadical/cadical_factor.cpp deleted file mode 100644 index 3fee406855..0000000000 --- a/src/sat/cadical/cadical_factor.cpp +++ /dev/null @@ -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 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 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 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 &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 &nounted) { - for (const auto &lit : nounted) { - CADICAL_assert (getfact (lit, NOUNTED)); - unmarkfact (lit, NOUNTED); - } - nounted.clear (); -} - -void Internal::clear_flauses (vector &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 &last_clauses = last_quotient->qlauses; - vector &count = factoring.count; - vector &counted = factoring.counted; - vector &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 &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 &last_clauses = last_quotient->qlauses; - vector &next_clauses = next_quotient->qlauses; - vector &matches = next_quotient->matches; - vector &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 &q_matches = q->matches, &prev_matches = prev->matches; - vector &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 diff --git a/src/sat/cadical/cadical_file.cpp b/src/sat/cadical/cadical_file.cpp deleted file mode 100644 index 98f19f752d..0000000000 --- a/src/sat/cadical/cadical_file.cpp +++ /dev/null @@ -1,527 +0,0 @@ -#include "global.h" - -#include "internal.hpp" - -/*------------------------------------------------------------------------*/ - -// Some more low-level 'C' headers. - -extern "C" { -#include -#include -#include -#include -#include -#include -} - -#ifdef WIN32 - -#include -#include -#include - -#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 -#include -} - -#endif - -#if defined(__APPLE__) || defined(__MACH__) - -extern "C" { -#include -#include -} - -#include - -#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 &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 &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 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 diff --git a/src/sat/cadical/cadical_flags.cpp b/src/sat/cadical/cadical_flags.cpp deleted file mode 100644 index 5c06a33b4a..0000000000 --- a/src/sat/cadical/cadical_flags.cpp +++ /dev/null @@ -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 diff --git a/src/sat/cadical/cadical_flip.cpp b/src/sat/cadical/cadical_flip.cpp deleted file mode 100644 index a89e3abb7c..0000000000 --- a/src/sat/cadical/cadical_flip.cpp +++ /dev/null @@ -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 diff --git a/src/sat/cadical/cadical_format.cpp b/src/sat/cadical/cadical_format.cpp deleted file mode 100644 index 942d9d74a4..0000000000 --- a/src/sat/cadical/cadical_format.cpp +++ /dev/null @@ -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 diff --git a/src/sat/cadical/cadical_frattracer.cpp b/src/sat/cadical/cadical_frattracer.cpp deleted file mode 100644 index d35a182719..0000000000 --- a/src/sat/cadical/cadical_frattracer.cpp +++ /dev/null @@ -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 &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 &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 &clause, - const vector &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 &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 &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 &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 &clause, - const vector &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 &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 &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 diff --git a/src/sat/cadical/cadical_gates.cpp b/src/sat/cadical/cadical_gates.cpp deleted file mode 100644 index 06a73d1ebd..0000000000 --- a/src/sat/cadical/cadical_gates.cpp +++ /dev/null @@ -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 &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 &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 &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 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 diff --git a/src/sat/cadical/cadical_idruptracer.cpp b/src/sat/cadical/cadical_idruptracer.cpp deleted file mode 100644 index ec6467163b..0000000000 --- a/src/sat/cadical/cadical_idruptracer.cpp +++ /dev/null @@ -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 &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 &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 &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 &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 &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 &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 &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 &clause, - const vector &) { - 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 &clause, - const vector &) { - 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 &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 &) { - 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 &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 &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 &model) { - if (file->closed ()) - return; - LOG (model, "IDRUP TRACER tracing conclusion of model"); - idrup_conclude_sat (model); -} - -void IdrupTracer::conclude_unknown (const vector &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 diff --git a/src/sat/cadical/cadical_instantiate.cpp b/src/sat/cadical/cadical_instantiate.cpp deleted file mode 100644 index 2c34015b2e..0000000000 --- a/src/sat/cadical/cadical_instantiate.cpp +++ /dev/null @@ -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 diff --git a/src/sat/cadical/cadical_internal.cpp b/src/sat/cadical/cadical_internal.cpp deleted file mode 100644 index abe18c59cd..0000000000 --- a/src/sat/cadical/cadical_internal.cpp +++ /dev/null @@ -1,1196 +0,0 @@ -#include "global.h" - -#include "internal.hpp" - -ABC_NAMESPACE_IMPL_START - -namespace CaDiCaL { - -/*------------------------------------------------------------------------*/ -static Clause external_reason_clause; - -Internal::Internal () - : mode (SEARCH), unsat (false), iterating (false), - localsearching (false), lookingahead (false), preprocessing (false), - protected_reasons (false), force_saved_phase (false), - searching_lucky_phases (false), stable (false), reported (false), - external_prop (false), did_external_prop (false), - external_prop_is_lazy (true), forced_backt_allowed (false), - - private_steps (false), rephased (0), vsize (0), max_var (0), - clause_id (0), original_id (0), reserved_ids (0), conflict_id (0), - saved_decisions (0), concluded (false), lrat (false), frat (false), - level (0), vals (0), score_inc (1.0), scores (this), conflict (0), - ignore (0), external_reason (&external_reason_clause), - newest_clause (0), force_no_backtrack (false), - from_propagator (false), ext_clause_forgettable (false), - tainted_literal (0), notified (0), probe_reason (0), propagated (0), - propagated2 (0), propergated (0), best_assigned (0), - target_assigned (0), no_conflict_until (0), unsat_constraint (false), - marked_failed (true), sweep_incomplete (false), citten (0), - num_assigned (0), proof (0), opts (this), -#ifndef CADICAL_QUIET - profiles (this), force_phase_messages (false), -#endif - arena (this), prefix ("c "), internal (this), external (0), - termination_forced (false), vars (this->max_var), - lits (this->max_var) { - control.push_back (Level (0, 0)); - - // The 'dummy_binary' is used in 'try_to_subsume_clause' to fake a real - // clause, which then can be used to subsume or strengthen the given - // clause in one routine for both binary and non binary clauses. This - // fake binary clause is always kept non-redundant (and not-moved etc.) - // due to the following 'memset'. Only literals will be changed. - - // In a previous version we used local automatic allocated 'Clause' on the - // stack, which became incompatible with several compilers (see the - // discussion on flexible array member in 'Clause.cpp'). - - size_t bytes = Clause::bytes (2); - dummy_binary = (Clause *) new char[bytes]; - memset (dummy_binary, 0, bytes); - dummy_binary->size = 2; -} - -Internal::~Internal () { - // If a memory exception ocurred a profile might still be active. -#ifndef CADICAL_QUIET -#define PROFILE(NAME, LEVEL) \ - if (PROFILE_ACTIVE (NAME)) \ - STOP (NAME); - PROFILES -#undef PROFILE -#endif - delete[] (char *) dummy_binary; - for (const auto &c : clauses) - delete_clause (c); - if (proof) - delete proof; - for (auto &tracer : tracers) - delete tracer; - for (auto &filetracer : file_tracers) - delete filetracer; - for (auto &stattracer : stat_tracers) - delete stattracer; - if (vals) { - vals -= vsize; - delete[] vals; - } -} - -/*------------------------------------------------------------------------*/ - -// Values in 'vals' can be accessed in the range '[-max_var,max_var]' that -// is directly by a literal. This is crucial for performance. By shifting -// the start of 'vals' appropriately, we achieve that negative offsets from -// the start of 'vals' can be used. We also need to set both values at -// 'lit' and '-lit' during assignments. In MiniSAT integer literals are -// encoded, using the least significant bit as negation. This avoids taking -// the 'abs ()' (as in our solution) and thus also avoids a branch in the -// hot-spot of the solver (clause traversal in propagation). That solution -// requires another (branch less) negation of the values though and -// debugging is harder since literals occur only encoded in clauses. -// The main draw-back of our solution is that we have to shift the memory -// and access it through negative indices, which looks less clean (but still -// as far I can tell is properly defined C / C++). You might get a warning -// by static analyzers though. Clang with '--analyze' thought that this -// idiom would generate a memory leak thus we use the following dummy. - -static signed char *ignore_clang_analyze_memory_leak_warning; - -void Internal::enlarge_vals (size_t new_vsize) { - signed char *new_vals; - const size_t bytes = 2u * new_vsize; - new_vals = new signed char[bytes]; // g++-4.8 does not like ... { 0 }; - memset (new_vals, 0, bytes); - ignore_clang_analyze_memory_leak_warning = new_vals; - new_vals += new_vsize; - - if (vals) { - memcpy (new_vals - max_var, vals - max_var, 2u * max_var + 1u); - vals -= vsize; - delete[] vals; - } else - CADICAL_assert (!vsize); - vals = new_vals; -} - -/*------------------------------------------------------------------------*/ - -void Internal::enlarge (int new_max_var) { - // New variables can be created that can invoke enlarge anytime (via calls - // during ipasir-up call-backs), thus assuming (!level) is not correct - size_t new_vsize = vsize ? 2 * vsize : 1 + (size_t) new_max_var; - while (new_vsize <= (size_t) new_max_var) - new_vsize *= 2; - LOG ("enlarge internal size from %zd to new size %zd", vsize, new_vsize); - // Ordered in the size of allocated memory (larger block first). - if (lrat || frat) - enlarge_zero (unit_clauses_idx, 2 * new_vsize); - enlarge_only (wtab, 2 * new_vsize); - enlarge_only (vtab, new_vsize); - enlarge_zero (parents, new_vsize); - enlarge_only (links, new_vsize); - enlarge_zero (btab, new_vsize); - enlarge_zero (gtab, new_vsize); - enlarge_zero (stab, new_vsize); - enlarge_init (ptab, 2 * new_vsize, -1); - enlarge_only (ftab, new_vsize); - enlarge_vals (new_vsize); - vsize = new_vsize; - if (external) - enlarge_zero (relevanttab, new_vsize); - const signed char val = opts.phase ? 1 : -1; - enlarge_init (phases.saved, new_vsize, val); - enlarge_zero (phases.forced, new_vsize); - enlarge_zero (phases.target, new_vsize); - enlarge_zero (phases.best, new_vsize); - enlarge_zero (phases.prev, new_vsize); - enlarge_zero (phases.min, new_vsize); - enlarge_zero (marks, new_vsize); -} - -void Internal::init_vars (int new_max_var) { - if (new_max_var <= max_var) - return; - // New variables can be created that can invoke enlarge anytime (via calls - // during ipasir-up call-backs), thus assuming (!level) is not correct - LOG ("initializing %d internal variables from %d to %d", - new_max_var - max_var, max_var + 1, new_max_var); - if ((size_t) new_max_var >= vsize) - enlarge (new_max_var); -#ifndef CADICAL_NDEBUG - for (int64_t i = -new_max_var; i < -max_var; i++) - CADICAL_assert (!vals[i]); - for (unsigned i = max_var + 1; i <= (unsigned) new_max_var; i++) - CADICAL_assert (!vals[i]), CADICAL_assert (!btab[i]), CADICAL_assert (!gtab[i]); - for (uint64_t i = 2 * ((uint64_t) max_var + 1); - i <= 2 * (uint64_t) new_max_var + 1; i++) - CADICAL_assert (ptab[i] == -1); -#endif - CADICAL_assert (!btab[0]); - int old_max_var = max_var; - max_var = new_max_var; - init_queue (old_max_var, new_max_var); - init_scores (old_max_var, new_max_var); - int initialized = new_max_var - old_max_var; - stats.vars += initialized; - stats.unused += initialized; - stats.inactive += initialized; - LOG ("finished initializing %d internal variables", initialized); -} - -void Internal::add_original_lit (int lit) { - CADICAL_assert (abs (lit) <= max_var); - if (lit) { - original.push_back (lit); - } else { - const int64_t id = - original_id < reserved_ids ? ++original_id : ++clause_id; - if (proof) { - // Use the external form of the clause for printing in proof - // Externalize(internalized literal) != external literal - CADICAL_assert (!original.size () || !external->eclause.empty ()); - proof->add_external_original_clause (id, false, external->eclause); - } - if (internal->opts.check && - (internal->opts.checkwitness || internal->opts.checkfailed)) { - bool forgettable = from_propagator && ext_clause_forgettable; - if (forgettable && opts.check) { - CADICAL_assert (!original.size () || !external->eclause.empty ()); - - // First integer is the presence-flag (even if the clause is empty) - external->forgettable_original[id] = {1}; - - for (auto const &elit : external->eclause) - external->forgettable_original[id].push_back (elit); - - LOG (external->eclause, - "clause added to external forgettable map:"); - } - } - - add_new_original_clause (id); - original.clear (); - } -} - -void Internal::finish_added_clause_with_id (int64_t id, bool restore) { - if (proof) { - // Use the external form of the clause for printing in proof - // Externalize(internalized literal) != external literal - CADICAL_assert (!original.size () || !external->eclause.empty ()); - proof->add_external_original_clause (id, false, external->eclause, - restore); - } - add_new_original_clause (id); - original.clear (); -} - -/*------------------------------------------------------------------------*/ - -void Internal::reserve_ids (int number) { - // return; - LOG ("reserving %d ids", number); - CADICAL_assert (number >= 0); - CADICAL_assert (!clause_id && !reserved_ids && !original_id); - clause_id = reserved_ids = number; - if (proof) - proof->begin_proof (reserved_ids); -} - -/*------------------------------------------------------------------------*/ - -#ifdef PROFILE_MODE - -// Separating these makes it easier to profile stable and unstable search. - -bool Internal::propagate_wrapper () { - if (stable) - return propagate_stable (); - else - return propagate_unstable (); -} - -void Internal::analyze_wrapper () { - if (stable) - analyze_stable (); - else - analyze_unstable (); -} - -int Internal::decide_wrapper () { - if (stable) - return decide_stable (); - else - return decide_unstable (); -} - -#endif - -/*------------------------------------------------------------------------*/ - -// This is the main CDCL loop with interleaved inprocessing. - -int Internal::cdcl_loop_with_inprocessing () { - - int res = 0; - - START (search); - - if (stable) { - START (stable); - report ('['); - } else { - START (unstable); - report ('{'); - } - - while (!res) { - if (unsat) - res = 20; - else if (unsat_constraint) - res = 20; - else if (!propagate_wrapper ()) - analyze_wrapper (); // propagate and analyze - else if (iterating) - iterate (); // report learned unit - else if (!external_propagate () || unsat) { // external propagation - if (unsat) - continue; - else - analyze (); - } else if (satisfied ()) { // found model - if (!external_check_solution () || unsat) { - if (unsat) - continue; - else - analyze (); - } else if (satisfied ()) - res = 10; - } else if (search_limits_hit ()) - break; // decision or conflict limit - else if (terminated_asynchronously ()) // externally terminated - break; - else if (restarting ()) - restart (); // restart by backtracking - else if (rephasing ()) - rephase (); // reset variable phases - else if (reducing ()) - reduce (); // collect useless clauses - else if (inprobing ()) - inprobe (); // schedule of inprocessing - else if (ineliminating ()) - elim (); // variable elimination - else if (compacting ()) - compact (); // collect variables - else if (conditioning ()) - condition (); // globally blocked clauses - else - res = decide (); // next decision - } - - if (stable) { - STOP (stable); - report (']'); - } else { - STOP (unstable); - report ('}'); - } - - STOP (search); - - return res; -} - -int Internal::propagate_assumptions () { - if (proof) - proof->solve_query (); - if (opts.ilb) { - if (opts.ilbassumptions) - sort_and_reuse_assumptions (); - stats.ilbtriggers++; - stats.ilbsuccess += (level > 0); - stats.levelsreused += level; - if (level) { - CADICAL_assert (control.size () > 1); - stats.literalsreused += num_assigned - control[1].trail; - } - } - init_search_limits (); - init_report_limits (); - - int res = already_solved (); // root-level propagation is done here - - int last_assumption_level = assumptions.size (); - if (constraint.size ()) - last_assumption_level++; - - if (!res) { - restore_clauses (); - while (!res) { - if (unsat) - res = 20; - else if (unsat_constraint) - res = 20; - else if (!propagate ()) { - // let analyze run to get failed assumptions - analyze (); - } else if (!external_propagate () || unsat) { // external propagation - if (unsat) - continue; - else - analyze (); - } else if (satisfied ()) { // found model - if (!external_check_solution () || unsat) { - if (unsat) - continue; - else - analyze (); - } else if (satisfied ()) - res = 10; - } else if (search_limits_hit ()) - break; // decision or conflict limit - else if (terminated_asynchronously ()) // externally terminated - break; - else { - if (level >= last_assumption_level) - break; - res = decide (); - } - } - } - - if (unsat || unsat_constraint) - res = 20; - - if (!res && satisfied ()) - res = 10; - - finalize (res); - reset_solving (); - report_solving (res); - - return res; -} - -void Internal::implied (std::vector &entrailed) { - int last_assumption_level = assumptions.size (); - if (constraint.size ()) - last_assumption_level++; - - size_t trail_limit = trail.size(); - if (level > last_assumption_level) - trail_limit = control[last_assumption_level + 1].trail; - - for (size_t i = 0; i < trail_limit; i++) - entrailed.push_back (trail[i]); -} - -/*------------------------------------------------------------------------*/ - -// Most of the limits are only initialized in the first 'solve' call and -// increased as in a stand-alone non-incremental SAT call except for those -// explicitly marked as being reset below. - -void Internal::init_report_limits () { - reported = false; - lim.report = 0; - lim.recompute_tier = 5000; -} - -void Internal::init_preprocessing_limits () { - - const bool incremental = lim.initialized; - if (incremental) - LOG ("reinitializing preprocessing limits incrementally"); - else - LOG ("initializing preprocessing limits and increments"); - - const char *mode = 0; - - /*----------------------------------------------------------------------*/ - - if (incremental) - mode = "keeping"; - else { - last.elim.marked = -1; - lim.elim = stats.conflicts + scale (opts.elimint); - mode = "initial"; - } - (void) mode; - LOG ("%s elim limit %" PRId64 " after %" PRId64 " conflicts", mode, - lim.elim, lim.elim - stats.conflicts); - - // Initialize and reset elimination bounds in any case. - - lim.elimbound = opts.elimboundmin; - LOG ("elimination bound %" PRId64 "", lim.elimbound); - - /*----------------------------------------------------------------------*/ - - if (!incremental) { - - last.ternary.marked = -1; // TODO this should not be necessary... - - lim.compact = stats.conflicts + opts.compactint; - LOG ("initial compact limit %" PRId64 " increment %" PRId64 "", - lim.compact, lim.compact - stats.conflicts); - } - - /*----------------------------------------------------------------------*/ - - if (incremental) - mode = "keeping"; - else { - double delta = log10 (stats.added.irredundant); - delta = delta * delta; - lim.inprobe = stats.conflicts + opts.inprobeint * delta; - mode = "initial"; - } - (void) mode; - LOG ("%s probe limit %" PRId64 " after %" PRId64 " conflicts", mode, - lim.inprobe, lim.inprobe - stats.conflicts); - - /*----------------------------------------------------------------------*/ - - if (incremental) - mode = "keeping"; - else { - lim.condition = stats.conflicts + opts.conditionint; - mode = "initial"; - } - LOG ("%s condition limit %" PRId64 " increment %" PRId64, mode, - lim.condition, lim.condition - stats.conflicts); - - /*----------------------------------------------------------------------*/ - - // Initial preprocessing rounds. - - if (inc.preprocessing <= 0) { - lim.preprocessing = 0; - LOG ("no preprocessing"); - } else { - lim.preprocessing = inc.preprocessing; - LOG ("limiting to %" PRId64 " preprocessing rounds", lim.preprocessing); - } -} - -void Internal::init_search_limits () { - - const bool incremental = lim.initialized; - if (incremental) - LOG ("reinitializing search limits incrementally"); - else - LOG ("initializing search limits and increments"); - - const char *mode = 0; - - /*----------------------------------------------------------------------*/ - - if (incremental) - mode = "keeping"; - else { - last.reduce.conflicts = -1; - lim.reduce = stats.conflicts + opts.reduceinit; - mode = "initial"; - } - (void) mode; - LOG ("%s reduce limit %" PRId64 " after %" PRId64 " conflicts", mode, - lim.reduce, lim.reduce - stats.conflicts); - - /*----------------------------------------------------------------------*/ - - if (incremental) - mode = "keeping"; - else { - lim.flush = opts.flushint; - inc.flush = opts.flushint; - mode = "initial"; - } - (void) mode; - LOG ("%s flush limit %" PRId64 " interval %" PRId64 "", mode, lim.flush, - inc.flush); - - /*----------------------------------------------------------------------*/ - - // Initialize or reset 'rephase' limits in any case. - - lim.rephase = stats.conflicts + opts.rephaseint; - lim.rephased[0] = lim.rephased[1] = 0; - LOG ("new rephase limit %" PRId64 " after %" PRId64 " conflicts", - lim.rephase, lim.rephase - stats.conflicts); - - /*----------------------------------------------------------------------*/ - - // Initialize or reset 'restart' limits in any case. - - lim.restart = stats.conflicts + opts.restartint; - LOG ("new restart limit %" PRId64 " increment %" PRId64 "", lim.restart, - lim.restart - stats.conflicts); - - /*----------------------------------------------------------------------*/ - - if (!incremental) { - stable = opts.stabilize && opts.stabilizeonly; - if (stable) - LOG ("starting in always forced stable phase"); - else - LOG ("starting in default non-stable phase"); - init_averages (); - } else if (opts.stabilize && opts.stabilizeonly) { - LOG ("keeping always forced stable phase"); - CADICAL_assert (stable); - } else if (stable) { - LOG ("switching back to default non-stable phase"); - stable = false; - swap_averages (); - } else - LOG ("keeping non-stable phase"); - - if (!incremental) { - inc.stabilize = 0; - lim.stabilize = stats.conflicts + opts.stabilizeinit; - LOG ("initial stabilize limit %" PRId64 " after %d conflicts", - lim.stabilize, (int) opts.stabilizeinit); - } - - if (opts.stabilize && opts.reluctant) { - LOG ("new restart reluctant doubling sequence period %d", - opts.reluctant); - reluctant.enable (opts.reluctant, opts.reluctantmax); - } else - reluctant.disable (); - - /*----------------------------------------------------------------------*/ - - // Conflict and decision limits. - - if (inc.conflicts < 0) { - lim.conflicts = -1; - LOG ("no limit on conflicts"); - } else { - lim.conflicts = stats.conflicts + inc.conflicts; - LOG ("conflict limit after %" PRId64 " conflicts at %" PRId64 - " conflicts", - inc.conflicts, lim.conflicts); - } - - if (inc.decisions < 0) { - lim.decisions = -1; - LOG ("no limit on decisions"); - } else { - lim.decisions = stats.decisions + inc.decisions; - LOG ("conflict limit after %" PRId64 " decisions at %" PRId64 - " decisions", - inc.decisions, lim.decisions); - } - - /*----------------------------------------------------------------------*/ - - // Initial preprocessing rounds. - - if (inc.localsearch <= 0) { - lim.localsearch = 0; - LOG ("no local search"); - } else { - lim.localsearch = inc.localsearch; - LOG ("limiting to %" PRId64 " local search rounds", lim.localsearch); - } - - /*----------------------------------------------------------------------*/ - - lim.initialized = true; -} - -/*------------------------------------------------------------------------*/ - -bool Internal::preprocess_round (int round) { - (void) round; - if (unsat) - return false; - if (!max_var) - return false; - START (preprocess); - struct { - int64_t vars, clauses; - } before, after; - before.vars = active (); - before.clauses = stats.current.irredundant; - stats.preprocessings++; - CADICAL_assert (!preprocessing); - preprocessing = true; - PHASE ("preprocessing", stats.preprocessings, - "starting round %d with %" PRId64 " variables and %" PRId64 - " clauses", - round, before.vars, before.clauses); - int old_elimbound = lim.elimbound; - if (opts.inprobing) - inprobe (false); - if (opts.elim) - elim (false); - if (opts.condition) - condition (false); - - after.vars = active (); - after.clauses = stats.current.irredundant; - CADICAL_assert (preprocessing); - preprocessing = false; - PHASE ("preprocessing", stats.preprocessings, - "finished round %d with %" PRId64 " variables and %" PRId64 - " clauses", - round, after.vars, after.clauses); - STOP (preprocess); - report ('P'); - if (unsat) - return false; - if (after.vars < before.vars) - return true; - if (old_elimbound < lim.elimbound) - return true; - return false; -} - -// for now counts as one of the preprocessing rounds TODO: change this? -void Internal::preprocess_quickly () { - if (unsat) - return; - if (!max_var) - return; - if (!opts.preprocesslight) - return; - START (preprocess); - struct { - int64_t vars, clauses; - } before, after; - before.vars = active (); - before.clauses = stats.current.irredundant; - // stats.preprocessings++; - CADICAL_assert (!preprocessing); - preprocessing = true; - PHASE ("preprocessing", stats.preprocessings, - "starting with %" PRId64 " variables and %" PRId64 " clauses", - before.vars, before.clauses); - - if (extract_gates ()) - decompose (); - - if (sweep ()) - decompose (); - - if (opts.factor) - factor (); - - if (opts.fastelim) - elimfast (); - // if (opts.condition) - // condition (false); - after.vars = active (); - after.clauses = stats.current.irredundant; - CADICAL_assert (preprocessing); - preprocessing = false; - PHASE ("preprocessing", stats.preprocessings, - "finished with %" PRId64 " variables and %" PRId64 " clauses", - after.vars, after.clauses); - STOP (preprocess); - report ('P'); -} - -int Internal::preprocess () { - preprocess_quickly (); - for (int i = 0; i < lim.preprocessing; i++) - if (!preprocess_round (i)) - break; - if (unsat) - return 20; - return 0; -} - -/*------------------------------------------------------------------------*/ - -int Internal::try_to_satisfy_formula_by_saved_phases () { - LOG ("satisfying formula by saved phases"); - CADICAL_assert (!level); - CADICAL_assert (!force_saved_phase); - CADICAL_assert (propagated == trail.size ()); - force_saved_phase = true; - if (external_prop) { - CADICAL_assert (!level); - LOG ("external notifications are turned off during preprocessing."); - private_steps = true; - } - int res = 0; - while (!res) { - if (satisfied ()) { - LOG ("formula indeed satisfied by saved phases"); - res = 10; - } else if (decide ()) { - LOG ("inconsistent assumptions with redundant clauses and phases"); - res = 20; - } else if (!propagate ()) { - LOG ("saved phases do not satisfy redundant clauses"); - CADICAL_assert (level > 0); - backtrack (); - conflict = 0; // ignore conflict - CADICAL_assert (!res); - break; - } - } - CADICAL_assert (force_saved_phase); - force_saved_phase = false; - if (external_prop) { - private_steps = false; - LOG ("external notifications are turned back on."); - if (!level) - notify_assignments (); // In case fixed assignments were found. - else { - renotify_trail_after_local_search (); - } - } - return res; -} - -/*------------------------------------------------------------------------*/ - -void Internal::produce_failed_assumptions () { - LOG ("producing failed assumptions"); - CADICAL_assert (!level); - CADICAL_assert (!assumptions.empty ()); - while (!unsat) { - CADICAL_assert (!satisfied ()); - notify_assignments (); - if (decide ()) - break; - while (!unsat && !propagate ()) - analyze (); - } - notify_assignments (); - if (unsat) - LOG ("formula is actually unsatisfiable unconditionally"); - else - LOG ("assumptions indeed failing"); -} - -/*------------------------------------------------------------------------*/ - -int Internal::local_search_round (int round) { - - CADICAL_assert (round > 0); - - if (unsat) - return false; - if (!max_var) - return false; - - START_OUTER_WALK (); - CADICAL_assert (!localsearching); - localsearching = true; - - // Determine propagation limit quadratically scaled with rounds. - // - int64_t limit = opts.walkmineff; - limit *= round; - if (LONG_MAX / round > limit) - limit *= round; - else - limit = LONG_MAX; - - int res = walk_round (limit, true); - - CADICAL_assert (localsearching); - localsearching = false; - STOP_OUTER_WALK (); - - report ('L'); - - return res; -} - -int Internal::local_search () { - - if (unsat) - return 0; - if (!max_var) - return 0; - if (!opts.walk) - return 0; - if (constraint.size ()) - return 0; - - int res = 0; - - for (int i = 1; !res && i <= lim.localsearch; i++) - res = local_search_round (i); - - if (res == 10) { - LOG ("local search determined formula to be satisfiable"); - CADICAL_assert (!stats.walk.minimum); - res = try_to_satisfy_formula_by_saved_phases (); - } else if (res == 20) { - LOG ("local search determined assumptions to be inconsistent"); - CADICAL_assert (!assumptions.empty ()); - produce_failed_assumptions (); - } - - return res; -} - -/*------------------------------------------------------------------------*/ - -// if preprocess_only is false and opts.ilb is true we do not preprocess -// such that we do not have to backtrack to level 0. -// -int Internal::solve (bool preprocess_only) { - CADICAL_assert (clause.empty ()); - START (solve); - if (proof) - proof->solve_query (); - if (opts.ilb) { - if (opts.ilbassumptions) - sort_and_reuse_assumptions (); - stats.ilbtriggers++; - stats.ilbsuccess += (level > 0); - stats.levelsreused += level; - if (level) { - CADICAL_assert (control.size () > 1); - stats.literalsreused += num_assigned - control[1].trail; - } - if (external->propagator) - renotify_trail_after_ilb (); - } - if (preprocess_only) - LOG ("internal solving in preprocessing only mode"); - else - LOG ("internal solving in full mode"); - init_report_limits (); - int res = already_solved (); - if (!res && preprocess_only && level) - backtrack (); - if (!res) - res = restore_clauses (); - if (!res || (res == 10 && external_prop)) { - init_preprocessing_limits (); - if (!preprocess_only) - init_search_limits (); - } - if (!preprocess_only) { - if (!res && !level) - res = local_search (); - } - if (!res && !level) - res = preprocess (); - if (!preprocess_only) { - if (!res && !level) - res = local_search (); - if (!res && !level) - res = lucky_phases (); - if (!res || (res == 10 && external_prop)) { - if (res == 10 && external_prop && level) - backtrack (); - res = cdcl_loop_with_inprocessing (); - } - } - finalize (res); - reset_solving (); - report_solving (res); - STOP (solve); - return res; -} - -int Internal::already_solved () { - int res = 0; - if (unsat || unsat_constraint) { - LOG ("already inconsistent"); - res = 20; - } else { - if (level && !opts.ilb) - backtrack (); - if (!level && !propagate ()) { - LOG ("root level propagation produces conflict"); - learn_empty_clause (); - res = 20; - } - if (max_var == 0 && res == 0) - res = 10; - } - return res; -} -void Internal::report_solving (int res) { - if (res == 10) - report ('1'); - else if (res == 20) - report ('0'); - else - report ('?'); -} - -void Internal::reset_solving () { - if (termination_forced) { - - // TODO this leads potentially to a data race if the external - // user is calling 'terminate' twice within one 'solve' call. - // A proper solution would be to guard / protect setting the - // 'termination_forced' flag and only allow it during solving and - // ignore it otherwise thus also the second time it is called during a - // 'solve' call. We could move resetting it also the start of - // 'solve'. - // - termination_forced = false; - - LOG ("reset forced termination"); - } -} - -int Internal::restore_clauses () { - int res = 0; - if (opts.restoreall <= 1 && external->tainted.empty ()) { - LOG ("no tainted literals and nothing to restore"); - report ('*'); - } else { - report ('+'); - // remove_garbage_binaries (); - external->restore_clauses (); - internal->report ('r'); - if (!unsat && !level && !propagate ()) { - LOG ("root level propagation after restore produces conflict"); - learn_empty_clause (); - res = 20; - } - } - return res; -} - -int Internal::lookahead () { - CADICAL_assert (clause.empty ()); - START (lookahead); - CADICAL_assert (!lookingahead); - lookingahead = true; - if (external_prop) { - if (level) { - // Combining lookahead with external propagator is limited - // Note that lookahead_probing (); would also force backtrack anyway - backtrack (); - } - LOG ("external notifications are turned off during preprocessing."); - private_steps = true; - } - int tmp = already_solved (); - if (!tmp) - tmp = restore_clauses (); - int res = 0; - if (!tmp) - res = lookahead_probing (); - if (res == INT_MIN) - res = 0; - reset_solving (); - report_solving (tmp); - CADICAL_assert (lookingahead); - lookingahead = false; - STOP (lookahead); - if (external_prop) { - private_steps = false; - LOG ("external notifications are turned back on."); - notify_assignments (); // In case fixed assignments were found. - } - return res; -} - -/*------------------------------------------------------------------------*/ - -void Internal::finalize (int res) { - if (!proof) - return; - LOG ("finalizing"); - // finalize external units - if (frat) { - for (const auto &evar : external->vars) { - CADICAL_assert (evar > 0); - const auto eidx = 2 * evar; - int sign = 1; - int64_t id = external->ext_units[eidx]; - if (!id) { - sign = -1; - id = external->ext_units[eidx + 1]; - } - if (id) { - proof->finalize_external_unit (id, evar * sign); - } - } - // finalize internal units - for (const auto &lit : lits) { - const auto elit = externalize (lit); - if (elit) { - const unsigned eidx = (elit < 0) + 2u * (unsigned) abs (elit); - const int64_t id = external->ext_units[eidx]; - if (id) { - CADICAL_assert (unit_clauses (vlit (lit)) == id); - continue; - } - } - const int64_t id = unit_clauses (vlit (lit)); - if (!id) - continue; - proof->finalize_unit (id, lit); - } - // See the discussion in 'propagate' on why garbage binary clauses stick - // around. - for (const auto &c : clauses) - if (!c->garbage || (c->size == 2 && !c->flushed)) - proof->finalize_clause (c); - - // finalize conflict and proof - if (conflict_id) { - proof->finalize_clause (conflict_id, {}); - } - } - proof->report_status (res, conflict_id); - if (res == 10) - external->conclude_sat (); - else if (res == 20) - conclude_unsat (); - else if (!res) - external->conclude_unknown (); -} - -/*------------------------------------------------------------------------*/ - -void Internal::print_statistics () { - stats.print (this); - for (auto &st : stat_tracers) - st->print_stats (); -} - -/*------------------------------------------------------------------------*/ - -// Only useful for debugging purposes. - -void Internal::dump (Clause *c) { - for (const auto &lit : *c) - printf ("%d ", lit); - printf ("0\n"); -} - -void Internal::dump () { - int64_t m = assumptions.size (); - for (auto idx : vars) - if (fixed (idx)) - m++; - for (const auto &c : clauses) - if (!c->garbage) - m++; - printf ("p cnf %d %" PRId64 "\n", max_var, m); - for (auto idx : vars) { - const int tmp = fixed (idx); - if (tmp) - printf ("%d 0\n", tmp < 0 ? -idx : idx); - } - for (const auto &c : clauses) - if (!c->garbage) - dump (c); - for (const auto &lit : assumptions) - printf ("%d 0\n", lit); - fflush (stdout); -} - -/*------------------------------------------------------------------------*/ - -bool Internal::traverse_constraint (ClauseIterator &it) { - if (constraint.empty () && !unsat_constraint) - return true; - - vector eclause; - if (unsat) - return it.clause (eclause); - - LOG (constraint, "traversing constraint"); - bool satisfied = false; - for (auto ilit : constraint) { - const int tmp = fixed (ilit); - if (tmp > 0) { - satisfied = true; - break; - } - if (tmp < 0) - continue; - const int elit = externalize (ilit); - eclause.push_back (elit); - } - if (!satisfied && !it.clause (eclause)) - return false; - - return true; -} -/*------------------------------------------------------------------------*/ - -bool Internal::traverse_clauses (ClauseIterator &it) { - vector eclause; - if (unsat) - return it.clause (eclause); - for (const auto &c : clauses) { - if (c->garbage) - continue; - if (c->redundant) - continue; - bool satisfied = false; - for (const auto &ilit : *c) { - const int tmp = fixed (ilit); - if (tmp > 0) { - satisfied = true; - break; - } - if (tmp < 0) - continue; - const int elit = externalize (ilit); - eclause.push_back (elit); - } - if (!satisfied && !it.clause (eclause)) - return false; - eclause.clear (); - } - return true; -} - -} // namespace CaDiCaL - -ABC_NAMESPACE_IMPL_END diff --git a/src/sat/cadical/cadical_ipasir.cpp b/src/sat/cadical/cadical_ipasir.cpp deleted file mode 100644 index cb88deb4b2..0000000000 --- a/src/sat/cadical/cadical_ipasir.cpp +++ /dev/null @@ -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 diff --git a/src/sat/cadical/cadical_kitten.c b/src/sat/cadical/cadical_kitten.c deleted file mode 100644 index da37389803..0000000000 --- a/src/sat/cadical/cadical_kitten.c +++ /dev/null @@ -1,2609 +0,0 @@ -#include "global.h" - -#include "kitten.h" -#include "random.h" -#include "stack.h" - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -ABC_NAMESPACE_IMPL_START - -typedef signed char value; - -static void die (const char *fmt, ...) { - fputs ("cadical_kitten: error: ", stderr); - va_list ap; - va_start (ap, fmt); - vfprintf (stderr, fmt, ap); - va_end (ap); - fputc ('\n', stderr); - exit (1); -} - -static inline void *cadical_kitten_calloc (size_t n, size_t size) { - void *res = calloc (n, size); - if (n && size && !res) - die ("out of memory allocating '%zu * %zu' bytes", n, size); - return res; -} - -#define CALLOC(T, P, N) \ - do { \ - (P) = (T*)cadical_kitten_calloc (N, sizeof *(P)); \ - } while (0) -#define DEALLOC(P, N) free (P) - -#undef ENLARGE_STACK - -#define ENLARGE_STACK(S) \ - do { \ - CADICAL_assert (FULL_STACK (S)); \ - const size_t SIZE = SIZE_STACK (S); \ - const size_t OLD_CAPACITY = CAPACITY_STACK (S); \ - const size_t NEW_CAPACITY = OLD_CAPACITY ? 2 * OLD_CAPACITY : 1; \ - const size_t BYTES = NEW_CAPACITY * sizeof *(S).begin; \ - (S).begin = (unsigned*)realloc ((S).begin, BYTES); \ - if (!(S).begin) \ - die ("out of memory reallocating '%zu' bytes", BYTES); \ - (S).allocated = (S).begin + NEW_CAPACITY; \ - (S).end = (S).begin + SIZE; \ - } while (0) - -// Beside allocators above also use stand alone statistics counters. - -#define INC(NAME) \ - do { \ - statistics *statistics = &cadical_kitten->statistics; \ - CADICAL_assert (statistics->NAME < UINT64_MAX); \ - statistics->NAME++; \ - } while (0) - -#define ADD(NAME, DELTA) \ - do { \ - statistics *statistics = &cadical_kitten->statistics; \ - CADICAL_assert (statistics->NAME <= UINT64_MAX - (DELTA)); \ - statistics->NAME += (DELTA); \ - } while (0) - -#define KITTEN_TICKS (cadical_kitten->statistics.cadical_kitten_ticks) - -#define INVALID UINT_MAX -#define MAX_VARS ((1u << 31) - 1) - -#define CORE_FLAG (1u) -#define LEARNED_FLAG (2u) - -// clang-format off - -typedef struct kar kar; -typedef struct kink kink; -typedef struct klause klause; -typedef STACK (unsigned) klauses; -typedef unsigneds katches; - -// clang-format on - -struct kar { - unsigned level; - unsigned reason; -}; - -struct kink { - unsigned next; - unsigned prev; - uint64_t stamp; -}; - -struct klause { - unsigned aux; - unsigned size; - unsigned flags; - unsigned lits[1]; -}; - -typedef struct statistics statistics; - -struct statistics { - uint64_t learned; - uint64_t original; - uint64_t cadical_kitten_flip; - uint64_t cadical_kitten_flipped; - uint64_t cadical_kitten_sat; - uint64_t cadical_kitten_solved; - uint64_t cadical_kitten_conflicts; - uint64_t cadical_kitten_decisions; - uint64_t cadical_kitten_propagations; - uint64_t cadical_kitten_ticks; - uint64_t cadical_kitten_unknown; - uint64_t cadical_kitten_unsat; -}; - -typedef struct kimits kimits; - -struct kimits { - uint64_t ticks; -}; - -struct cadical_kitten { - // First zero initialized field in 'clear_cadical_kitten' is 'status'. - // - int status; - -#if defined(LOGGING) - bool logging; -#endif - bool antecedents; - bool learned; - - unsigned level; - unsigned propagated; - unsigned unassigned; - unsigned inconsistent; - unsigned failing; - - uint64_t generator; - - size_t lits; - size_t evars; - - size_t end_original_ref; - - struct { - unsigned first, last; - uint64_t stamp; - unsigned search; - } queue; - - // The 'size' field below is the first not zero reinitialized field - // by 'memset' in 'clear_cadical_kitten' (after 'kissat'). - - size_t size; - size_t esize; - - kar *vars; - kink *links; - value *marks; - value *values; - bool *failed; - unsigned char *phases; - unsigned *import; - katches *watches; - - unsigneds analyzed; - unsigneds assumptions; - unsigneds core; - unsigneds rcore; - unsigneds eclause; - unsigneds export_; - unsigneds klause; - unsigneds klauses; - unsigneds resolved; - unsigneds trail; - unsigneds units; - unsigneds prime[2]; - - kimits limits; - int (*terminator) (void *); - void *terminator_data; - unsigneds clause; - uint64_t initialized; - statistics statistics; -}; - -/*------------------------------------------------------------------------*/ - -static inline bool is_core_klause (klause *c) { - return c->flags & CORE_FLAG; -} - -static inline bool is_learned_klause (klause *c) { - return c->flags & LEARNED_FLAG; -} - -static inline void set_core_klause (klause *c) { c->flags |= CORE_FLAG; } - -static inline void unset_core_klause (klause *c) { c->flags &= ~CORE_FLAG; } - -static inline klause *dereference_klause (cadical_kitten *cadical_kitten, unsigned ref) { - unsigned *res = BEGIN_STACK (cadical_kitten->klauses) + ref; - CADICAL_assert (res < END_STACK (cadical_kitten->klauses)); - return (klause *) res; -} - -static inline unsigned reference_klause (cadical_kitten *cadical_kitten, const klause *c) { - const unsigned *const begin = BEGIN_STACK (cadical_kitten->klauses); - const unsigned *p = (const unsigned *) c; - CADICAL_assert (begin <= p); - CADICAL_assert (p < END_STACK (cadical_kitten->klauses)); - const unsigned res = p - begin; - return res; -} - -/*------------------------------------------------------------------------*/ - -#define KATCHES(KIT) (cadical_kitten->watches[CADICAL_assert ((KIT) < cadical_kitten->lits), (KIT)]) - -#define all_klauses(C) \ - klause *C = begin_klauses (cadical_kitten), *end_##C = end_klauses (cadical_kitten); \ - (C) != end_##C; \ - (C) = next_klause (cadical_kitten, C) - -#define all_original_klauses(C) \ - klause *C = begin_klauses (cadical_kitten), \ - *end_##C = end_original_klauses (cadical_kitten); \ - (C) != end_##C; \ - (C) = next_klause (cadical_kitten, C) - -#define all_learned_klauses(C) \ - klause *C = begin_learned_klauses (cadical_kitten), \ - *end_##C = end_klauses (cadical_kitten); \ - (C) != end_##C; \ - (C) = next_klause (cadical_kitten, C) - -#define all_kits(KIT) \ - size_t KIT = 0, KIT_##END = cadical_kitten->lits; \ - KIT != KIT_##END; \ - KIT++ - -#define BEGIN_KLAUSE(C) (C)->lits - -#define END_KLAUSE(C) (BEGIN_KLAUSE (C) + (C)->size) - -#define all_literals_in_klause(KIT, C) \ - unsigned KIT, *KIT##_PTR = BEGIN_KLAUSE (C), \ - *KIT##_END = END_KLAUSE (C); \ - KIT##_PTR != KIT##_END && ((KIT = *KIT##_PTR), true); \ - ++KIT##_PTR - -#define all_antecedents(REF, C) \ - unsigned REF, *REF##_PTR = antecedents (C), \ - *REF##_END = REF##_PTR + (C)->aux; \ - REF##_PTR != REF##_END && ((REF = *REF##_PTR), true); \ - ++REF##_PTR - -#ifdef LOGGING - -#define logging (cadical_kitten->logging) - -static void log_basic (cadical_kitten *, const char *, ...) - __attribute__ ((format (printf, 2, 3))); - -static void log_basic (cadical_kitten *cadical_kitten, const char *fmt, ...) { - CADICAL_assert (logging); - printf ("c KITTEN %u ", cadical_kitten->level); - va_list ap; - va_start (ap, fmt); - vprintf (fmt, ap); - va_end (ap); - fputc ('\n', stdout); - fflush (stdout); -} - -static void log_reference (cadical_kitten *, unsigned, const char *, ...) - __attribute__ ((format (printf, 3, 4))); - -static void log_reference (cadical_kitten *cadical_kitten, unsigned ref, const char *fmt, - ...) { - klause *c = dereference_klause (cadical_kitten, ref); - CADICAL_assert (logging); - printf ("c KITTEN %u ", cadical_kitten->level); - va_list ap; - va_start (ap, fmt); - vprintf (fmt, ap); - va_end (ap); - if (is_learned_klause (c)) { - fputs (" learned", stdout); - if (c->aux) - printf ("[%u]", c->aux); - } else { - fputs (" original", stdout); - if (c->aux != INVALID) - printf ("[%u]", c->aux); - } - printf (" size %u clause[%u]", c->size, ref); - value *values = cadical_kitten->values; - kar *vars = cadical_kitten->vars; - for (all_literals_in_klause (lit, c)) { - printf (" %u", lit); - const value value = values[lit]; - if (value) - printf ("@%u=%d", vars[lit / 2].level, (int) value); - } - fputc ('\n', stdout); - fflush (stdout); -} - -static void log_literals (cadical_kitten *, unsigned *, unsigned, const char *, ...) - __attribute__ ((format (printf, 4, 5))); - -static void log_literals (cadical_kitten *cadical_kitten, unsigned *lits, unsigned size, - const char *fmt, ...) { - CADICAL_assert (logging); - printf ("c KITTEN %u ", cadical_kitten->level); - va_list ap; - va_start (ap, fmt); - vprintf (fmt, ap); - va_end (ap); - value *values = cadical_kitten->values; - kar *vars = cadical_kitten->vars; - for (unsigned i = 0; i < size; i++) { - const unsigned lit = lits[i]; - printf (" %u", lit); - const value value = values[lit]; - if (value) - printf ("@%u=%d", vars[lit / 2].level, (int) value); - } - fputc ('\n', stdout); - fflush (stdout); -} - -#define LOG(...) \ - do { \ - if (logging) \ - log_basic (cadical_kitten, __VA_ARGS__); \ - } while (0) - -#define ROG(...) \ - do { \ - if (logging) \ - log_reference (cadical_kitten, __VA_ARGS__); \ - } while (0) - -#define LOGLITS(...) \ - do { \ - if (logging) \ - log_literals (cadical_kitten, __VA_ARGS__); \ - } while (0) - -#else - -#define LOG(...) \ - do { \ - } while (0) -#define ROG(...) \ - do { \ - } while (0) -#define LOGLITS(...) \ - do { \ - } while (0) - -#endif - -static void check_queue (cadical_kitten *cadical_kitten) { -#ifdef CHECK_KITTEN - const unsigned vars = cadical_kitten->lits / 2; - unsigned found = 0, prev = INVALID; - kink *links = cadical_kitten->links; - uint64_t stamp = 0; - for (unsigned idx = cadical_kitten->queue.first, next; idx != INVALID; - idx = next) { - kink *link = links + idx; - CADICAL_assert (link->prev == prev); - CADICAL_assert (!found || stamp < link->stamp); - CADICAL_assert (link->stamp < cadical_kitten->queue.stamp); - stamp = link->stamp; - next = link->next; - prev = idx; - found++; - } - CADICAL_assert (found == vars); - unsigned next = INVALID; - found = 0; - for (unsigned idx = cadical_kitten->queue.last, prev; idx != INVALID; - idx = prev) { - kink *link = links + idx; - CADICAL_assert (link->next == next); - prev = link->prev; - next = idx; - found++; - } - CADICAL_assert (found == vars); - value *values = cadical_kitten->values; - bool first = true; - for (unsigned idx = cadical_kitten->queue.search, next; idx != INVALID; - idx = next) { - kink *link = links + idx; - next = link->next; - const unsigned lit = 2 * idx; - CADICAL_assert (first || values[lit]); - first = false; - } -#else - (void) cadical_kitten; -#endif -} - -static void update_search (cadical_kitten *cadical_kitten, unsigned idx) { - if (cadical_kitten->queue.search == idx) - return; - cadical_kitten->queue.search = idx; - LOG ("search updated to %u stamped %" PRIu64, idx, - cadical_kitten->links[idx].stamp); -} - -static void enqueue (cadical_kitten *cadical_kitten, unsigned idx) { - LOG ("enqueue %u", idx); - kink *links = cadical_kitten->links; - kink *l = links + idx; - const unsigned last = cadical_kitten->queue.last; - if (last == INVALID) - cadical_kitten->queue.first = idx; - else - links[last].next = idx; - l->prev = last; - l->next = INVALID; - cadical_kitten->queue.last = idx; - l->stamp = cadical_kitten->queue.stamp++; - LOG ("stamp %" PRIu64, l->stamp); -} - -static void dequeue (cadical_kitten *cadical_kitten, unsigned idx) { - LOG ("dequeue %u", idx); - kink *links = cadical_kitten->links; - kink *l = links + idx; - const unsigned prev = l->prev; - const unsigned next = l->next; - if (prev == INVALID) - cadical_kitten->queue.first = next; - else - links[prev].next = next; - if (next == INVALID) - cadical_kitten->queue.last = prev; - else - links[next].prev = prev; -} - -static void init_queue (cadical_kitten *cadical_kitten, size_t old_vars, size_t new_vars) { - for (size_t idx = old_vars; idx < new_vars; idx++) { - CADICAL_assert (!cadical_kitten->values[2 * idx]); - CADICAL_assert (cadical_kitten->unassigned < UINT_MAX); - cadical_kitten->unassigned++; - enqueue (cadical_kitten, idx); - } - LOG ("initialized decision queue from %zu to %zu", old_vars, new_vars); - update_search (cadical_kitten, cadical_kitten->queue.last); - check_queue (cadical_kitten); -} - -static void initialize_cadical_kitten (cadical_kitten *cadical_kitten) { - cadical_kitten->queue.first = INVALID; - cadical_kitten->queue.last = INVALID; - cadical_kitten->inconsistent = INVALID; - cadical_kitten->failing = INVALID; - cadical_kitten->queue.search = INVALID; - cadical_kitten->terminator = 0; - cadical_kitten->terminator_data = 0; - cadical_kitten->limits.ticks = UINT64_MAX; - cadical_kitten->generator = cadical_kitten->initialized++; -} - -static void clear_cadical_kitten (cadical_kitten *cadical_kitten) { - size_t bytes = (char *) &cadical_kitten->size - (char *) &cadical_kitten->status; - memset (&cadical_kitten->status, 0, bytes); - memset (&cadical_kitten->statistics, 0, sizeof (statistics)); - initialize_cadical_kitten (cadical_kitten); -} - -#define RESIZE1(T, P) \ - do { \ - void *OLD_PTR = (P); \ - CALLOC (T, (P), new_size / 2); \ - const size_t BYTES = old_vars * sizeof *(P); \ - memcpy ((P), OLD_PTR, BYTES); \ - void *NEW_PTR = (P); \ - (P) = (T*)OLD_PTR; \ - DEALLOC ((P), old_size / 2); \ - (P) = (T*)NEW_PTR; \ - } while (0) - -#define RESIZE2(T, P) \ - do { \ - void *OLD_PTR = (P); \ - CALLOC (T, (P), new_size); \ - const size_t BYTES = old_lits * sizeof *(P); \ - memcpy ((P), OLD_PTR, BYTES); \ - void *NEW_PTR = (P); \ - (P) = (T*)OLD_PTR; \ - DEALLOC ((P), old_size); \ - (P) = (T*)NEW_PTR; \ - } while (0) - -static void enlarge_internal (cadical_kitten *cadical_kitten, size_t lit) { - const size_t new_lits = (lit | 1) + 1; - const size_t old_lits = cadical_kitten->lits; - CADICAL_assert (old_lits <= lit); - CADICAL_assert (old_lits < new_lits); - CADICAL_assert ((lit ^ 1) < new_lits); - CADICAL_assert (lit < new_lits); - const size_t old_size = cadical_kitten->size; - const unsigned new_vars = new_lits / 2; - const unsigned old_vars = old_lits / 2; - if (old_size < new_lits) { - size_t new_size = old_size ? 2 * old_size : 2; - while (new_size <= lit) - new_size *= 2; - LOG ("internal literals resized to %zu from %zu (requested %zu)", - new_size, old_size, new_lits); - - RESIZE1 (value, cadical_kitten->marks); - RESIZE1 (unsigned char, cadical_kitten->phases); - RESIZE2 (value, cadical_kitten->values); - RESIZE2 (bool, cadical_kitten->failed); - RESIZE1 (kar, cadical_kitten->vars); - RESIZE1 (kink, cadical_kitten->links); - RESIZE2 (katches, cadical_kitten->watches); - - cadical_kitten->size = new_size; - } - cadical_kitten->lits = new_lits; - init_queue (cadical_kitten, old_vars, new_vars); - LOG ("internal literals activated until %zu literals", new_lits); - return; -} - -static const char *status_to_string (int status) { - switch (status) { - case 10: - return "formula satisfied"; - case 11: - return "formula satisfied and prime implicant computed"; - case 20: - return "formula inconsistent"; - case 21: - return "formula inconsistent and core computed"; - default: - CADICAL_assert (!status); - return "formula unsolved"; - } -} - -static void invalid_api_usage (const char *fun, const char *fmt, ...) { - fprintf (stderr, "cadical_kitten: fatal error: invalid API usage in '%s': ", fun); - va_list ap; - va_start (ap, fmt); - vfprintf (stderr, fmt, ap); - va_end (ap); - fputc ('\n', stderr); - fflush (stderr); - abort (); -} - -#define INVALID_API_USAGE(...) invalid_api_usage (__func__, __VA_ARGS__) - -#define REQUIRE_INITIALIZED() \ - do { \ - if (!cadical_kitten) \ - INVALID_API_USAGE ("solver argument zero"); \ - } while (0) - -#define REQUIRE_STATUS(EXPECTED) \ - do { \ - REQUIRE_INITIALIZED (); \ - if (cadical_kitten->status != (EXPECTED)) \ - INVALID_API_USAGE ("invalid status '%s' (expected '%s')", \ - status_to_string (cadical_kitten->status), \ - status_to_string (EXPECTED)); \ - } while (0) - -#define UPDATE_STATUS(STATUS) \ - do { \ - if (cadical_kitten->status != (STATUS)) \ - LOG ("updating status from '%s' to '%s'", \ - status_to_string (cadical_kitten->status), status_to_string (STATUS)); \ - else \ - LOG ("keeping status at '%s'", status_to_string (STATUS)); \ - cadical_kitten->status = (STATUS); \ - } while (0) - -cadical_kitten *cadical_kitten_init (void) { - cadical_kitten *cadical_kitten; - CALLOC (struct cadical_kitten, cadical_kitten, 1); - initialize_cadical_kitten (cadical_kitten); - return cadical_kitten; -} - -#ifdef LOGGING -void cadical_kitten_set_logging (cadical_kitten *cadical_kitten) { logging = true; } -#endif - -void cadical_kitten_track_antecedents (cadical_kitten *cadical_kitten) { - REQUIRE_STATUS (0); - - if (cadical_kitten->learned) - INVALID_API_USAGE ("can not start tracking antecedents after learning"); - - LOG ("enabling antecedents tracking"); - cadical_kitten->antecedents = true; -} - -void cadical_kitten_randomize_phases (cadical_kitten *cadical_kitten) { - REQUIRE_INITIALIZED (); - - LOG ("randomizing phases"); - - unsigned char *phases = cadical_kitten->phases; - const unsigned vars = cadical_kitten->size / 2; - - uint64_t random = kissat_next_random64 (&cadical_kitten->generator); - - unsigned i = 0; - const unsigned rest = vars & ~63u; - - while (i != rest) { - uint64_t *p = (uint64_t *) (phases + i); - p[0] = (random >> 0) & 0x0101010101010101; - p[1] = (random >> 1) & 0x0101010101010101; - p[2] = (random >> 2) & 0x0101010101010101; - p[3] = (random >> 3) & 0x0101010101010101; - p[4] = (random >> 4) & 0x0101010101010101; - p[5] = (random >> 5) & 0x0101010101010101; - p[6] = (random >> 6) & 0x0101010101010101; - p[7] = (random >> 7) & 0x0101010101010101; - random = kissat_next_random64 (&cadical_kitten->generator); - i += 64; - } - - unsigned shift = 0; - while (i != vars) - phases[i++] = (random >> shift++) & 1; -} - -void cadical_kitten_flip_phases (cadical_kitten *cadical_kitten) { - REQUIRE_INITIALIZED (); - - LOG ("flipping phases"); - - unsigned char *phases = cadical_kitten->phases; - const unsigned vars = cadical_kitten->size / 2; - - unsigned i = 0; - const unsigned rest = vars & ~7u; - - while (i != rest) { - uint64_t *p = (uint64_t *) (phases + i); - *p ^= 0x0101010101010101; - i += 8; - } - - while (i != vars) - phases[i++] ^= 1; -} - -void cadical_kitten_no_ticks_limit (cadical_kitten *cadical_kitten) { - REQUIRE_INITIALIZED (); - LOG ("forcing no ticks limit"); - cadical_kitten->limits.ticks = UINT64_MAX; -} - -uint64_t cadical_kitten_current_ticks (cadical_kitten *cadical_kitten) { - REQUIRE_INITIALIZED (); - const uint64_t current = KITTEN_TICKS; - return current; -} - -void cadical_kitten_set_ticks_limit (cadical_kitten *cadical_kitten, uint64_t delta) { - REQUIRE_INITIALIZED (); - const uint64_t current = KITTEN_TICKS; - uint64_t limit; - if (UINT64_MAX - delta <= current) { - LOG ("forcing unlimited ticks limit"); - limit = UINT64_MAX; - } else { - limit = current + delta; - LOG ("new limit of %" PRIu64 " ticks after %" PRIu64, limit, delta); - } - - cadical_kitten->limits.ticks = limit; -} - -void cadical_kitten_no_terminator (cadical_kitten *cadical_kitten) { - REQUIRE_INITIALIZED (); - LOG ("removing terminator"); - cadical_kitten->terminator = 0; - cadical_kitten->terminator_data = 0; -} - -void cadical_kitten_set_terminator (cadical_kitten *cadical_kitten, void *data, - int (*terminator) (void *)) { - REQUIRE_INITIALIZED (); - LOG ("setting terminator"); - cadical_kitten->terminator = terminator; - cadical_kitten->terminator_data = data; -} - -static void shuffle_unsigned_array (cadical_kitten *cadical_kitten, size_t size, - unsigned *a) { - for (size_t i = 0; i < size; i++) { - const size_t j = kissat_pick_random (&cadical_kitten->generator, 0, i); - if (j == i) - continue; - const unsigned first = a[i]; - const unsigned second = a[j]; - a[i] = second; - a[j] = first; - } -} - -static void shuffle_unsigned_stack (cadical_kitten *cadical_kitten, unsigneds *stack) { - const size_t size = SIZE_STACK (*stack); - unsigned *a = BEGIN_STACK (*stack); - shuffle_unsigned_array (cadical_kitten, size, a); -} - -static void shuffle_katches (cadical_kitten *cadical_kitten) { - LOG ("shuffling watch lists"); - for (size_t lit = 0; lit < cadical_kitten->lits; lit++) - shuffle_unsigned_stack (cadical_kitten, &KATCHES (lit)); -} - -static void shuffle_queue (cadical_kitten *cadical_kitten) { - LOG ("shuffling variable decision order"); - - const unsigned vars = cadical_kitten->lits / 2; - for (unsigned i = 0; i < vars; i++) { - const unsigned idx = kissat_pick_random (&cadical_kitten->generator, 0, vars); - dequeue (cadical_kitten, idx); - enqueue (cadical_kitten, idx); - } - update_search (cadical_kitten, cadical_kitten->queue.last); -} - -static void shuffle_units (cadical_kitten *cadical_kitten) { - LOG ("shuffling units"); - shuffle_unsigned_stack (cadical_kitten, &cadical_kitten->units); -} - -void cadical_kitten_shuffle_clauses (cadical_kitten *cadical_kitten) { - REQUIRE_STATUS (0); - shuffle_queue (cadical_kitten); - shuffle_katches (cadical_kitten); - shuffle_units (cadical_kitten); -} - -static inline unsigned *antecedents (klause *c) { - CADICAL_assert (is_learned_klause (c)); - return c->lits + c->size; -} - -static inline void watch_klause (cadical_kitten *cadical_kitten, unsigned lit, - unsigned ref) { - ROG (ref, "watching %u in", lit); - katches *watches = &KATCHES (lit); - PUSH_STACK (*watches, ref); -} - -static inline void connect_new_klause (cadical_kitten *cadical_kitten, unsigned ref) { - ROG (ref, "new"); - - klause *c = dereference_klause (cadical_kitten, ref); - - if (!c->size) { - if (cadical_kitten->inconsistent == INVALID) { - ROG (ref, "registering inconsistent empty"); - cadical_kitten->inconsistent = ref; - } else - ROG (ref, "ignoring inconsistent empty"); - } else if (c->size == 1) { - ROG (ref, "watching unit"); - PUSH_STACK (cadical_kitten->units, ref); - } else { - watch_klause (cadical_kitten, c->lits[0], ref); - watch_klause (cadical_kitten, c->lits[1], ref); - } -} - -static unsigned new_reference (cadical_kitten *cadical_kitten) { - size_t ref = SIZE_STACK (cadical_kitten->klauses); - if (ref >= INVALID) { - die ("maximum number of literals exhausted"); - } - const unsigned res = (unsigned) ref; - CADICAL_assert (res != INVALID); - INC (cadical_kitten_ticks); - return res; -} - -static void new_original_klause (cadical_kitten *cadical_kitten, unsigned id) { - unsigned res = new_reference (cadical_kitten); - unsigned size = SIZE_STACK (cadical_kitten->klause); - unsigneds *klauses = &cadical_kitten->klauses; - PUSH_STACK (*klauses, id); - PUSH_STACK (*klauses, size); - PUSH_STACK (*klauses, 0); - for (all_stack (unsigned, lit, cadical_kitten->klause)) - PUSH_STACK (*klauses, lit); - connect_new_klause (cadical_kitten, res); - cadical_kitten->end_original_ref = SIZE_STACK (*klauses); - cadical_kitten->statistics.original++; -} - -static void enlarge_external (cadical_kitten *cadical_kitten, size_t eidx) { - const size_t old_size = cadical_kitten->esize; - const unsigned old_evars = cadical_kitten->evars; - CADICAL_assert (old_evars <= eidx); - const unsigned new_evars = eidx + 1; - if (old_size <= eidx) { - size_t new_size = old_size ? 2 * old_size : 1; - while (new_size <= eidx) - new_size *= 2; - LOG ("external resizing to %zu variables from %zu (requested %u)", - new_size, old_size, new_evars); - unsigned *old_import = cadical_kitten->import; - CALLOC (unsigned, cadical_kitten->import, new_size); - const size_t bytes = old_evars * sizeof *cadical_kitten->import; - memcpy (cadical_kitten->import, old_import, bytes); - DEALLOC (old_import, old_size); - cadical_kitten->esize = new_size; - } - cadical_kitten->evars = new_evars; - LOG ("external variables enlarged to %u", new_evars); -} - -static unsigned import_literal (cadical_kitten *cadical_kitten, unsigned elit) { - const unsigned eidx = elit / 2; - if (eidx >= cadical_kitten->evars) - enlarge_external (cadical_kitten, eidx); - - unsigned iidx = cadical_kitten->import[eidx]; - if (!iidx) { - iidx = SIZE_STACK (cadical_kitten->export_); - PUSH_STACK (cadical_kitten->export_, eidx); - cadical_kitten->import[eidx] = iidx + 1; - } else - iidx--; - unsigned ilit = 2 * iidx + (elit & 1); - LOG ("imported external literal %u as internal literal %u", elit, ilit); - if (ilit >= cadical_kitten->lits) - enlarge_internal (cadical_kitten, ilit); - return ilit; -} - -static unsigned export_literal (cadical_kitten *cadical_kitten, unsigned ilit) { - const unsigned iidx = ilit / 2; - CADICAL_assert (iidx < SIZE_STACK (cadical_kitten->export_)); - const unsigned eidx = PEEK_STACK (cadical_kitten->export_, iidx); - const unsigned elit = 2 * eidx + (ilit & 1); - return elit; -} - -unsigned cadical_new_learned_klause (cadical_kitten *cadical_kitten) { - unsigned res = new_reference (cadical_kitten); - unsigneds *klauses = &cadical_kitten->klauses; - const size_t size = SIZE_STACK (cadical_kitten->klause); - CADICAL_assert (size <= UINT_MAX); - const size_t aux = - cadical_kitten->antecedents ? SIZE_STACK (cadical_kitten->resolved) : 0; - CADICAL_assert (aux <= UINT_MAX); - PUSH_STACK (*klauses, (unsigned) aux); - PUSH_STACK (*klauses, (unsigned) size); - PUSH_STACK (*klauses, LEARNED_FLAG); - for (all_stack (unsigned, lit, cadical_kitten->klause)) - PUSH_STACK (*klauses, lit); - if (aux) - for (all_stack (unsigned, ref, cadical_kitten->resolved)) - PUSH_STACK (*klauses, ref); - connect_new_klause (cadical_kitten, res); - cadical_kitten->learned = true; - cadical_kitten->statistics.learned++; - return res; -} - -void cadical_kitten_clear (cadical_kitten *cadical_kitten) { - LOG ("clear cadical_kitten of size %zu", cadical_kitten->size); - - CADICAL_assert (EMPTY_STACK (cadical_kitten->analyzed)); - CADICAL_assert (EMPTY_STACK (cadical_kitten->eclause)); - CADICAL_assert (EMPTY_STACK (cadical_kitten->resolved)); - - CLEAR_STACK (cadical_kitten->assumptions); - CLEAR_STACK (cadical_kitten->core); - CLEAR_STACK (cadical_kitten->klause); - CLEAR_STACK (cadical_kitten->klauses); - CLEAR_STACK (cadical_kitten->trail); - CLEAR_STACK (cadical_kitten->units); - CLEAR_STACK (cadical_kitten->clause); - CLEAR_STACK (cadical_kitten->prime[0]); - CLEAR_STACK (cadical_kitten->prime[1]); - - for (all_kits (kit)) - CLEAR_STACK (KATCHES (kit)); - - while (!EMPTY_STACK (cadical_kitten->export_)) - cadical_kitten->import[POP_STACK (cadical_kitten->export_)] = 0; - - const size_t lits = cadical_kitten->size; - const unsigned vars = lits / 2; - -#ifndef CADICAL_NDEBUG - for (unsigned i = 0; i < vars; i++) - CADICAL_assert (!cadical_kitten->marks[i]); -#endif - - memset (cadical_kitten->phases, 0, vars); - memset (cadical_kitten->values, 0, lits); - memset (cadical_kitten->failed, 0, lits); - memset (cadical_kitten->vars, 0, vars); - - clear_cadical_kitten (cadical_kitten); -} - -void cadical_kitten_release (cadical_kitten *cadical_kitten) { - RELEASE_STACK (cadical_kitten->analyzed); - RELEASE_STACK (cadical_kitten->assumptions); - RELEASE_STACK (cadical_kitten->core); - RELEASE_STACK (cadical_kitten->eclause); - RELEASE_STACK (cadical_kitten->export_); - RELEASE_STACK (cadical_kitten->klause); - RELEASE_STACK (cadical_kitten->klauses); - RELEASE_STACK (cadical_kitten->resolved); - RELEASE_STACK (cadical_kitten->trail); - RELEASE_STACK (cadical_kitten->units); - RELEASE_STACK (cadical_kitten->clause); - RELEASE_STACK (cadical_kitten->prime[0]); - RELEASE_STACK (cadical_kitten->prime[1]); - - for (size_t lit = 0; lit < cadical_kitten->size; lit++) - RELEASE_STACK (cadical_kitten->watches[lit]); - - const size_t lits = cadical_kitten->size; - const unsigned vars = lits / 2; - DEALLOC (cadical_kitten->marks, vars); - DEALLOC (cadical_kitten->phases, vars); - DEALLOC (cadical_kitten->values, lits); - DEALLOC (cadical_kitten->failed, lits); - DEALLOC (cadical_kitten->vars, vars); - DEALLOC (cadical_kitten->links, vars); - DEALLOC (cadical_kitten->watches, lits); - DEALLOC (cadical_kitten->import, cadical_kitten->esize); - (void) vars; - - free (cadical_kitten); -} - -static inline void move_to_front (cadical_kitten *cadical_kitten, unsigned idx) { - if (idx == cadical_kitten->queue.last) - return; - LOG ("move to front variable %u", idx); - dequeue (cadical_kitten, idx); - enqueue (cadical_kitten, idx); - CADICAL_assert (cadical_kitten->values[2 * idx]); -} - -static inline void assign (cadical_kitten *cadical_kitten, unsigned lit, unsigned reason) { -#ifdef LOGGING - if (reason == INVALID) - LOG ("assign %u as decision", lit); - else - ROG (reason, "assign %u reason", lit); -#endif - value *values = cadical_kitten->values; - const unsigned not_lit = lit ^ 1; - CADICAL_assert (!values[lit]); - CADICAL_assert (!values[not_lit]); - values[lit] = 1; - values[not_lit] = -1; - const unsigned idx = lit / 2; - const unsigned sign = lit & 1; - cadical_kitten->phases[idx] = sign; - PUSH_STACK (cadical_kitten->trail, lit); - kar *v = cadical_kitten->vars + idx; - v->level = cadical_kitten->level; - if (!v->level) { - CADICAL_assert (reason != INVALID); - klause *c = dereference_klause (cadical_kitten, reason); - if (c->size > 1) { - if (cadical_kitten->antecedents) { - PUSH_STACK (cadical_kitten->resolved, reason); - for (all_literals_in_klause (other, c)) - if (other != lit) { - const unsigned other_idx = other / 2; - const unsigned other_ref = cadical_kitten->vars[other_idx].reason; - CADICAL_assert (other_ref != INVALID); - PUSH_STACK (cadical_kitten->resolved, other_ref); - } - } - PUSH_STACK (cadical_kitten->klause, lit); - reason = cadical_new_learned_klause (cadical_kitten); - CLEAR_STACK (cadical_kitten->resolved); - CLEAR_STACK (cadical_kitten->klause); - } - } - v->reason = reason; - CADICAL_assert (cadical_kitten->unassigned); - cadical_kitten->unassigned--; -} - -static inline unsigned propagate_literal (cadical_kitten *cadical_kitten, unsigned lit) { - LOG ("propagating %u", lit); - value *values = cadical_kitten->values; - CADICAL_assert (values[lit] > 0); - const unsigned not_lit = lit ^ 1; - katches *watches = cadical_kitten->watches + not_lit; - unsigned conflict = INVALID; - unsigned *q = BEGIN_STACK (*watches); - const unsigned *const end_watches = END_STACK (*watches); - unsigned const *p = q; - uint64_t ticks = (((char *) end_watches - (char *) q) >> 7) + 1; - while (p != end_watches) { - const unsigned ref = *q++ = *p++; - klause *c = dereference_klause (cadical_kitten, ref); - CADICAL_assert (c->size > 1); - unsigned *lits = c->lits; - const unsigned other = lits[0] ^ lits[1] ^ not_lit; - const value other_value = values[other]; - ticks++; - if (other_value > 0) - continue; - value replacement_value = -1; - unsigned replacement = INVALID; - const unsigned *const end_lits = lits + c->size; - unsigned *r; - for (r = lits + 2; r != end_lits; r++) { - replacement = *r; - replacement_value = values[replacement]; - if (replacement_value >= 0) - break; - } - if (replacement_value >= 0) { - CADICAL_assert (replacement != INVALID); - ROG (ref, "unwatching %u in", not_lit); - lits[0] = other; - lits[1] = replacement; - *r = not_lit; - watch_klause (cadical_kitten, replacement, ref); - q--; - } else if (other_value < 0) { - ROG (ref, "conflict"); - INC (cadical_kitten_conflicts); - conflict = ref; - break; - } else { - CADICAL_assert (!other_value); - assign (cadical_kitten, other, ref); - } - } - while (p != end_watches) - *q++ = *p++; - SET_END_OF_STACK (*watches, q); - ADD (cadical_kitten_ticks, ticks); - return conflict; -} - -static inline unsigned propagate (cadical_kitten *cadical_kitten) { - CADICAL_assert (cadical_kitten->inconsistent == INVALID); - unsigned propagated = 0; - unsigned conflict = INVALID; - while (conflict == INVALID && - cadical_kitten->propagated < SIZE_STACK (cadical_kitten->trail)) { - if (cadical_kitten->terminator && - cadical_kitten->terminator (cadical_kitten->terminator_data)) { - break; - } - const unsigned lit = PEEK_STACK (cadical_kitten->trail, cadical_kitten->propagated); - conflict = propagate_literal (cadical_kitten, lit); - cadical_kitten->propagated++; - propagated++; - } - ADD (cadical_kitten_propagations, propagated); - return conflict; -} - -static void bump (cadical_kitten *cadical_kitten) { - value *marks = cadical_kitten->marks; - for (all_stack (unsigned, idx, cadical_kitten->analyzed)) { - marks[idx] = 0; - move_to_front (cadical_kitten, idx); - } - check_queue (cadical_kitten); -} - -static inline void unassign (cadical_kitten *cadical_kitten, value *values, unsigned lit) { - const unsigned not_lit = lit ^ 1; - CADICAL_assert (values[lit]); - CADICAL_assert (values[not_lit]); - const unsigned idx = lit / 2; -#ifdef LOGGING - kar *var = cadical_kitten->vars + idx; - cadical_kitten->level = var->level; - LOG ("unassign %u", lit); -#endif - values[lit] = values[not_lit] = 0; - CADICAL_assert (cadical_kitten->unassigned < cadical_kitten->lits / 2); - cadical_kitten->unassigned++; - kink *links = cadical_kitten->links; - kink *link = links + idx; - if (link->stamp > links[cadical_kitten->queue.search].stamp) - update_search (cadical_kitten, idx); -} - -static void backtrack (cadical_kitten *cadical_kitten, unsigned jump) { - check_queue (cadical_kitten); - CADICAL_assert (jump < cadical_kitten->level); - LOG ("back%s to level %u", - (cadical_kitten->level == jump + 1 ? "tracking" : "jumping"), jump); - kar *vars = cadical_kitten->vars; - value *values = cadical_kitten->values; - unsigneds *trail = &cadical_kitten->trail; - while (!EMPTY_STACK (*trail)) { - const unsigned lit = TOP_STACK (*trail); - const unsigned idx = lit / 2; - const unsigned level = vars[idx].level; - if (level == jump) - break; - (void) POP_STACK (*trail); - unassign (cadical_kitten, values, lit); - } - cadical_kitten->propagated = SIZE_STACK (*trail); - cadical_kitten->level = jump; - check_queue (cadical_kitten); -} - -void cadical_completely_backtrack_to_root_level (cadical_kitten *cadical_kitten) { - check_queue (cadical_kitten); - LOG ("completely backtracking to level 0"); - value *values = cadical_kitten->values; - unsigneds *trail = &cadical_kitten->trail; - unsigneds *units = &cadical_kitten->units; -#ifndef CADICAL_NDEBUG - kar *vars = cadical_kitten->vars; -#endif - for (all_stack (unsigned, lit, *trail)) { - CADICAL_assert (vars[lit / 2].level); - unassign (cadical_kitten, values, lit); - } - CLEAR_STACK (*trail); - for (all_stack (unsigned, ref, *units)) { - klause *c = dereference_klause (cadical_kitten, ref); - CADICAL_assert (c->size == 1); - const unsigned unit = c->lits[0]; - const value value = values[unit]; - if (value <= 0) - continue; - unassign (cadical_kitten, values, unit); - } - cadical_kitten->propagated = 0; - cadical_kitten->level = 0; - check_queue (cadical_kitten); -} - -static void analyze (cadical_kitten *cadical_kitten, unsigned conflict) { - CADICAL_assert (cadical_kitten->level); - CADICAL_assert (cadical_kitten->inconsistent == INVALID); - CADICAL_assert (EMPTY_STACK (cadical_kitten->analyzed)); - CADICAL_assert (EMPTY_STACK (cadical_kitten->resolved)); - CADICAL_assert (EMPTY_STACK (cadical_kitten->klause)); - PUSH_STACK (cadical_kitten->klause, INVALID); - unsigned reason = conflict; - value *marks = cadical_kitten->marks; - const kar *const vars = cadical_kitten->vars; - const unsigned level = cadical_kitten->level; - unsigned const *p = END_STACK (cadical_kitten->trail); - unsigned open = 0, jump = 0, size = 1, uip; - for (;;) { - CADICAL_assert (reason != INVALID); - klause *c = dereference_klause (cadical_kitten, reason); - CADICAL_assert (c); - ROG (reason, "analyzing"); - PUSH_STACK (cadical_kitten->resolved, reason); - for (all_literals_in_klause (lit, c)) { - const unsigned idx = lit / 2; - if (marks[idx]) - continue; - CADICAL_assert (cadical_kitten->values[lit] < 0); - LOG ("analyzed %u", lit); - marks[idx] = true; - PUSH_STACK (cadical_kitten->analyzed, idx); - const kar *const v = vars + idx; - const unsigned tmp = v->level; - if (tmp < level) { - if (tmp > jump) { - jump = tmp; - if (size > 1) { - const unsigned other = PEEK_STACK (cadical_kitten->klause, 1); - POKE_STACK (cadical_kitten->klause, 1, lit); - lit = other; - } - } - PUSH_STACK (cadical_kitten->klause, lit); - size++; - } else - open++; - } - unsigned idx; - do { - CADICAL_assert (BEGIN_STACK (cadical_kitten->trail) < p); - uip = *--p; - } while (!marks[idx = uip / 2]); - CADICAL_assert (open); - if (!--open) - break; - reason = vars[idx].reason; - } - const unsigned not_uip = uip ^ 1; - LOG ("first UIP %u jump level %u size %u", not_uip, jump, size); - POKE_STACK (cadical_kitten->klause, 0, not_uip); - bump (cadical_kitten); - CLEAR_STACK (cadical_kitten->analyzed); - const unsigned learned_ref = cadical_new_learned_klause (cadical_kitten); - CLEAR_STACK (cadical_kitten->resolved); - CLEAR_STACK (cadical_kitten->klause); - backtrack (cadical_kitten, jump); - assign (cadical_kitten, not_uip, learned_ref); -} - -static void failing (cadical_kitten *cadical_kitten) { - CADICAL_assert (cadical_kitten->inconsistent == INVALID); - CADICAL_assert (!EMPTY_STACK (cadical_kitten->assumptions)); - CADICAL_assert (EMPTY_STACK (cadical_kitten->analyzed)); - CADICAL_assert (EMPTY_STACK (cadical_kitten->resolved)); - CADICAL_assert (EMPTY_STACK (cadical_kitten->klause)); - LOG ("analyzing failing assumptions"); - const value *const values = cadical_kitten->values; - const kar *const vars = cadical_kitten->vars; - unsigned failed_clashing = INVALID; - unsigned first_failed = INVALID; - unsigned failed_unit = INVALID; - for (all_stack (unsigned, lit, cadical_kitten->assumptions)) { - if (values[lit] >= 0) - continue; - if (first_failed == INVALID) - first_failed = lit; - const unsigned failed_idx = lit / 2; - const kar *const failed_var = vars + failed_idx; - if (!failed_var->level) { - failed_unit = lit; - break; - } - if (failed_clashing == INVALID && failed_var->reason == INVALID) - failed_clashing = lit; - } - unsigned failed; - if (failed_unit != INVALID) - failed = failed_unit; - else if (failed_clashing != INVALID) - failed = failed_clashing; - else - failed = first_failed; - CADICAL_assert (failed != INVALID); - const unsigned failed_idx = failed / 2; - const kar *const failed_var = vars + failed_idx; - const unsigned failed_reason = failed_var->reason; - LOG ("first failed assumption %u", failed); - cadical_kitten->failed[failed] = true; - - if (failed_unit != INVALID) { - CADICAL_assert (dereference_klause (cadical_kitten, failed_reason)->size == 1); - LOG ("root-level falsified assumption %u", failed); - cadical_kitten->failing = failed_reason; - ROG (cadical_kitten->failing, "failing reason"); - return; - } - - const unsigned not_failed = failed ^ 1; - if (failed_clashing != INVALID) { - LOG ("clashing with negated assumption %u", not_failed); - cadical_kitten->failed[not_failed] = true; - CADICAL_assert (cadical_kitten->failing == INVALID); - return; - } - - value *marks = cadical_kitten->marks; - CADICAL_assert (!marks[failed_idx]); - marks[failed_idx] = true; - PUSH_STACK (cadical_kitten->analyzed, failed_idx); - PUSH_STACK (cadical_kitten->klause, not_failed); - - unsigneds work; - INIT_STACK (work); - - LOGLITS (BEGIN_STACK (cadical_kitten->trail), SIZE_STACK (cadical_kitten->trail), - "trail"); - - CADICAL_assert (SIZE_STACK (cadical_kitten->trail)); - unsigned const *p = END_STACK (cadical_kitten->trail); - unsigned open = 1; - for (;;) { - if (!open) - break; - open--; - unsigned idx, uip; - do { - CADICAL_assert (BEGIN_STACK (cadical_kitten->trail) < p); - uip = *--p; - } while (!marks[idx = uip / 2]); - - const kar *var = vars + idx; - const unsigned reason = var->reason; - if (reason == INVALID) { - unsigned lit = 2 * idx; - if (values[lit] < 0) - lit ^= 1; - LOG ("failed assumption %u", lit); - CADICAL_assert (!cadical_kitten->failed[lit]); - cadical_kitten->failed[lit] = true; - const unsigned not_lit = lit ^ 1; - PUSH_STACK (cadical_kitten->klause, not_lit); - } else { - ROG (reason, "analyzing"); - PUSH_STACK (cadical_kitten->resolved, reason); - klause *c = dereference_klause (cadical_kitten, reason); - for (all_literals_in_klause (other, c)) { - const unsigned other_idx = other / 2; - if (marks[other_idx]) - continue; - CADICAL_assert (other_idx != idx); - marks[other_idx] = true; - CADICAL_assert (values[other]); - if (vars[other_idx].level) - open++; - else - PUSH_STACK (work, other_idx); - PUSH_STACK (cadical_kitten->analyzed, other_idx); - - LOG ("analyzing final literal %u", other ^ 1); - } - } - } - for (size_t next = 0; next < SIZE_STACK (work); next++) { - const unsigned idx = PEEK_STACK (work, next); - const kar *var = vars + idx; - const unsigned reason = var->reason; - if (reason == INVALID) { - unsigned lit = 2 * idx; - if (values[lit] < 0) - lit ^= 1; - LOG ("failed assumption %u", lit); - CADICAL_assert (!cadical_kitten->failed[lit]); - cadical_kitten->failed[lit] = true; - const unsigned not_lit = lit ^ 1; - PUSH_STACK (cadical_kitten->klause, not_lit); - } else { - ROG (reason, "analyzing unit"); - PUSH_STACK (cadical_kitten->resolved, reason); - } - } - - // this is bfs not dfs so it does not work for lrat :/ - /* - for (size_t next = 0; next < SIZE_STACK (cadical_kitten->analyzed); next++) { - const unsigned idx = PEEK_STACK (cadical_kitten->analyzed, next); - CADICAL_assert (marks[idx]); - const kar *var = vars + idx; - const unsigned reason = var->reason; - if (reason == INVALID) { - unsigned lit = 2 * idx; - if (values[lit] < 0) - lit ^= 1; - LOG ("failed assumption %u", lit); - CADICAL_assert (!cadical_kitten->failed[lit]); - cadical_kitten->failed[lit] = true; - const unsigned not_lit = lit ^ 1; - PUSH_STACK (cadical_kitten->klause, not_lit); - } else { - ROG (reason, "analyzing"); - PUSH_STACK (cadical_kitten->resolved, reason); - klause *c = dereference_klause (cadical_kitten, reason); - for (all_literals_in_klause (other, c)) { - const unsigned other_idx = other / 2; - if (other_idx == idx) - continue; - if (marks[other_idx]) - continue; - marks[other_idx] = true; - PUSH_STACK (cadical_kitten->analyzed, other_idx); - LOG ("analyzing final literal %u", other ^ 1); - } - } - } - */ - - for (all_stack (unsigned, idx, cadical_kitten->analyzed)) - CADICAL_assert (marks[idx]), marks[idx] = 0; - CLEAR_STACK (cadical_kitten->analyzed); - - RELEASE_STACK (work); - - const size_t resolved = SIZE_STACK (cadical_kitten->resolved); - CADICAL_assert (resolved); - - if (resolved == 1) { - cadical_kitten->failing = PEEK_STACK (cadical_kitten->resolved, 0); - ROG (cadical_kitten->failing, "reusing as core"); - } else { - cadical_kitten->failing = cadical_new_learned_klause (cadical_kitten); - ROG (cadical_kitten->failing, "new core"); - } - - CLEAR_STACK (cadical_kitten->resolved); - CLEAR_STACK (cadical_kitten->klause); -} - -static void flush_trail (cadical_kitten *cadical_kitten) { - unsigneds *trail = &cadical_kitten->trail; - LOG ("flushing %zu root-level literals from trail", SIZE_STACK (*trail)); - CADICAL_assert (!cadical_kitten->level); - cadical_kitten->propagated = 0; - CLEAR_STACK (*trail); -} - -static int decide (cadical_kitten *cadical_kitten) { - if (!cadical_kitten->level && !EMPTY_STACK (cadical_kitten->trail)) - flush_trail (cadical_kitten); - - const value *const values = cadical_kitten->values; - unsigned decision = INVALID; - const size_t assumptions = SIZE_STACK (cadical_kitten->assumptions); - while (cadical_kitten->level < assumptions) { - unsigned assumption = PEEK_STACK (cadical_kitten->assumptions, cadical_kitten->level); - value value = values[assumption]; - if (value < 0) { - LOG ("found failing assumption %u", assumption); - failing (cadical_kitten); - return 20; - } else if (value > 0) { - - cadical_kitten->level++; - LOG ("pseudo decision level %u for already satisfied assumption %u", - cadical_kitten->level, assumption); - } else { - decision = assumption; - LOG ("using assumption %u as decision", decision); - break; - } - } - - if (!cadical_kitten->unassigned) - return 10; - - if (KITTEN_TICKS >= cadical_kitten->limits.ticks) { - LOG ("ticks limit %" PRIu64 " hit after %" PRIu64 " ticks", - cadical_kitten->limits.ticks, KITTEN_TICKS); - return -1; - } - - if (cadical_kitten->terminator && cadical_kitten->terminator (cadical_kitten->terminator_data)) { - LOG ("terminator requested termination"); - return -1; - } - - if (decision == INVALID) { - unsigned idx = cadical_kitten->queue.search; - const kink *const links = cadical_kitten->links; - for (;;) { - CADICAL_assert (idx != INVALID); - if (!values[2 * idx]) - break; - idx = links[idx].prev; - } - update_search (cadical_kitten, idx); - const unsigned phase = cadical_kitten->phases[idx]; - decision = 2 * idx + phase; - LOG ("decision %u variable %u phase %u", decision, idx, phase); - } - INC (cadical_kitten_decisions); - cadical_kitten->level++; - assign (cadical_kitten, decision, INVALID); - return 0; -} - -static void inconsistent (cadical_kitten *cadical_kitten, unsigned ref) { - CADICAL_assert (ref != INVALID); - CADICAL_assert (cadical_kitten->inconsistent == INVALID); - - if (!cadical_kitten->antecedents) { - cadical_kitten->inconsistent = ref; - ROG (ref, "registering inconsistent virtually empty"); - return; - } - - unsigneds *analyzed = &cadical_kitten->analyzed; - unsigneds *resolved = &cadical_kitten->resolved; - - CADICAL_assert (EMPTY_STACK (*analyzed)); - CADICAL_assert (EMPTY_STACK (*resolved)); - - value *marks = cadical_kitten->marks; - const kar *const vars = cadical_kitten->vars; - unsigned next = 0; - - for (;;) { - CADICAL_assert (ref != INVALID); - klause *c = dereference_klause (cadical_kitten, ref); - CADICAL_assert (c); - ROG (ref, "analyzing inconsistent"); - PUSH_STACK (*resolved, ref); - for (all_literals_in_klause (lit, c)) { - const unsigned idx = lit / 2; - CADICAL_assert (!vars[idx].level); - if (marks[idx]) - continue; - CADICAL_assert (cadical_kitten->values[lit] < 0); - LOG ("analyzed %u", lit); - marks[idx] = true; - PUSH_STACK (cadical_kitten->analyzed, idx); - } - if (next == SIZE_STACK (cadical_kitten->analyzed)) - break; - const unsigned idx = PEEK_STACK (cadical_kitten->analyzed, next); - next++; - const kar *const v = vars + idx; - CADICAL_assert (!v->level); - ref = v->reason; - } - CADICAL_assert (EMPTY_STACK (cadical_kitten->klause)); - ref = cadical_new_learned_klause (cadical_kitten); - ROG (ref, "registering final inconsistent empty"); - cadical_kitten->inconsistent = ref; - - for (all_stack (unsigned, idx, *analyzed)) - marks[idx] = 0; - - CLEAR_STACK (*analyzed); - CLEAR_STACK (*resolved); -} - -static int propagate_units (cadical_kitten *cadical_kitten) { - if (cadical_kitten->inconsistent != INVALID) - return 20; - - if (EMPTY_STACK (cadical_kitten->units)) { - LOG ("no root level unit clauses"); - return 0; - } - - LOG ("propagating %zu root level unit clauses", - SIZE_STACK (cadical_kitten->units)); - - const value *const values = cadical_kitten->values; - - for (size_t next = 0; next < SIZE_STACK (cadical_kitten->units); next++) { - const unsigned ref = PEEK_STACK (cadical_kitten->units, next); - CADICAL_assert (ref != INVALID); - klause *c = dereference_klause (cadical_kitten, ref); - CADICAL_assert (c->size == 1); - ROG (ref, "propagating unit"); - const unsigned unit = c->lits[0]; - const value value = values[unit]; - if (value > 0) - continue; - if (value < 0) { - inconsistent (cadical_kitten, ref); - return 20; - } - assign (cadical_kitten, unit, ref); - } - const unsigned conflict = propagate (cadical_kitten); - if (conflict == INVALID) - return 0; - inconsistent (cadical_kitten, conflict); - return 20; -} - -/*------------------------------------------------------------------------*/ - -static klause *begin_klauses (cadical_kitten *cadical_kitten) { - return (klause *) BEGIN_STACK (cadical_kitten->klauses); -} - -static klause *end_original_klauses (cadical_kitten *cadical_kitten) { - return (klause *) (BEGIN_STACK (cadical_kitten->klauses) + - cadical_kitten->end_original_ref); -} - -static klause *end_klauses (cadical_kitten *cadical_kitten) { - return (klause *) END_STACK (cadical_kitten->klauses); -} - -static klause *next_klause (cadical_kitten *cadical_kitten, klause *c) { - CADICAL_assert (begin_klauses (cadical_kitten) <= c); - CADICAL_assert (c < end_klauses (cadical_kitten)); - unsigned *res = c->lits + c->size; - if (cadical_kitten->antecedents && is_learned_klause (c)) - res += c->aux; - return (klause *) res; -} - -/*------------------------------------------------------------------------*/ - -static void reset_core (cadical_kitten *cadical_kitten) { - LOG ("resetting core clauses"); - size_t reset = 0; - for (all_klauses (c)) - if (is_core_klause (c)) - unset_core_klause (c), reset++; - LOG ("reset %zu core clauses", reset); - CLEAR_STACK (cadical_kitten->core); -} - -static void reset_assumptions (cadical_kitten *cadical_kitten) { - LOG ("reset %zu assumptions", SIZE_STACK (cadical_kitten->assumptions)); - while (!EMPTY_STACK (cadical_kitten->assumptions)) { - const unsigned assumption = POP_STACK (cadical_kitten->assumptions); - cadical_kitten->failed[assumption] = false; - } -#ifndef CADICAL_NDEBUG - for (size_t i = 0; i < cadical_kitten->size; i++) - CADICAL_assert (!cadical_kitten->failed[i]); -#endif - CLEAR_STACK (cadical_kitten->assumptions); - if (cadical_kitten->failing != INVALID) { - ROG (cadical_kitten->failing, "reset failed assumption reason"); - cadical_kitten->failing = INVALID; - } -} - -static void reset_incremental (cadical_kitten *cadical_kitten) { - // if (cadical_kitten->level) - cadical_completely_backtrack_to_root_level (cadical_kitten); - if (!EMPTY_STACK (cadical_kitten->assumptions)) - reset_assumptions (cadical_kitten); - else - CADICAL_assert (cadical_kitten->failing == INVALID); - if (cadical_kitten->status == 21) - reset_core (cadical_kitten); - UPDATE_STATUS (0); -} - -/*------------------------------------------------------------------------*/ - -static bool flip_literal (cadical_kitten *cadical_kitten, unsigned lit) { - INC (cadical_kitten_flip); - signed char *values = cadical_kitten->values; - if (values[lit] < 0) - lit ^= 1; - LOG ("trying to flip value of satisfied literal %u", lit); - CADICAL_assert (values[lit] > 0); - katches *watches = cadical_kitten->watches + lit; - unsigned *q = BEGIN_STACK (*watches); - const unsigned *const end_watches = END_STACK (*watches); - unsigned const *p = q; - uint64_t ticks = (((char *) end_watches - (char *) q) >> 7) + 1; - bool res = true; - while (p != end_watches) { - const unsigned ref = *q++ = *p++; - klause *c = dereference_klause (cadical_kitten, ref); - unsigned *lits = c->lits; - const unsigned other = lits[0] ^ lits[1] ^ lit; - const value other_value = values[other]; - ticks++; - if (other_value > 0) - continue; - value replacement_value = -1; - unsigned replacement = INVALID; - const unsigned *const end_lits = lits + c->size; - unsigned *r; - for (r = lits + 2; r != end_lits; r++) { - replacement = *r; - CADICAL_assert (replacement != lit); - replacement_value = values[replacement]; - CADICAL_assert (replacement_value); - if (replacement_value > 0) - break; - } - if (replacement_value > 0) { - CADICAL_assert (replacement != INVALID); - ROG (ref, "unwatching %u in", lit); - lits[0] = other; - lits[1] = replacement; - *r = lit; - watch_klause (cadical_kitten, replacement, ref); - q--; - } else { - CADICAL_assert (replacement_value < 0); - ROG (ref, "single satisfied"); - res = false; - break; - } - } - while (p != end_watches) - *q++ = *p++; - SET_END_OF_STACK (*watches, q); - ADD (cadical_kitten_ticks, ticks); - if (res) { - LOG ("flipping value of %u", lit); - values[lit] = -1; - const unsigned not_lit = lit ^ 1; - values[not_lit] = 1; - INC (cadical_kitten_flipped); - } else - LOG ("failed to flip value of %u", lit); - return res; -} - -/*------------------------------------------------------------------------*/ - -// this cadical specific clause addition avoids copying clauses multiple -// times just to convert literals to unsigned representation. -// -static unsigned int2u (int lit) { - CADICAL_assert (lit != 0); - int idx = abs (lit) - 1; - return (lit < 0) + 2u * (unsigned) idx; -} - -void cadical_kitten_assume (cadical_kitten *cadical_kitten, unsigned elit) { - REQUIRE_INITIALIZED (); - if (cadical_kitten->status) - reset_incremental (cadical_kitten); - const unsigned ilit = import_literal (cadical_kitten, elit); - LOG ("registering assumption %u", ilit); - PUSH_STACK (cadical_kitten->assumptions, ilit); -} - -void cadical_kitten_assume_signed (cadical_kitten *cadical_kitten, int elit) { - unsigned kelit = int2u (elit); - cadical_kitten_assume (cadical_kitten, kelit); -} - -void cadical_kitten_clause_with_id_and_exception (cadical_kitten *cadical_kitten, unsigned id, - size_t size, - const unsigned *elits, - unsigned except) { - REQUIRE_INITIALIZED (); - if (cadical_kitten->status) - reset_incremental (cadical_kitten); - CADICAL_assert (EMPTY_STACK (cadical_kitten->klause)); - const unsigned *const end = elits + size; - for (const unsigned *p = elits; p != end; p++) { - const unsigned elit = *p; - if (elit == except) - continue; - const unsigned ilit = import_literal (cadical_kitten, elit); - CADICAL_assert (ilit < cadical_kitten->lits); - const unsigned iidx = ilit / 2; - if (cadical_kitten->marks[iidx]) - INVALID_API_USAGE ("variable '%u' of literal '%u' occurs twice", - elit / 2, elit); - cadical_kitten->marks[iidx] = true; - PUSH_STACK (cadical_kitten->klause, ilit); - } - for (unsigned *p = cadical_kitten->klause.begin; p != cadical_kitten->klause.end; p++) - cadical_kitten->marks[*p / 2] = false; - new_original_klause (cadical_kitten, id); - CLEAR_STACK (cadical_kitten->klause); -} - -void citten_clause_with_id_and_exception (cadical_kitten *cadical_kitten, unsigned id, - size_t size, const int *elits, - unsigned except) { - REQUIRE_INITIALIZED (); - if (cadical_kitten->status) - reset_incremental (cadical_kitten); - CADICAL_assert (EMPTY_STACK (cadical_kitten->klause)); - const int *const end = elits + size; - for (const int *p = elits; p != end; p++) { - const unsigned elit = int2u (*p); // this is the conversion - if (elit == except) - continue; - const unsigned ilit = import_literal (cadical_kitten, elit); - CADICAL_assert (ilit < cadical_kitten->lits); - const unsigned iidx = ilit / 2; - if (cadical_kitten->marks[iidx]) - INVALID_API_USAGE ("variable '%u' of literal '%u' occurs twice", - elit / 2, elit); - cadical_kitten->marks[iidx] = true; - PUSH_STACK (cadical_kitten->klause, ilit); - } - for (unsigned *p = cadical_kitten->klause.begin; p != cadical_kitten->klause.end; p++) - cadical_kitten->marks[*p / 2] = false; - new_original_klause (cadical_kitten, id); - CLEAR_STACK (cadical_kitten->klause); -} - -void citten_clause_with_id_and_equivalence (cadical_kitten *cadical_kitten, unsigned id, - size_t size, const int *elits, - unsigned lit, unsigned other) { - REQUIRE_INITIALIZED (); - if (cadical_kitten->status) - reset_incremental (cadical_kitten); - CADICAL_assert (EMPTY_STACK (cadical_kitten->klause)); - bool sat = false; - const int *const end = elits + size; - for (const int *p = elits; p != end; p++) { - const unsigned elit = int2u (*p); // this is the conversion - if (elit == (lit ^ 1u) || elit == (other ^ 1u)) - continue; - if (elit == lit || elit == other) { - sat = true; - break; - } - const unsigned ilit = import_literal (cadical_kitten, elit); - CADICAL_assert (ilit < cadical_kitten->lits); - const unsigned iidx = ilit / 2; - if (cadical_kitten->marks[iidx]) - INVALID_API_USAGE ("variable '%u' of literal '%u' occurs twice", - elit / 2, elit); - cadical_kitten->marks[iidx] = true; - PUSH_STACK (cadical_kitten->klause, ilit); - } - for (unsigned *p = cadical_kitten->klause.begin; p != cadical_kitten->klause.end; p++) - cadical_kitten->marks[*p / 2] = false; - if (!sat) - new_original_klause (cadical_kitten, id); - CLEAR_STACK (cadical_kitten->klause); -} - -void cadical_kitten_clause (cadical_kitten *cadical_kitten, size_t size, unsigned *elits) { - cadical_kitten_clause_with_id_and_exception (cadical_kitten, INVALID, size, elits, - INVALID); -} - -void citten_clause_with_id (cadical_kitten *cadical_kitten, unsigned id, size_t size, - int *elits) { - citten_clause_with_id_and_exception (cadical_kitten, id, size, elits, INVALID); -} - -void cadical_kitten_unit (cadical_kitten *cadical_kitten, unsigned lit) { - cadical_kitten_clause (cadical_kitten, 1, &lit); -} - -void cadical_kitten_binary (cadical_kitten *cadical_kitten, unsigned a, unsigned b) { - unsigned clause[2] = {a, b}; - cadical_kitten_clause (cadical_kitten, 2, clause); -} - -int cadical_kitten_solve (cadical_kitten *cadical_kitten) { - REQUIRE_INITIALIZED (); - if (cadical_kitten->status) - reset_incremental (cadical_kitten); - else // if (cadical_kitten->level) - cadical_completely_backtrack_to_root_level (cadical_kitten); - - LOG ("starting solving under %zu assumptions", - SIZE_STACK (cadical_kitten->assumptions)); - - INC (cadical_kitten_solved); - - int res = propagate_units (cadical_kitten); - while (!res) { - const unsigned conflict = propagate (cadical_kitten); - if (cadical_kitten->terminator && - cadical_kitten->terminator (cadical_kitten->terminator_data)) { - LOG ("terminator requested termination"); - res = -1; - break; - } - if (conflict != INVALID) { - if (cadical_kitten->level) - analyze (cadical_kitten, conflict); - else { - inconsistent (cadical_kitten, conflict); - res = 20; - } - } else - res = decide (cadical_kitten); - } - - if (res < 0) - res = 0; - - if (!res && !EMPTY_STACK (cadical_kitten->assumptions)) - reset_assumptions (cadical_kitten); - - UPDATE_STATUS (res); - - if (res == 10) - INC (cadical_kitten_sat); - else if (res == 20) - INC (cadical_kitten_unsat); - else - INC (cadical_kitten_unknown); - - LOG ("finished solving with result %d", res); - - return res; -} - -int cadical_kitten_status (cadical_kitten *cadical_kitten) { return cadical_kitten->status; } - -unsigned cadical_kitten_compute_clausal_core (cadical_kitten *cadical_kitten, - uint64_t *learned_ptr) { - REQUIRE_STATUS (20); - - if (!cadical_kitten->antecedents) - INVALID_API_USAGE ("antecedents not tracked"); - - LOG ("computing clausal core"); - - unsigneds *resolved = &cadical_kitten->resolved; - CADICAL_assert (EMPTY_STACK (*resolved)); - - unsigned original = 0; - uint64_t learned = 0; - - unsigned reason_ref = cadical_kitten->inconsistent; - - if (reason_ref == INVALID) { - CADICAL_assert (!EMPTY_STACK (cadical_kitten->assumptions)); - reason_ref = cadical_kitten->failing; - if (reason_ref == INVALID) { - LOG ("assumptions mutually inconsistent"); - //goto DONE; - if (learned_ptr) - *learned_ptr = learned; - - LOG ("clausal core of %u original clauses", original); - LOG ("clausal core of %" PRIu64 " learned clauses", learned); - cadical_kitten->statistics.original = original; - cadical_kitten->statistics.learned = 0; - UPDATE_STATUS (21); - - return original; - } - } - - PUSH_STACK (*resolved, reason_ref); - unsigneds *core = &cadical_kitten->core; - CADICAL_assert (EMPTY_STACK (*core)); - - while (!EMPTY_STACK (*resolved)) { - const unsigned c_ref = POP_STACK (*resolved); - if (c_ref == INVALID) { - const unsigned d_ref = POP_STACK (*resolved); - ROG (d_ref, "core[%zu]", SIZE_STACK (*core)); - PUSH_STACK (*core, d_ref); - klause *d = dereference_klause (cadical_kitten, d_ref); - CADICAL_assert (!is_core_klause (d)); - set_core_klause (d); - if (is_learned_klause (d)) - learned++; - else - original++; - } else { - klause *c = dereference_klause (cadical_kitten, c_ref); - if (is_core_klause (c)) - continue; - PUSH_STACK (*resolved, c_ref); - PUSH_STACK (*resolved, INVALID); - ROG (c_ref, "analyzing antecedent core"); - if (!is_learned_klause (c)) - continue; - for (all_antecedents (d_ref, c)) { - klause *d = dereference_klause (cadical_kitten, d_ref); - if (!is_core_klause (d)) - PUSH_STACK (*resolved, d_ref); - } - } - } - - //DONE: - - if (learned_ptr) - *learned_ptr = learned; - - LOG ("clausal core of %u original clauses", original); - LOG ("clausal core of %" PRIu64 " learned clauses", learned); - cadical_kitten->statistics.original = original; - cadical_kitten->statistics.learned = 0; - UPDATE_STATUS (21); - - return original; -} - -void cadical_kitten_traverse_core_ids (cadical_kitten *cadical_kitten, void *state, - void (*traverse) (void *, unsigned)) { - REQUIRE_STATUS (21); - - LOG ("traversing core of original clauses"); - - unsigned traversed = 0; - - for (all_original_klauses (c)) { - // only happens for 'true' incremental calls, i.e. if add happens after - // solve - if (is_learned_klause (c)) - continue; - if (!is_core_klause (c)) - continue; - ROG (reference_klause (cadical_kitten, c), "traversing"); - traverse (state, c->aux); - traversed++; - } - - LOG ("traversed %u original core clauses", traversed); - (void) traversed; - - CADICAL_assert (cadical_kitten->status == 21); -} - -void cadical_kitten_traverse_core_clauses (cadical_kitten *cadical_kitten, void *state, - void (*traverse) (void *, bool, size_t, - const unsigned *)) { - REQUIRE_STATUS (21); - - LOG ("traversing clausal core"); - - unsigned traversed = 0; - - for (all_stack (unsigned, c_ref, cadical_kitten->core)) { - klause *c = dereference_klause (cadical_kitten, c_ref); - CADICAL_assert (is_core_klause (c)); - const bool learned = is_learned_klause (c); - unsigneds *eclause = &cadical_kitten->eclause; - CADICAL_assert (EMPTY_STACK (*eclause)); - for (all_literals_in_klause (ilit, c)) { - const unsigned elit = export_literal (cadical_kitten, ilit); - PUSH_STACK (*eclause, elit); - } - const size_t size = SIZE_STACK (*eclause); - const unsigned *elits = eclause->begin; - ROG (reference_klause (cadical_kitten, c), "traversing"); - traverse (state, learned, size, elits); - CLEAR_STACK (*eclause); - traversed++; - } - - LOG ("traversed %u core clauses", traversed); - (void) traversed; - - CADICAL_assert (cadical_kitten->status == 21); -} - -void cadical_kitten_traverse_core_clauses_with_id ( - cadical_kitten *cadical_kitten, void *state, - void (*traverse) (void *state, unsigned, bool learned, size_t, - const unsigned *)) { - REQUIRE_STATUS (21); - - LOG ("traversing clausal core"); - - unsigned traversed = 0; - - for (all_stack (unsigned, c_ref, cadical_kitten->core)) { - klause *c = dereference_klause (cadical_kitten, c_ref); - CADICAL_assert (is_core_klause (c)); - const bool learned = is_learned_klause (c); - unsigneds *eclause = &cadical_kitten->eclause; - CADICAL_assert (EMPTY_STACK (*eclause)); - for (all_literals_in_klause (ilit, c)) { - const unsigned elit = export_literal (cadical_kitten, ilit); - PUSH_STACK (*eclause, elit); - } - const size_t size = SIZE_STACK (*eclause); - const unsigned *elits = eclause->begin; - ROG (reference_klause (cadical_kitten, c), "traversing"); - unsigned ctag = learned ? 0 : c->aux; - traverse (state, ctag, learned, size, elits); - CLEAR_STACK (*eclause); - traversed++; - } - - LOG ("traversed %u core clauses", traversed); - (void) traversed; - - CADICAL_assert (cadical_kitten->status == 21); -} - -void cadical_kitten_trace_core (cadical_kitten *cadical_kitten, void *state, - void (*trace) (void *, unsigned, unsigned, bool, - size_t, const unsigned *, size_t, - const unsigned *)) { - REQUIRE_STATUS (21); - - LOG ("tracing clausal core"); - - unsigned traced = 0; - - for (all_stack (unsigned, c_ref, cadical_kitten->core)) { - klause *c = dereference_klause (cadical_kitten, c_ref); - CADICAL_assert (is_core_klause (c)); - const bool learned = is_learned_klause (c); - unsigneds *eclause = &cadical_kitten->eclause; - CADICAL_assert (EMPTY_STACK (*eclause)); - for (all_literals_in_klause (ilit, c)) { - const unsigned elit = export_literal (cadical_kitten, ilit); - PUSH_STACK (*eclause, elit); - } - const size_t size = SIZE_STACK (*eclause); - const unsigned *elits = eclause->begin; - - unsigneds *resolved = &cadical_kitten->resolved; - CADICAL_assert (EMPTY_STACK (*resolved)); - if (learned) { - for (all_antecedents (ref, c)) { - PUSH_STACK (*resolved, ref); - } - } - const size_t rsize = SIZE_STACK (*resolved); - const unsigned *rids = resolved->begin; - - unsigned cid = reference_klause (cadical_kitten, c); - unsigned ctag = learned ? 0 : c->aux; - ROG (cid, "tracing"); - trace (state, cid, ctag, learned, size, elits, rsize, rids); - CLEAR_STACK (*eclause); - CLEAR_STACK (*resolved); - traced++; - } - - LOG ("traced %u core clauses", traced); - (void) traced; - - CADICAL_assert (cadical_kitten->status == 21); -} - -void cadical_kitten_shrink_to_clausal_core (cadical_kitten *cadical_kitten) { - REQUIRE_STATUS (21); - - LOG ("shrinking formula to core of original clauses"); - - CLEAR_STACK (cadical_kitten->trail); - - cadical_kitten->unassigned = cadical_kitten->lits / 2; - cadical_kitten->propagated = 0; - cadical_kitten->level = 0; - - update_search (cadical_kitten, cadical_kitten->queue.last); - - memset (cadical_kitten->values, 0, cadical_kitten->lits); - - for (all_kits (lit)) - CLEAR_STACK (KATCHES (lit)); - - CADICAL_assert (cadical_kitten->inconsistent != INVALID); - klause *inconsistent = dereference_klause (cadical_kitten, cadical_kitten->inconsistent); - if (is_learned_klause (inconsistent) || inconsistent->size) { - ROG (cadical_kitten->inconsistent, "resetting inconsistent"); - cadical_kitten->inconsistent = INVALID; - } else - ROG (cadical_kitten->inconsistent, "keeping inconsistent"); - - CLEAR_STACK (cadical_kitten->units); - - klause *begin = begin_klauses (cadical_kitten), *q = begin; - klause const *const end = end_original_klauses (cadical_kitten); -#ifdef LOGGING - unsigned original = 0; -#endif - for (klause *c = begin, *next; c != end; c = next) { - next = next_klause (cadical_kitten, c); - // CADICAL_assert (!is_learned_klause (c)); not necessarily true - if (is_learned_klause (c)) - continue; - if (!is_core_klause (c)) - continue; - unset_core_klause (c); - const unsigned dst = (unsigned *) q - (unsigned *) begin; - const unsigned size = c->size; - if (!size) { - if (cadical_kitten->inconsistent != INVALID) - cadical_kitten->inconsistent = dst; - } else if (size == 1) { - PUSH_STACK (cadical_kitten->units, dst); - ROG (dst, "keeping"); - } else { - watch_klause (cadical_kitten, c->lits[0], dst); - watch_klause (cadical_kitten, c->lits[1], dst); - } - if (c == q) - q = next; - else { - const size_t bytes = (char *) next - (char *) c; - memmove (q, c, bytes); - q = (klause *) ((char *) q + bytes); - } -#ifdef LOGGING - original++; -#endif - } - SET_END_OF_STACK (cadical_kitten->klauses, (unsigned *) q); - cadical_kitten->end_original_ref = SIZE_STACK (cadical_kitten->klauses); - LOG ("end of original clauses at %zu", cadical_kitten->end_original_ref); - LOG ("%u original clauses left", original); - - CLEAR_STACK (cadical_kitten->core); - - UPDATE_STATUS (0); -} - -signed char cadical_kitten_signed_value (cadical_kitten *cadical_kitten, int selit) { - REQUIRE_STATUS (10); - const unsigned elit = int2u (selit); - const unsigned eidx = elit / 2; - if (eidx >= cadical_kitten->evars) - return 0; - unsigned iidx = cadical_kitten->import[eidx]; - if (!iidx) - return 0; - const unsigned ilit = 2 * (iidx - 1) + (elit & 1); - return cadical_kitten->values[ilit]; -} - -signed char cadical_kitten_value (cadical_kitten *cadical_kitten, unsigned elit) { - REQUIRE_STATUS (10); - const unsigned eidx = elit / 2; - if (eidx >= cadical_kitten->evars) - return 0; - unsigned iidx = cadical_kitten->import[eidx]; - if (!iidx) - return 0; - const unsigned ilit = 2 * (iidx - 1) + (elit & 1); - return cadical_kitten->values[ilit]; -} - -signed char cadical_kitten_fixed (cadical_kitten *cadical_kitten, unsigned elit) { - const unsigned eidx = elit / 2; - if (eidx >= cadical_kitten->evars) - return 0; - unsigned iidx = cadical_kitten->import[eidx]; - if (!iidx) - return 0; - iidx--; - const unsigned ilit = 2 * iidx + (elit & 1); - signed char res = cadical_kitten->values[ilit]; - if (!res) - return 0; - kar *v = cadical_kitten->vars + iidx; - if (v->level) - return 0; - return res; -} - -signed char cadical_kitten_fixed_signed (cadical_kitten *cadical_kitten, int elit) { - unsigned kelit = int2u (elit); - return cadical_kitten_fixed (cadical_kitten, kelit); -} - -bool cadical_kitten_flip_literal (cadical_kitten *cadical_kitten, unsigned elit) { - REQUIRE_STATUS (10); - const unsigned eidx = elit / 2; - if (eidx >= cadical_kitten->evars) - return false; - unsigned iidx = cadical_kitten->import[eidx]; - if (!iidx) - return false; - const unsigned ilit = 2 * (iidx - 1) + (elit & 1); - if (cadical_kitten_fixed (cadical_kitten, elit)) - return false; - return flip_literal (cadical_kitten, ilit); -} - -bool cadical_kitten_flip_signed_literal (cadical_kitten *cadical_kitten, int elit) { - REQUIRE_STATUS (10); - unsigned kelit = int2u (elit); - return cadical_kitten_flip_literal (cadical_kitten, kelit); -} - -bool cadical_kitten_failed (cadical_kitten *cadical_kitten, unsigned elit) { - REQUIRE_STATUS (20); - const unsigned eidx = elit / 2; - if (eidx >= cadical_kitten->evars) - return false; - unsigned iidx = cadical_kitten->import[eidx]; - if (!iidx) - return false; - const unsigned ilit = 2 * (iidx - 1) + (elit & 1); - return cadical_kitten->failed[ilit]; -} - -// checks both watches for clauses with only one literal positively -// assigned. if such a clause is found, return false. Otherwise fix watch -// invariant and return true -static bool prime_propagate (cadical_kitten *cadical_kitten, const unsigned idx, - void *state, const bool ignoring, - bool (*ignore) (void *, unsigned)) { - unsigned lit = 2 * idx; - unsigned conflict = INVALID; - value *values = cadical_kitten->values; - for (int i = 0; i < 2; i++) { - if (conflict != INVALID) - break; - lit = lit ^ i; - const unsigned not_lit = lit ^ 1; - katches *watches = cadical_kitten->watches + not_lit; - unsigned *q = BEGIN_STACK (*watches); - const unsigned *const end_watches = END_STACK (*watches); - unsigned const *p = q; - uint64_t ticks = (((char *) end_watches - (char *) q) >> 7) + 1; - while (p != end_watches) { - const unsigned ref = *q++ = *p++; - klause *c = dereference_klause (cadical_kitten, ref); - if (is_learned_klause (c) || ignore (state, c->aux) == ignoring) - continue; - CADICAL_assert (c->size > 1); - unsigned *lits = c->lits; - const unsigned other = lits[0] ^ lits[1] ^ not_lit; - const value other_value = values[other]; - ticks++; - if (other_value > 0) - continue; - value replacement_value = -1; - unsigned replacement = INVALID; - const unsigned *const end_lits = lits + c->size; - unsigned *r; - for (r = lits + 2; r != end_lits; r++) { - replacement = *r; - replacement_value = values[replacement]; - if (replacement_value > 0) - break; - } - if (replacement_value > 0) { - CADICAL_assert (replacement != INVALID); - ROG (ref, "unwatching %u in", not_lit); - lits[0] = other; - lits[1] = replacement; - *r = not_lit; - watch_klause (cadical_kitten, replacement, ref); - q--; - } else { - ROG (ref, "idx %u forced prime by", idx); - conflict = ref; - break; - } - } - while (p != end_watches) - *q++ = *p++; - SET_END_OF_STACK (*watches, q); - ADD (cadical_kitten_ticks, ticks); - } - return conflict == INVALID; -} - -void cadical_kitten_add_prime_implicant (cadical_kitten *cadical_kitten, void *state, int side, - void (*add_implicant) (void *, int, size_t, - const unsigned *)) { - REQUIRE_STATUS (11); - // might be possible in some edge cases - unsigneds *prime = &cadical_kitten->prime[side]; - unsigneds *prime2 = &cadical_kitten->prime[!side]; - CADICAL_assert (!EMPTY_STACK (*prime) || !EMPTY_STACK (*prime2)); - CLEAR_STACK (*prime2); - - for (all_stack (unsigned, lit, *prime)) { - const unsigned not_lit = lit ^ 1; - const unsigned elit = export_literal (cadical_kitten, not_lit); - PUSH_STACK (*prime2, elit); - } - - // adds a clause which will reset cadical_kitten status and backtrack - add_implicant (state, side, SIZE_STACK (*prime2), BEGIN_STACK (*prime2)); - CLEAR_STACK (*prime); - CLEAR_STACK (*prime2); -} - -// computes two prime implicants, only considering clauses based on ignore -// return -1 if no prime implicant has been computed, otherwise returns -// index of shorter implicant. -// TODO does not work if flip has been called beforehand -int cadical_kitten_compute_prime_implicant (cadical_kitten *cadical_kitten, void *state, - bool (*ignore) (void *, unsigned)) { - REQUIRE_STATUS (10); - - value *values = cadical_kitten->values; - kar *vars = cadical_kitten->vars; - unsigneds unassigned; - INIT_STACK (unassigned); - bool limit_hit = 0; - CADICAL_assert (EMPTY_STACK (cadical_kitten->prime[0]) && EMPTY_STACK (cadical_kitten->prime[1])); - for (int i = 0; i < 2; i++) { - const bool ignoring = i; - for (all_stack (unsigned, lit, cadical_kitten->trail)) { - if (KITTEN_TICKS >= cadical_kitten->limits.ticks) { - LOG ("ticks limit %" PRIu64 " hit after %" PRIu64 " ticks", - cadical_kitten->limits.ticks, KITTEN_TICKS); - limit_hit = 1; - break; - } - CADICAL_assert (values[lit] > 0); - const unsigned idx = lit / 2; - const unsigned ref = vars[idx].reason; - CADICAL_assert (vars[idx].level); - klause *c = 0; - if (ref != INVALID) - c = dereference_klause (cadical_kitten, ref); - if (ref == INVALID || is_learned_klause (c) || - ignore (state, c->aux) == ignoring) { - LOG ("non-prime candidate var %d", idx); - if (prime_propagate (cadical_kitten, idx, state, ignoring, ignore)) { - values[lit] = 0; - values[lit ^ 1] = 0; - PUSH_STACK (unassigned, lit); - } else - CADICAL_assert (values[lit] > 0); - } - } - unsigneds *prime = &cadical_kitten->prime[i]; - // push on prime implicant stack. - for (all_kits (lit)) { - if (values[lit] > 0) - PUSH_STACK (*prime, lit); - } - // reassign all literals on - for (all_stack (unsigned, lit, unassigned)) { - CADICAL_assert (!values[lit]); - values[lit] = 1; - values[lit ^ 1] = -1; - } - CLEAR_STACK (unassigned); - } - RELEASE_STACK (unassigned); - - if (limit_hit) { - CLEAR_STACK (cadical_kitten->prime[0]); - CLEAR_STACK (cadical_kitten->prime[1]); - return -1; - } - // the only case when one of the prime implicants is allowed to be empty - // is if ignore returns always true or always false. - CADICAL_assert (!EMPTY_STACK (cadical_kitten->prime[0]) || - !EMPTY_STACK (cadical_kitten->prime[1])); - UPDATE_STATUS (11); - - int res = SIZE_STACK (cadical_kitten->prime[0]) > SIZE_STACK (cadical_kitten->prime[1]); - return res; -} - -static bool contains_blit (cadical_kitten *cadical_kitten, klause *c, const unsigned blit) { - for (all_literals_in_klause (lit, c)) { - if (lit == blit) - return true; - } - return false; -} - -static bool prime_propagate_blit (cadical_kitten *cadical_kitten, const unsigned idx, - const unsigned blit) { - unsigned lit = 2 * idx; - unsigned conflict = INVALID; - value *values = cadical_kitten->values; - LOG ("prime propagating idx %u for blit %u", idx, blit); - for (int i = 0; i < 2; i++) { - if (conflict != INVALID) - break; - lit = lit ^ i; - if (lit == blit) - continue; - const unsigned not_lit = lit ^ 1; - katches *watches = cadical_kitten->watches + not_lit; - unsigned *q = BEGIN_STACK (*watches); - const unsigned *const end_watches = END_STACK (*watches); - unsigned const *p = q; - uint64_t ticks = (((char *) end_watches - (char *) q) >> 7) + 1; - while (p != end_watches) { - const unsigned ref = *q++ = *p++; - klause *c = dereference_klause (cadical_kitten, ref); - if (is_learned_klause (c)) - continue; - ROG (ref, "checking with blit %u", blit); - CADICAL_assert (c->size > 1); - unsigned *lits = c->lits; - const unsigned other = lits[0] ^ lits[1] ^ not_lit; - const value other_value = values[other]; - ticks++; - bool use = other == blit || not_lit == blit; - if (other_value > 0) - continue; - value replacement_value = -1; - unsigned replacement = INVALID; - const unsigned *const end_lits = lits + c->size; - unsigned *r; - for (r = lits + 2; r != end_lits; r++) { - replacement = *r; - replacement_value = values[replacement]; - use = use || replacement == blit; - if (replacement_value > 0) - break; - } - if (replacement_value > 0) { - CADICAL_assert (replacement != INVALID); - ROG (ref, "unwatching %u in", not_lit); - lits[0] = other; - lits[1] = replacement; - *r = not_lit; - watch_klause (cadical_kitten, replacement, ref); - q--; - } else if (!use) { - continue; - } else { - ROG (ref, "idx %u forced prime by", idx); - conflict = ref; - break; - } - } - while (p != end_watches) - *q++ = *p++; - SET_END_OF_STACK (*watches, q); - ADD (cadical_kitten_ticks, ticks); - } - return conflict == INVALID; -} - -static int compute_prime_implicant_for (cadical_kitten *cadical_kitten, unsigned blit) { - value *values = cadical_kitten->values; - kar *vars = cadical_kitten->vars; - unsigneds unassigned; - INIT_STACK (unassigned); - bool limit_hit = false; - CADICAL_assert (EMPTY_STACK (cadical_kitten->prime[0]) && EMPTY_STACK (cadical_kitten->prime[1])); - for (int i = 0; i < 2; i++) { - const unsigned block = blit ^ i; - const bool ignoring = i; - if (prime_propagate_blit (cadical_kitten, block / 2, block)) { - value tmp = values[blit]; - CADICAL_assert (tmp); - values[blit] = 0; - values[blit ^ 1] = 0; - PUSH_STACK (unassigned, tmp > 0 ? blit : blit ^ 1); - PUSH_STACK (cadical_kitten->prime[i], block); // will be negated! - } else - CADICAL_assert (false); - for (all_stack (unsigned, lit, cadical_kitten->trail)) { - if (KITTEN_TICKS >= cadical_kitten->limits.ticks) { - LOG ("ticks limit %" PRIu64 " hit after %" PRIu64 " ticks", - cadical_kitten->limits.ticks, KITTEN_TICKS); - limit_hit = true; - break; - } - if (!values[lit]) - continue; - CADICAL_assert (values[lit]); // not true when flipping is involved - const unsigned idx = lit / 2; - const unsigned ref = vars[idx].reason; - CADICAL_assert (vars[idx].level); - LOG ("non-prime candidate var %d", idx); - if (prime_propagate_blit (cadical_kitten, idx, block)) { - value tmp = values[lit]; - CADICAL_assert (tmp); - values[lit] = 0; - values[lit ^ 1] = 0; - PUSH_STACK (unassigned, tmp > 0 ? lit : lit ^ 1); - } - } - unsigneds *prime = &cadical_kitten->prime[i]; - // push on prime implicant stack. - for (all_kits (lit)) { - if (values[lit] > 0) - PUSH_STACK (*prime, lit); - } - // reassign all literals on - for (all_stack (unsigned, lit, unassigned)) { - CADICAL_assert (!values[lit]); - values[lit] = 1; - values[lit ^ 1] = -1; - } - CLEAR_STACK (unassigned); - } - RELEASE_STACK (unassigned); - - if (limit_hit) { - CLEAR_STACK (cadical_kitten->prime[0]); - CLEAR_STACK (cadical_kitten->prime[1]); - return -1; - } - // the only case when one of the prime implicants is allowed to be empty - // is if ignore returns always true or always false. - CADICAL_assert (!EMPTY_STACK (cadical_kitten->prime[0]) || - !EMPTY_STACK (cadical_kitten->prime[1])); - LOGLITS (BEGIN_STACK (cadical_kitten->prime[0]), SIZE_STACK (cadical_kitten->prime[0]), - "first implicant %u", blit); - LOGLITS (BEGIN_STACK (cadical_kitten->prime[1]), SIZE_STACK (cadical_kitten->prime[1]), - "second implicant %u", blit ^ 1); - UPDATE_STATUS (11); - - int res = SIZE_STACK (cadical_kitten->prime[0]) > SIZE_STACK (cadical_kitten->prime[1]); - return res; -} - -int cadical_kitten_flip_and_implicant_for_signed_literal (cadical_kitten *cadical_kitten, - int elit) { - REQUIRE_STATUS (10); - unsigned kelit = int2u (elit); - if (!cadical_kitten_flip_literal (cadical_kitten, kelit)) { - return -2; - } - const unsigned eidx = kelit / 2; - unsigned iidx = cadical_kitten->import[eidx]; - CADICAL_assert (iidx); - const unsigned ilit = 2 * (iidx - 1) + (kelit & 1); - return compute_prime_implicant_for (cadical_kitten, ilit); -} - -ABC_NAMESPACE_IMPL_END diff --git a/src/sat/cadical/cadical_lidruptracer.cpp b/src/sat/cadical/cadical_lidruptracer.cpp deleted file mode 100644 index db07e36e6f..0000000000 --- a/src/sat/cadical/cadical_lidruptracer.cpp +++ /dev/null @@ -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 &clause, const vector &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 &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 &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 &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 &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 &clause, - const vector &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 &clause, - const vector &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 &) { - 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 &) { - 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 &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 &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 &model) { - if (file->closed ()) - return; - LOG (model, "LIDRUP TRACER tracing conclusion of model"); - lidrup_conclude_sat (model); -} - -void LidrupTracer::conclude_unknown (const vector &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 diff --git a/src/sat/cadical/cadical_limit.cpp b/src/sat/cadical/cadical_limit.cpp deleted file mode 100644 index 69417a4730..0000000000 --- a/src/sat/cadical/cadical_limit.cpp +++ /dev/null @@ -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 diff --git a/src/sat/cadical/cadical_logging.cpp b/src/sat/cadical/cadical_logging.cpp deleted file mode 100644 index 589bf097e0..0000000000 --- a/src/sat/cadical/cadical_logging.cpp +++ /dev/null @@ -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 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 &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 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::const_iterator &begin, - const vector::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 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 &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 diff --git a/src/sat/cadical/cadical_lookahead.cpp b/src/sat/cadical/cadical_lookahead.cpp deleted file mode 100644 index de84858cca..0000000000 --- a/src/sat/cadical/cadical_lookahead.cpp +++ /dev/null @@ -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 Internal::lookahead_populate_locc () { - std::vector 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 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 &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 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 ()); - 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 current_assumptions{assumptions}; - std::vector> 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> 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 cube1{cubes2[j]}; - cube1.push_back (res); - std::vector 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 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 diff --git a/src/sat/cadical/cadical_lratchecker.cpp b/src/sat/cadical/cadical_lratchecker.cpp deleted file mode 100644 index 5f342a3706..0000000000 --- a/src/sat/cadical/cadical_lratchecker.cpp +++ /dev/null @@ -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 &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 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 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 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 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 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 &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 &c, - const vector &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 &c, - const vector &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 &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 &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 &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 &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 e = c; - sort (begin (e), end (e)); - clauses_to_reconstruct[id] = e; -} - -void LratChecker::restore_clause (int64_t id, const vector &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 e = c; - sort (begin (e), end (e)); - const vector &d = clauses_to_reconstruct.find (id)->second; - bool eq = true; - if (c.size () != d.size ()) { - eq = false; - } - - for (std::vector::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 &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 diff --git a/src/sat/cadical/cadical_lrattracer.cpp b/src/sat/cadical/cadical_lrattracer.cpp deleted file mode 100644 index 4da728e18c..0000000000 --- a/src/sat/cadical/cadical_lrattracer.cpp +++ /dev/null @@ -1,206 +0,0 @@ -#include "global.h" - -#include "internal.hpp" - -#include - -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 &clause, - const vector &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 &clause, - const vector &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 &) { - 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 diff --git a/src/sat/cadical/cadical_lucky.cpp b/src/sat/cadical/cadical_lucky.cpp deleted file mode 100644 index d67782db4d..0000000000 --- a/src/sat/cadical/cadical_lucky.cpp +++ /dev/null @@ -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 diff --git a/src/sat/cadical/cadical_message.cpp b/src/sat/cadical/cadical_message.cpp deleted file mode 100644 index 42d8c0e482..0000000000 --- a/src/sat/cadical/cadical_message.cpp +++ /dev/null @@ -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 diff --git a/src/sat/cadical/cadical_minimize.cpp b/src/sat/cadical/cadical_minimize.cpp deleted file mode 100644 index 97af5fbe6b..0000000000 --- a/src/sat/cadical/cadical_minimize.cpp +++ /dev/null @@ -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 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 &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 diff --git a/src/sat/cadical/cadical_occs.cpp b/src/sat/cadical/cadical_occs.cpp deleted file mode 100644 index f79d7a6905..0000000000 --- a/src/sat/cadical/cadical_occs.cpp +++ /dev/null @@ -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 diff --git a/src/sat/cadical/cadical_options.cpp b/src/sat/cadical/cadical_options.cpp deleted file mode 100644 index 88eba53ab3..0000000000 --- a/src/sat/cadical/cadical_options.cpp +++ /dev/null @@ -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 diff --git a/src/sat/cadical/cadical_parse.cpp b/src/sat/cadical/cadical_parse.cpp deleted file mode 100644 index 0668aca7d5..0000000000 --- a/src/sat/cadical/cadical_parse.cpp +++ /dev/null @@ -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 ' 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, ""); - 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, ""); - 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, ""); - 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, ""); - 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 diff --git a/src/sat/cadical/cadical_phases.cpp b/src/sat/cadical/cadical_phases.cpp deleted file mode 100644 index f7e4eaffd8..0000000000 --- a/src/sat/cadical/cadical_phases.cpp +++ /dev/null @@ -1,50 +0,0 @@ -#include "global.h" - -#include "internal.hpp" - -ABC_NAMESPACE_IMPL_START - -namespace CaDiCaL { - -void Internal::copy_phases (vector &dst) { - START (copy); - for (auto i : vars) - dst[i] = phases.saved[i]; - STOP (copy); -} - -void Internal::clear_phases (vector &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 diff --git a/src/sat/cadical/cadical_probe.cpp b/src/sat/cadical/cadical_probe.cpp deleted file mode 100644 index 19ff0961f7..0000000000 --- a/src/sat/cadical/cadical_probe.cpp +++ /dev/null @@ -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 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 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 diff --git a/src/sat/cadical/cadical_profile.cpp b/src/sat/cadical/cadical_profile.cpp deleted file mode 100644 index a95c97661d..0000000000 --- a/src/sat/cadical/cadical_profile.cpp +++ /dev/null @@ -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 diff --git a/src/sat/cadical/cadical_proof.cpp b/src/sat/cadical/cadical_proof.cpp deleted file mode 100644 index 13c9a1a5d5..0000000000 --- a/src/sat/cadical/cadical_proof.cpp +++ /dev/null @@ -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 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 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 &c) { - for (auto const &lit : c) - add_literal (lit); -} - -/*------------------------------------------------------------------------*/ - -void Proof::add_original_clause (int64_t id, bool r, const vector &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 &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 &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 &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 &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 &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 &c, - const vector &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 &c, - const vector &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 &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 &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 &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 &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 &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 &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 &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 &old, - const vector &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 &conclusion) { - LOG (clause, "PROOF conclude unsat"); - for (auto &tracer : tracers) { - tracer->conclude_unsat (con, conclusion); - } -} - -void Proof::conclude_sat (const vector &model) { - LOG (clause, "PROOF conclude sat"); - for (auto &tracer : tracers) { - tracer->conclude_sat (model); - } -} - -void Proof::conclude_unknown (const vector &trail) { - LOG (clause, "PROOF conclude unknown"); - for (auto &tracer : tracers) { - tracer->conclude_unknown (trail); - } -} - -} // namespace CaDiCaL - -ABC_NAMESPACE_IMPL_END diff --git a/src/sat/cadical/cadical_propagate.cpp b/src/sat/cadical/cadical_propagate.cpp deleted file mode 100644 index a4f21ee6a7..0000000000 --- a/src/sat/cadical/cadical_propagate.cpp +++ /dev/null @@ -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 diff --git a/src/sat/cadical/cadical_queue.cpp b/src/sat/cadical/cadical_queue.cpp deleted file mode 100644 index c8b5b8030f..0000000000 --- a/src/sat/cadical/cadical_queue.cpp +++ /dev/null @@ -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 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 diff --git a/src/sat/cadical/cadical_random.cpp b/src/sat/cadical/cadical_random.cpp deleted file mode 100644 index 85d022e8f9..0000000000 --- a/src/sat/cadical/cadical_random.cpp +++ /dev/null @@ -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 -#include -#include -#include -#include -#include -} - -#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 -} - -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 -#ifdef WIN32 -#include -#else -#include -#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 - -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 diff --git a/src/sat/cadical/cadical_reap.cpp b/src/sat/cadical/cadical_reap.cpp deleted file mode 100644 index 4625c44dbf..0000000000 --- a/src/sat/cadical/cadical_reap.cpp +++ /dev/null @@ -1,142 +0,0 @@ -#include "global.h" - -#include "reap.hpp" -#include -#include -#include - -#ifdef _MSC_VER -#include -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 &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 diff --git a/src/sat/cadical/cadical_reduce.cpp b/src/sat/cadical/cadical_reduce.cpp deleted file mode 100644 index cc6f352285..0000000000 --- a/src/sat/cadical/cadical_reduce.cpp +++ /dev/null @@ -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 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 diff --git a/src/sat/cadical/cadical_rephase.cpp b/src/sat/cadical/cadical_rephase.cpp deleted file mode 100644 index 3b2e625266..0000000000 --- a/src/sat/cadical/cadical_rephase.cpp +++ /dev/null @@ -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 diff --git a/src/sat/cadical/cadical_report.cpp b/src/sat/cadical/cadical_report.cpp deleted file mode 100644 index 18271d17e1..0000000000 --- a/src/sat/cadical/cadical_report.cpp +++ /dev/null @@ -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 ...' with '' 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 diff --git a/src/sat/cadical/cadical_resources.cpp b/src/sat/cadical/cadical_resources.cpp deleted file mode 100644 index 9a2b421e5b..0000000000 --- a/src/sat/cadical/cadical_resources.cpp +++ /dev/null @@ -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 -#include - -// clang-format on - -#else - -#include -#include -#include -#include - -#endif - -#include -} - -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 diff --git a/src/sat/cadical/cadical_restart.cpp b/src/sat/cadical/cadical_restart.cpp deleted file mode 100644 index 1c6dc44c80..0000000000 --- a/src/sat/cadical/cadical_restart.cpp +++ /dev/null @@ -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 diff --git a/src/sat/cadical/cadical_restore.cpp b/src/sat/cadical/cadical_restore.cpp deleted file mode 100644 index f9551fb5e9..0000000000 --- a/src/sat/cadical/cadical_restore.cpp +++ /dev/null @@ -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::const_iterator &begin, - const vector::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 diff --git a/src/sat/cadical/cadical_score.cpp b/src/sat/cadical/cadical_score.cpp deleted file mode 100644 index 420357929f..0000000000 --- a/src/sat/cadical/cadical_score.cpp +++ /dev/null @@ -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 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 diff --git a/src/sat/cadical/cadical_shrink.cpp b/src/sat/cadical/cadical_shrink.cpp deleted file mode 100644 index 201c220e78..0000000000 --- a/src/sat/cadical/cadical_shrink.cpp +++ /dev/null @@ -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::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::reverse_iterator &rbegin_block, - std::vector::reverse_iterator &rend_block, - std::vector::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::reverse_iterator &rbegin_block, - const std::vector::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::reverse_iterator &rbegin_block, - const std::vector::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::reverse_iterator &rbegin_lits, - std::vector::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::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::reverse_iterator Internal::minimize_and_shrink_block ( - std::vector::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::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 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 stack; - { - std::vector::size_type i = 1; - for (std::vector::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 diff --git a/src/sat/cadical/cadical_signal.cpp b/src/sat/cadical/cadical_signal.cpp deleted file mode 100644 index 099277f035..0000000000 --- a/src/sat/cadical/cadical_signal.cpp +++ /dev/null @@ -1,144 +0,0 @@ -#include "global.h" - -#include "signal.hpp" -#include "cadical.hpp" -#include "resources.hpp" - -/*------------------------------------------------------------------------*/ - -#include -#include - -/*------------------------------------------------------------------------*/ - -#ifndef WIN32 -extern "C" { -#include -} -#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 diff --git a/src/sat/cadical/cadical_solution.cpp b/src/sat/cadical/cadical_solution.cpp deleted file mode 100644 index 1d7514e42f..0000000000 --- a/src/sat/cadical/cadical_solution.cpp +++ /dev/null @@ -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 diff --git a/src/sat/cadical/cadical_solver.cpp b/src/sat/cadical/cadical_solver.cpp deleted file mode 100644 index df051f4dea..0000000000 --- a/src/sat/cadical/cadical_solver.cpp +++ /dev/null @@ -1,1764 +0,0 @@ -#include "global.h" - -#include "internal.hpp" - -ABC_NAMESPACE_IMPL_START - -/*------------------------------------------------------------------------*/ - -namespace CaDiCaL { - -/*------------------------------------------------------------------------*/ - -// See corresponding header file 'cadical.hpp' (!) for more information. -// -// Again, to avoid confusion, note that, 'cadical.hpp' is the header file of -// this file 'solver.cpp', since we want to call the application and main -// file 'cadical.cpp', while at the same time using 'cadical.hpp' as the -// main header file of the library (and not 'solver.hpp'). - -/*------------------------------------------------------------------------*/ -#ifdef LOGGING - -// Needs to be kept in sync with the color schemes used in 'logging.cpp'. -// -#define api_code blue_code // API call color -#define log_code magenta_code // standard/default logging color -#define emph_code bright_magenta_code // emphasized logging color - -#endif -/*------------------------------------------------------------------------*/ - -// Log state transitions. - -#define STATE(S) \ - do { \ - CADICAL_assert (is_power_of_two (S)); \ - if (_state == S) \ - break; \ - _state = S; \ - LOG ("API enters state %s" #S "%s", tout.emph_code (), \ - tout.normal_code ()); \ - } while (0) - -void Solver::transition_to_steady_state () { - if (state () == CONFIGURING) { - LOG ("API leaves state %sCONFIGURING%s", tout.emph_code (), - tout.normal_code ()); - if (internal->opts.check && internal->opts.checkproof) { - internal->check (); - } - } else if (state () == SATISFIED) { - LOG ("API leaves state %sSATISFIED%s", tout.emph_code (), - tout.normal_code ()); - external->reset_assumptions (); - external->reset_concluded (); - external->reset_constraint (); - } else if (state () == UNSATISFIED) { - LOG ("API leaves state %sUNSATISFIED%s", tout.emph_code (), - tout.normal_code ()); - external->reset_assumptions (); - external->reset_concluded (); - external->reset_constraint (); - } else if (state() == INCONCLUSIVE) { - external->reset_assumptions (); - external->reset_concluded (); - external->reset_constraint (); - } - if (state () != STEADY) - STATE (STEADY); -} - -/*------------------------------------------------------------------------*/ -#ifdef LOGGING -/*------------------------------------------------------------------------*/ - -// The following logging code is useful for debugging mostly (or trying to -// understand what the solver is actually doing). It needs to be enabled -// during configuration using the '-l' option for './configure', which -// forces 'LOGGING' to be defined during compilation. This includes all the -// logging code, which then still needs to enabled during run-time with the -// '-l' or 'log' option. - -static void log_api_call (Internal *internal, const char *name, - const char *suffix) { - Logger::log (internal, "API call %s'%s ()'%s %s", tout.api_code (), name, - tout.log_code (), suffix); -} - -static void log_api_call (Internal *internal, const char *name, int arg, - const char *suffix) { - Logger::log (internal, "API call %s'%s (%d)'%s %s", tout.api_code (), - name, arg, tout.log_code (), suffix); -} - -static void log_api_call (Internal *internal, const char *name, - const char *arg, const char *suffix) { - Logger::log (internal, "API call %s'%s (\"%s\")'%s %s", tout.api_code (), - name, arg, tout.log_code (), suffix); -} - -static void log_api_call (Internal *internal, const char *name, - const char *a1, int a2, const char *s) { - Logger::log (internal, "API call %s'%s (\"%s\", %d)'%s %s", - tout.api_code (), name, a1, a2, tout.log_code (), s); -} - -/*------------------------------------------------------------------------*/ - -// We factored out API call begin/end logging and use overloaded functions. - -static void log_api_call_begin (Internal *internal, const char *name) { - Logger::log_empty_line (internal); - log_api_call (internal, name, "started"); -} - -static void log_api_call_begin (Internal *internal, const char *name, - int arg) { - Logger::log_empty_line (internal); - log_api_call (internal, name, arg, "started"); -} - -static void log_api_call_begin (Internal *internal, const char *name, - const char *arg) { - Logger::log_empty_line (internal); - log_api_call (internal, name, arg, "started"); -} - -static void log_api_call_begin (Internal *internal, const char *name, - const char *arg1, int arg2) { - Logger::log_empty_line (internal); - log_api_call (internal, name, arg1, arg2, "started"); -} - -/*------------------------------------------------------------------------*/ - -static void log_api_call_end (Internal *internal, const char *name) { - log_api_call (internal, name, "succeeded"); -} - -static void log_api_call_end (Internal *internal, const char *name, - int lit) { - log_api_call (internal, name, lit, "succeeded"); -} - -static void log_api_call_end (Internal *internal, const char *name, - const char *arg) { - Logger::log_empty_line (internal); - log_api_call (internal, name, arg, "succeeded"); -} - -static void log_api_call_end (Internal *internal, const char *name, - const char *arg, bool res) { - log_api_call (internal, name, arg, res ? "succeeded" : "failed"); -} - -static void log_api_call_end (Internal *internal, const char *name, - const char *arg, int val, bool res) { - log_api_call (internal, name, arg, val, res ? "succeeded" : "failed"); -} - -static void log_api_call_returns (Internal *internal, const char *name, - bool res) { - log_api_call (internal, name, res ? "returns 'true'" : "returns 'false'"); -} - -static void log_api_call_returns (Internal *internal, const char *name, - int res) { - char fmt[32]; - snprintf (fmt, sizeof fmt, "returns '%d'", res); - log_api_call (internal, name, fmt); -} - -static void log_api_call_returns (Internal *internal, const char *name, - int64_t res) { - char fmt[32]; - snprintf (fmt, sizeof fmt, "returns '%" PRId64 "'", res); - log_api_call (internal, name, fmt); -} - -static void log_api_call_returns (Internal *internal, const char *name, - int lit, int res) { - char fmt[32]; - snprintf (fmt, sizeof fmt, "returns '%d'", res); - log_api_call (internal, name, lit, fmt); -} - -static void log_api_call_returns (Internal *internal, const char *name, - const char *arg, bool res) { - log_api_call (internal, name, arg, - res ? "returns 'true'" : "returns 'false'"); -} - -static void log_api_call_returns (Internal *internal, const char *name, - int lit, bool res) { - log_api_call (internal, name, lit, - res ? "returns 'true'" : "returns 'false'"); -} - -static void log_api_call_returns (Internal *internal, const char *name, - const char *arg, const char *res) { - Logger::log (internal, "API call %s'%s (\"%s\")'%s returns '%s'", - tout.api_code (), name, arg, tout.log_code (), - res ? res : ""); -} - -static void log_api_call_returns (Internal *internal, const char *name, - const char *arg1, int arg2, - const char *res) { - Logger::log (internal, "API call %s'%s (\"%s\", %d)'%s returns '%s'", - tout.api_code (), name, arg1, arg2, tout.log_code (), - res ? res : ""); -} - -/*------------------------------------------------------------------------*/ - -#define LOG_API_CALL_BEGIN(...) \ - do { \ - if (!internal->opts.log) \ - break; \ - log_api_call_begin (internal, __VA_ARGS__); \ - } while (0) - -#define LOG_API_CALL_END(...) \ - do { \ - if (!internal->opts.log) \ - break; \ - log_api_call_end (internal, __VA_ARGS__); \ - } while (0) - -#define LOG_API_CALL_RETURNS(...) \ - do { \ - if (!internal->opts.log) \ - break; \ - log_api_call_returns (internal, __VA_ARGS__); \ - } while (0) - -/*------------------------------------------------------------------------*/ -#else // end of 'then' part of 'ifdef LOGGING' -/*------------------------------------------------------------------------*/ - -#define LOG_API_CALL_BEGIN(...) \ - do { \ - } while (0) -#define LOG_API_CALL_END(...) \ - do { \ - } while (0) -#define LOG_API_CALL_RETURNS(...) \ - do { \ - } while (0) - -/*------------------------------------------------------------------------*/ -#endif // end of 'else' part of 'ifdef LOGGING' -/*------------------------------------------------------------------------*/ - -/*------------------------------------------------------------------------*/ -#ifndef CADICAL_NTRACING -/*------------------------------------------------------------------------*/ - -#define TRACE(...) \ - do { \ - /*if ((this == 0)) break; */ /* gcc-12 produces warning */ \ - if ((internal == 0)) \ - break; \ - LOG_API_CALL_BEGIN (__VA_ARGS__); \ - if (!trace_api_file) \ - break; \ - trace_api_call (__VA_ARGS__); \ - } while (0) - -void Solver::trace_api_call (const char *s0) const { - CADICAL_assert (trace_api_file); - LOG ("TRACE %s", s0); - fprintf (trace_api_file, "%s\n", s0); - fflush (trace_api_file); -} - -void Solver::trace_api_call (const char *s0, int i1) const { - CADICAL_assert (trace_api_file); - LOG ("TRACE %s %d", s0, i1); - fprintf (trace_api_file, "%s %d\n", s0, i1); - fflush (trace_api_file); -} - -void Solver::trace_api_call (const char *s0, const char *s1) const { - CADICAL_assert (trace_api_file); - LOG ("TRACE %s %s", s0, s1); - fprintf (trace_api_file, "%s %s\n", s0, s1); - fflush (trace_api_file); -} - -void Solver::trace_api_call (const char *s0, const char *s1, int i2) const { - CADICAL_assert (trace_api_file); - LOG ("TRACE %s %s %d", s0, s1, i2); - fprintf (trace_api_file, "%s %s %d\n", s0, s1, i2); - fflush (trace_api_file); -} - -/*------------------------------------------------------------------------*/ - -// The global 'tracing_api_calls_through_environment_variable_method' flag -// is used to ensure that only one solver traces to a file. Otherwise the -// method to use an environment variable to point to the trace file is -// bogus, since those different solver instances would all write to the same -// file producing garbage. A more sophisticated solution would use a -// different mechanism to tell the solver to which file to trace to, but in -// our experience it is quite convenient to get traces out of applications -// which use the solver as library by just setting an environment variable -// without requiring to change any application code. -// -static bool tracing_api_calls_through_environment_variable_method; - -/*------------------------------------------------------------------------*/ -#else // CADICAL_NTRACING -/*------------------------------------------------------------------------*/ - -#define TRACE(...) \ - do { \ - } while (0) - -/*------------------------------------------------------------------------*/ -#endif -/*------------------------------------------------------------------------*/ - -static bool tracing_nb_lidrup_env_var_method = false; - -Solver::Solver () { - -#ifndef CADICAL_NTRACING - const char *path = getenv ("CADICAL_API_TRACE"); - if (!path) - path = getenv ("CADICALAPITRACE"); - if (path) { - if (tracing_api_calls_through_environment_variable_method) - FATAL ("can not trace API calls of two solver instances " - "using environment variable 'CADICAL_API_TRACE'"); - if (!(trace_api_file = fopen (path, "w"))) - FATAL ("failed to open file '%s' to trace API calls " - "using environment variable 'CADICAL_API_TRACE'", - path); - close_trace_api_file = true; - tracing_api_calls_through_environment_variable_method = true; - } else { - tracing_api_calls_through_environment_variable_method = false; - close_trace_api_file = false; - trace_api_file = 0; - } -#endif - - adding_clause = false; - adding_constraint = false; - _state = INITIALIZING; - internal = new Internal (); - DeferDeletePtr delete_internal (internal); - TRACE ("init"); - external = new External (internal); - DeferDeletePtr delete_external (external); - STATE (CONFIGURING); -#ifndef CADICAL_NTRACING - if (tracing_api_calls_through_environment_variable_method) - message ("tracing API calls to '%s'", path); -#endif - - const char *lidrup_path = getenv ("CADICAL_LIDRUP_TRACE"); - if (!lidrup_path) - lidrup_path = getenv ("CADICALLIDRUPTRACE"); - if (lidrup_path) { - - // if (tracing_nb_lidrup_env_var_method) - // FATAL ("can not trace LIDRUP of two solver instances " - // "using environment variable 'CADICAL_LIDRUP_TRACE'"); - // Here we use the solver interface to setup non-binary IDRUP tracing to - // the defined file. Options set by the user can and will overwrite - // these settings if neeed be. - set ("lidrup", 1); - set ("binary", 0); - trace_proof (lidrup_path); - tracing_nb_lidrup_env_var_method = true; - } else { - tracing_nb_lidrup_env_var_method = false; - } - - delete_internal.release (); - delete_external.release (); -} - -Solver::~Solver () { - - TRACE ("reset"); - REQUIRE_VALID_OR_SOLVING_STATE (); - STATE (DELETING); - - tracing_nb_lidrup_env_var_method = false; - -#ifdef LOGGING - // - // After deleting 'internal' logging does not work anymore. - // - bool logging = internal->opts.log; - int level = internal->level; - string prefix = internal->prefix; -#endif - - delete internal; - delete external; - -#ifndef CADICAL_NTRACING - if (close_trace_api_file) { - close_trace_api_file = false; - CADICAL_assert (trace_api_file); - CADICAL_assert (tracing_api_calls_through_environment_variable_method); - fclose (trace_api_file); - tracing_api_calls_through_environment_variable_method = false; - } -#endif - -#ifdef LOGGING - // - // Need to log success of this API call manually. - // - if (logging) { - printf ("%s%sLOG %s%d%s API call %s'reset ()'%s succeeded%s\n", - prefix.c_str (), tout.log_code (), tout.emph_code (), level, - tout.log_code (), tout.api_code (), tout.log_code (), - tout.normal_code ()); - fflush (stdout); - } -#endif -} - -/*------------------------------------------------------------------------*/ - -int Solver::vars () { - TRACE ("vars"); - REQUIRE_VALID_OR_SOLVING_STATE (); - int res = external->max_var; - LOG_API_CALL_RETURNS ("vars", res); - return res; -} - -void Solver::reserve (int min_max_var) { - TRACE ("reserve", min_max_var); - REQUIRE_VALID_STATE (); - transition_to_steady_state (); - external->reset_extended (); - external->init (min_max_var); - LOG_API_CALL_END ("reserve", min_max_var); -} - -int Solver::reserve_difference (int number_of_vars) { - TRACE ("reserve_difference", number_of_vars); - REQUIRE_VALID_STATE (); - transition_to_steady_state (); - external->reset_extended (); - int new_max_var = external->max_var + number_of_vars; - external->init (new_max_var); - LOG_API_CALL_END ("reserve_difference", number_of_vars); - return new_max_var; -} - -/*------------------------------------------------------------------------*/ -#ifndef CADICAL_NTRACING - -void Solver::trace_api_calls (FILE *file) { - LOG_API_CALL_BEGIN ("trace_api_calls"); - REQUIRE_VALID_STATE (); - REQUIRE (file != 0, "invalid zero file argument"); - REQUIRE (!tracing_api_calls_through_environment_variable_method, - "already tracing API calls " - "using environment variable 'CADICAL_API_TRACE'"); - REQUIRE (!trace_api_file, "called twice"); - trace_api_file = file; - LOG_API_CALL_END ("trace_api_calls"); - trace_api_call ("init"); -} - -#endif -/*------------------------------------------------------------------------*/ - -bool Solver::is_valid_option (const char *name) { - return Options::has (name); -} - -bool Solver::is_preprocessing_option (const char *name) { - return Options::is_preprocessing_option (name); -} - -bool Solver::is_valid_long_option (const char *arg) { - string name; - int tmp; - return Options::parse_long_option (arg, name, tmp); -} - -int Solver::get (const char *arg) { - REQUIRE_VALID_OR_SOLVING_STATE (); - return internal->opts.get (arg); -} - -bool Solver::set (const char *arg, int val) { - TRACE ("set", arg, val); - REQUIRE_VALID_STATE (); - if (strcmp (arg, "log") && strcmp (arg, "quiet") && - strcmp (arg, "report") && strcmp (arg, "verbose")) { - REQUIRE ( - state () == CONFIGURING, - "can only set option 'set (\"%s\", %d)' right after initialization", - arg, val); - } - bool res = internal->opts.set (arg, val); - LOG_API_CALL_END ("set", arg, val, res); - - return res; -} - -bool Solver::set_long_option (const char *arg) { - LOG_API_CALL_BEGIN ("set", arg); - REQUIRE_VALID_STATE (); - REQUIRE (state () == CONFIGURING, - "can only set option '%s' right after initialization", arg); - bool res; - if (arg[0] != '-' || arg[1] != '-') - res = false; - else { - int val; - string name; - res = Options::parse_long_option (arg, name, val); - if (res) - set (name.c_str (), val); - } - LOG_API_CALL_END ("set", arg, res); - return res; -} - -void Solver::optimize (int arg) { - LOG_API_CALL_BEGIN ("optimize", arg); - REQUIRE_VALID_STATE (); - internal->opts.optimize (arg); - LOG_API_CALL_END ("optimize", arg); -} - -bool Solver::limit (const char *arg, int val) { - TRACE ("limit", arg, val); - REQUIRE_VALID_STATE (); - bool res = internal->limit (arg, val); - LOG_API_CALL_END ("limit", arg, val, res); - return res; -} - -bool Solver::is_valid_limit (const char *arg) { - return Internal::is_valid_limit (arg); -} - -void Solver::prefix (const char *str) { - LOG_API_CALL_BEGIN ("prefix", str); - REQUIRE_VALID_OR_SOLVING_STATE (); - internal->prefix = str; - LOG_API_CALL_END ("prefix", str); -} - -bool Solver::is_valid_configuration (const char *name) { - return Config::has (name); -} - -bool Solver::configure (const char *name) { - TRACE ("configure", name); - LOG_API_CALL_BEGIN ("configure", name); - REQUIRE_VALID_STATE (); - REQUIRE (state () == CONFIGURING, - "can only set configuration '%s' right after initialization", - name); - bool res = Config::set (internal->opts, name); - LOG_API_CALL_END ("configure", name, res); - return res; -} - -/*===== IPASIR BEGIN =====================================================*/ - -void Solver::add (int lit) { - TRACE ("add", lit); - REQUIRE_VALID_STATE (); - if (lit) - REQUIRE_VALID_LIT (lit); - transition_to_steady_state (); - external->add (lit); - adding_clause = lit; - if (adding_clause) - STATE (ADDING); - else if (!adding_constraint) - STATE (STEADY); - LOG_API_CALL_END ("add", lit); -} - -void Solver::clause (int a) { - REQUIRE_VALID_LIT (a); - add (a), add (0); -} - -void Solver::clause (int a, int b) { - REQUIRE_VALID_LIT (a); - REQUIRE_VALID_LIT (b); - add (a), add (b), add (0); -} - -void Solver::clause (int a, int b, int c) { - REQUIRE_VALID_LIT (a); - REQUIRE_VALID_LIT (b); - REQUIRE_VALID_LIT (c); - add (a), add (b), add (c), add (0); -} - -void Solver::clause (int a, int b, int c, int d) { - REQUIRE_VALID_LIT (a); - REQUIRE_VALID_LIT (b); - REQUIRE_VALID_LIT (c); - REQUIRE_VALID_LIT (d); - add (a), add (b), add (c), add (d), add (0); -} - -void Solver::clause (int a, int b, int c, int d, int e) { - REQUIRE_VALID_LIT (a); - REQUIRE_VALID_LIT (b); - REQUIRE_VALID_LIT (c); - REQUIRE_VALID_LIT (d); - REQUIRE_VALID_LIT (e); - add (a), add (b), add (c), add (d), add (e), add (0); -} - -void Solver::clause (const int *lits, size_t size) { - REQUIRE (!size || lits, - "first argument 'lits' zero while second argument 'size' not"); - const int *end = lits + size; - for (const int *p = lits; p != end; p++) { - const int lit = *p; - REQUIRE_VALID_LIT (lit); - add (lit); - } - add (0); -} - -void Solver::clause (const std::vector &lits) { - for (auto lit : lits) { - REQUIRE_VALID_LIT (lit); - add (lit); - } - add (0); -} - -bool Solver::inconsistent () { return internal->unsat; } - -void Solver::constrain (int lit) { - TRACE ("constrain", lit); - REQUIRE_VALID_STATE (); - if (lit) - REQUIRE_VALID_LIT (lit); - transition_to_steady_state (); - external->constrain (lit); - adding_constraint = lit; - if (adding_constraint) - STATE (ADDING); - else if (!adding_clause) - STATE (STEADY); - LOG_API_CALL_END ("constrain", lit); -} - -void Solver::assume (int lit) { - TRACE ("assume", lit); - REQUIRE_VALID_STATE (); - REQUIRE_VALID_LIT (lit); - transition_to_steady_state (); - external->assume (lit); - LOG_API_CALL_END ("assume", lit); -} - -int Solver::lookahead () { - TRACE ("lookahead"); - REQUIRE_VALID_OR_SOLVING_STATE (); - int lit = external->lookahead (); - TRACE ("lookahead"); - return lit; -} - -Solver::CubesWithStatus Solver::generate_cubes (int depth, int min_depth) { - TRACE ("lookahead_cubes"); - REQUIRE_VALID_OR_SOLVING_STATE (); - auto cubes = external->generate_cubes (depth, min_depth); - TRACE ("lookahead_cubes"); - - CubesWithStatus cubes2; - cubes2.status = cubes.status; - cubes2.cubes = cubes.cubes; - return cubes2; -} - -void Solver::reset_assumptions () { - TRACE ("reset_assumptions"); - REQUIRE_VALID_STATE (); - transition_to_steady_state (); - external->reset_assumptions (); - external->reset_concluded (); - LOG_API_CALL_END ("reset_assumptions"); -} - -void Solver::reset_constraint () { - TRACE ("reset_constraint"); - REQUIRE_VALID_STATE (); - transition_to_steady_state (); - external->reset_constraint (); - external->reset_concluded (); - LOG_API_CALL_END ("reset_constraint"); -} - -/*------------------------------------------------------------------------*/ - -int Solver::propagate () { - TRACE ("propagate_assumptions"); - REQUIRE_VALID_STATE (); - transition_to_steady_state (); - const int res = external->propagate_assumptions (); - if (tracing_nb_lidrup_env_var_method) - flush_proof_trace (true); - LOG_API_CALL_RETURNS ("propagate_assumptions", res); - if (res == 10) - STATE (SATISFIED); - else if (res == 20) - STATE (UNSATISFIED); - else - STATE (INCONCLUSIVE); - return res; -} - -void Solver::implied (std::vector &entrailed) { - TRACE ("implied"); - REQUIRE_VALID_STATE (); - REQUIRE (state () == INCONCLUSIVE, - "can only get implied literals only in unknown state"); - external->conclude_unknown (); - external->implied (entrailed); - if (tracing_nb_lidrup_env_var_method) - flush_proof_trace (true); - LOG_API_CALL_RETURNS ("implied", (int) entrailed.size ()); -} - -/*------------------------------------------------------------------------*/ - -int Solver::call_external_solve_and_check_results (bool preprocess_only) { - transition_to_steady_state (); - CADICAL_assert (state () & READY); - STATE (SOLVING); - const int res = external->solve (preprocess_only); - if (res == 10) - STATE (SATISFIED); - else if (res == 20) - STATE (UNSATISFIED); - else - STATE (INCONCLUSIVE); -#if 0 // EXPENSIVE ALTERNATIVE ASSUMPTION CHECKING - // This checks that the set of failed assumptions form a core using the - // external 'copy (...)' function to copy the solver, which can be trusted - // less, since it involves copying the extension stack too. The - // 'External::check_assumptions_failing' is a better alternative and can - // be enabled by options too. We keep this code though to have an - // alternative failed assumption checking available for debugging. - // - if (res == 20 && !external->assumptions.empty ()) { - Solver checker; - // checking restored clauses does not work (because the clauses are not added) - checker.set("checkproof", 1); - checker.set("lrat", 0); - checker.prefix ("checker "); - copy (checker); - checker.set("log", 1); - for (const auto & lit : external->assumptions) - if (failed (lit)) - checker.add (lit), checker.add (0); - if (checker.solve () != 20) - FATAL ("copying assumption checker failed"); - } -#endif - if (!res) { - external->reset_assumptions (); - external->reset_constraint (); - external->reset_concluded (); - } - return res; -} - -int Solver::solve () { - TRACE ("solve"); - REQUIRE_READY_STATE (); - const int res = call_external_solve_and_check_results (false); - LOG_API_CALL_RETURNS ("solve", res); - if (tracing_nb_lidrup_env_var_method) - flush_proof_trace (true); - return res; -} - -int Solver::simplify (int rounds) { - TRACE ("simplify", rounds); - REQUIRE_READY_STATE (); - REQUIRE (rounds >= 0, "negative number of simplification rounds '%d'", - rounds); - internal->limit ("preprocessing", rounds); - const int res = call_external_solve_and_check_results (true); - LOG_API_CALL_RETURNS ("simplify", rounds, res); - return res; -} - -/*------------------------------------------------------------------------*/ - -int Solver::val (int lit) { - TRACE ("val", lit); - REQUIRE_VALID_STATE (); - REQUIRE_VALID_LIT (lit); - REQUIRE (state () == SATISFIED, "can only get value in satisfied state"); - if (!external->extended) - external->extend (); - external->conclude_sat (); - int res = external->ival (lit); - LOG_API_CALL_RETURNS ("val", lit, res); - CADICAL_assert (state () == SATISFIED); - CADICAL_assert (res == lit || res == -lit); - return res; -} - -bool Solver::flip (int lit) { - TRACE ("flip", lit); - REQUIRE_VALID_STATE (); - REQUIRE_VALID_LIT (lit); - REQUIRE (state () == SATISFIED, "can only flip value in satisfied state"); - REQUIRE (!external->propagator, - "can only flip when no external propagator is present"); - bool res = external->flip (lit); - LOG_API_CALL_RETURNS ("flip", lit, res); - CADICAL_assert (state () == SATISFIED); - return res; -} - -bool Solver::flippable (int lit) { - TRACE ("flippable", lit); - REQUIRE_VALID_STATE (); - REQUIRE_VALID_LIT (lit); - REQUIRE (state () == SATISFIED, "can only flip value in satisfied state"); - REQUIRE (!external->propagator, - "can only flip when no external propagator is present"); - bool res = external->flippable (lit); - LOG_API_CALL_RETURNS ("flippable", lit, res); - CADICAL_assert (state () == SATISFIED); - return res; -} - -bool Solver::failed (int lit) { - TRACE ("failed", lit); - REQUIRE_VALID_STATE (); - REQUIRE_VALID_LIT (lit); - REQUIRE (state () == UNSATISFIED, - "can only get failed assumptions in unsatisfied state"); - bool res = external->failed (lit); - LOG_API_CALL_RETURNS ("failed", lit, res); - CADICAL_assert (state () == UNSATISFIED); - return res; -} - -bool Solver::constraint_failed () { - TRACE ("constraint_failed"); - REQUIRE_VALID_STATE (); - REQUIRE (state () == UNSATISFIED, - "can only determine if constraint failed in unsatisfied state"); - bool res = external->failed_constraint (); - LOG_API_CALL_RETURNS ("constraint_failed", res); - CADICAL_assert (state () == UNSATISFIED); - return res; -} - -int Solver::fixed (int lit) const { - TRACE ("fixed", lit); - REQUIRE_VALID_STATE (); - REQUIRE_VALID_LIT (lit); - int res = external->fixed (lit); - LOG_API_CALL_RETURNS ("fixed", lit, res); - return res; -} - -void Solver::phase (int lit) { - TRACE ("phase", lit); - REQUIRE_VALID_OR_SOLVING_STATE (); - REQUIRE_VALID_LIT (lit); - external->phase (lit); - LOG_API_CALL_END ("phase", lit); -} - -void Solver::unphase (int lit) { - TRACE ("unphase", lit); - REQUIRE_VALID_OR_SOLVING_STATE (); - REQUIRE_VALID_LIT (lit); - external->unphase (lit); - LOG_API_CALL_END ("unphase", lit); -} - -/*------------------------------------------------------------------------*/ - -void Solver::terminate () { - LOG_API_CALL_BEGIN ("terminate"); - REQUIRE_VALID_OR_SOLVING_STATE (); - external->terminate (); - LOG_API_CALL_END ("terminate"); -} - -void Solver::connect_terminator (Terminator *terminator) { - LOG_API_CALL_BEGIN ("connect_terminator"); - REQUIRE_VALID_STATE (); - REQUIRE (terminator, "can not connect zero terminator"); -#ifdef LOGGING - if (external->terminator) - LOG ("connecting new terminator (disconnecting previous one)"); - else - LOG ("connecting new terminator (no previous one)"); -#endif - external->terminator = terminator; - LOG_API_CALL_END ("connect_terminator"); -} - -void Solver::disconnect_terminator () { - LOG_API_CALL_BEGIN ("disconnect_terminator"); - REQUIRE_VALID_STATE (); -#ifdef LOGGING - if (external->terminator) - LOG ("disconnecting previous terminator"); - else - LOG ("ignoring to disconnect terminator (no previous one)"); -#endif - external->terminator = 0; - LOG_API_CALL_END ("disconnect_terminator"); -} - -/*------------------------------------------------------------------------*/ - -void Solver::connect_learner (Learner *learner) { - LOG_API_CALL_BEGIN ("connect_learner"); - REQUIRE_VALID_STATE (); - REQUIRE (learner, "can not connect zero learner"); -#ifdef LOGGING - if (external->learner) - LOG ("connecting new learner (disconnecting previous one)"); - else - LOG ("connecting new learner (no previous one)"); -#endif - external->learner = learner; - LOG_API_CALL_END ("connect_learner"); -} - -void Solver::disconnect_learner () { - LOG_API_CALL_BEGIN ("disconnect_learner"); - REQUIRE_VALID_STATE (); -#ifdef LOGGING - if (external->learner) - LOG ("disconnecting previous learner"); - else - LOG ("ignoring to disconnect learner (no previous one)"); -#endif - external->learner = 0; - LOG_API_CALL_END ("disconnect_learner"); -} - -/*===== IPASIR END =======================================================*/ - -void Solver::connect_fixed_listener ( - FixedAssignmentListener *fixed_listener) { - LOG_API_CALL_BEGIN ("connect_fixed_listener"); - REQUIRE_VALID_STATE (); - REQUIRE (fixed_listener, "can not connect zero fixed listener"); - -#ifdef LOGGING - if (external->fixed_listener) - LOG ("connecting new listener of fixed assignments (disconnecting " - "previous one)"); - else - LOG ("connecting new listener of fixed assigments (no previous one)"); -#endif - if (external->fixed_listener) - disconnect_fixed_listener (); - external->fixed_listener = fixed_listener; - // Listeners are treated as real-time listeners, thus previously found - // fixed assignments are not sent out (would be rather expensive to - // recover it retrospect, see external_propagate.cpp/get_fixed_literals () - // function). - LOG_API_CALL_END ("connect_fixed_listener"); -} - -void Solver::disconnect_fixed_listener () { - LOG_API_CALL_BEGIN ("disconnect_fixed_listener"); - REQUIRE_VALID_STATE (); -#ifdef LOGGING - if (external->fixed_listener) - LOG ("disconnecting previous listener of fixed assignments"); - else - LOG ("ignoring to disconnect listener of fixed assignments (no " - "previous one)"); -#endif - external->fixed_listener = 0; - LOG_API_CALL_END ("disconnect_fixed_listener"); -} - -/*===== IPASIR-UP BEGIN ==================================================*/ - -void Solver::connect_external_propagator (ExternalPropagator *propagator) { - LOG_API_CALL_BEGIN ("connect_external_propagator"); - REQUIRE_VALID_STATE (); - REQUIRE (propagator, "can not connect zero propagator"); -#ifdef LOGGING - if (external->propagator) - LOG ("connecting new external propagator (disconnecting previous one)"); - else - LOG ("connecting new external propagator (no previous one)"); -#endif - if (external->propagator) - disconnect_external_propagator (); - - external->propagator = propagator; - internal->connect_propagator (); - internal->external_prop = true; - internal->external_prop_is_lazy = propagator->is_lazy; - LOG_API_CALL_END ("connect_external_propagator"); -} - -void Solver::disconnect_external_propagator () { - LOG_API_CALL_BEGIN ("disconnect_external_propagator"); - REQUIRE_VALID_STATE (); - -#ifdef LOGGING - if (external->propagator) - LOG ("disconnecting previous external propagator"); - else - LOG ("ignoring to disconnect external propagator (no previous one)"); -#endif - if (external->propagator) - external->reset_observed_vars (); - - external->propagator = 0; - internal->set_tainted_literal (); - internal->external_prop = false; - internal->external_prop_is_lazy = true; - LOG_API_CALL_END ("disconnect_external_propagator"); -} - -void Solver::add_observed_var (int idx) { - TRACE ("observe", idx); - REQUIRE_VALID_OR_SOLVING_STATE (); - REQUIRE_VALID_LIT (idx); - external->add_observed_var (idx); - LOG_API_CALL_END ("observe", idx); -} - -void Solver::remove_observed_var (int idx) { - TRACE ("unobserve", idx); - REQUIRE_VALID_STATE (); - REQUIRE_VALID_LIT (idx); - external->remove_observed_var (idx); - LOG_API_CALL_END ("unobserve", idx); -} - -void Solver::reset_observed_vars () { - TRACE ("reset_observed_vars"); - REQUIRE_VALID_OR_SOLVING_STATE (); - external->reset_observed_vars (); - LOG_API_CALL_END ("reset_observed_vars"); -} - -/*===== IPASIR-UP END ====================================================*/ - -int Solver::active () const { - TRACE ("active"); - REQUIRE_VALID_STATE (); - int res = internal->active (); - LOG_API_CALL_RETURNS ("active", res); - return res; -} - -int64_t Solver::redundant () const { - TRACE ("redundant"); - REQUIRE_VALID_STATE (); - int64_t res = internal->redundant (); - LOG_API_CALL_RETURNS ("redundant", res); - return res; -} - -int64_t Solver::irredundant () const { - TRACE ("irredundant"); - REQUIRE_VALID_STATE (); - int64_t res = internal->irredundant (); - LOG_API_CALL_RETURNS ("irredundant", res); - return res; -} - -/*------------------------------------------------------------------------*/ - -void Solver::freeze (int lit) { - TRACE ("freeze", lit); - REQUIRE_VALID_STATE (); - REQUIRE_VALID_LIT (lit); - external->freeze (lit); - LOG_API_CALL_END ("freeze", lit); -} - -void Solver::melt (int lit) { - TRACE ("melt", lit); - REQUIRE_VALID_STATE (); - REQUIRE_VALID_LIT (lit); - REQUIRE (external->frozen (lit), - "can not melt completely melted literal '%d'", lit); - external->melt (lit); - LOG_API_CALL_END ("melt", lit); -} - -bool Solver::frozen (int lit) const { - TRACE ("frozen", lit); - REQUIRE_VALID_STATE (); - REQUIRE_VALID_LIT (lit); - bool res = external->frozen (lit); - LOG_API_CALL_RETURNS ("frozen", lit, res); - return res; -} - -/*------------------------------------------------------------------------*/ - -bool Solver::trace_proof (FILE *external_file, const char *name) { - TRACE ("trace_proof", name); - REQUIRE_VALID_STATE (); - REQUIRE ( - state () == CONFIGURING, - "can only start proof tracing to '%s' right after initialization", - name); - File *internal_file = File::write (internal, external_file, name); - CADICAL_assert (internal_file); - internal->trace (internal_file); - LOG_API_CALL_RETURNS ("trace_proof", name, true); - return true; -} - -bool Solver::trace_proof (const char *path) { - TRACE ("trace_proof", path); - REQUIRE_VALID_STATE (); - REQUIRE ( - state () == CONFIGURING, - "can only start proof tracing to '%s' right after initialization", - path); - File *internal_file = File::write (internal, path); - bool res = (internal_file != 0); - internal->trace (internal_file); - LOG_API_CALL_RETURNS ("trace_proof", path, res); - return res; -} - -void Solver::flush_proof_trace (bool print_statistics_unless_quiet) { - TRACE ("flush_proof_trace"); - REQUIRE_VALID_STATE (); - REQUIRE (!internal->file_tracers.empty (), "proof is not traced"); - REQUIRE (!internal->file_tracers.back ()->closed (), - "proof trace already closed"); - internal->flush_trace (print_statistics_unless_quiet); - LOG_API_CALL_END ("flush_proof_trace"); -} - -void Solver::close_proof_trace (bool print_statistics_unless_quiet) { - TRACE ("close_proof_trace"); - REQUIRE_VALID_STATE (); - REQUIRE (!internal->file_tracers.empty (), "proof is not traced"); - REQUIRE (!internal->file_tracers.back ()->closed (), - "proof trace already closed"); - internal->close_trace (print_statistics_unless_quiet); - LOG_API_CALL_END ("close_proof_trace"); -} - -/*------------------------------------------------------------------------*/ - -void Solver::connect_proof_tracer (Tracer *tracer, bool antecedents, - bool finalize_clauses) { - LOG_API_CALL_BEGIN ("connect proof tracer"); - REQUIRE_VALID_STATE (); - REQUIRE (state () == CONFIGURING, - "can only start proof tracing to right after initialization"); - REQUIRE (tracer, "can not connect zero tracer"); - internal->connect_proof_tracer (tracer, antecedents, finalize_clauses); - LOG_API_CALL_END ("connect proof tracer"); -} - -void Solver::connect_proof_tracer (InternalTracer *tracer, bool antecedents, - bool finalize_clauses) { - LOG_API_CALL_BEGIN ("connect proof tracer"); - REQUIRE_VALID_STATE (); - REQUIRE (state () == CONFIGURING, - "can only start proof tracing to right after initialization"); - REQUIRE (tracer, "can not connect zero tracer"); - internal->connect_proof_tracer (tracer, antecedents, finalize_clauses); - LOG_API_CALL_END ("connect proof tracer"); -} - -void Solver::connect_proof_tracer (StatTracer *tracer, bool antecedents, - bool finalize_clauses) { - LOG_API_CALL_BEGIN ("connect proof tracer with stats"); - REQUIRE_VALID_STATE (); - REQUIRE (state () == CONFIGURING, - "can only start proof tracing to right after initialization"); - REQUIRE (tracer, "can not connect zero tracer"); - internal->connect_proof_tracer (tracer, antecedents, finalize_clauses); - LOG_API_CALL_END ("connect proof tracer with stats"); -} - -void Solver::connect_proof_tracer (FileTracer *tracer, bool antecedents, - bool finalize_clauses) { - LOG_API_CALL_BEGIN ("connect proof tracer with file"); - REQUIRE_VALID_STATE (); - REQUIRE (state () == CONFIGURING, - "can only start proof tracing right after initialization"); - REQUIRE (tracer, "can not connect zero tracer"); - internal->connect_proof_tracer (tracer, antecedents, finalize_clauses); - LOG_API_CALL_END ("connect proof tracer with file"); -} - -bool Solver::disconnect_proof_tracer (Tracer *tracer) { - LOG_API_CALL_BEGIN ("disconnect proof tracer"); - REQUIRE_VALID_STATE (); - REQUIRE (tracer, "can not disconnect zero tracer"); - bool res = internal->disconnect_proof_tracer (tracer); - LOG_API_CALL_RETURNS ("connect proof tracer", res); - return res; -} - -bool Solver::disconnect_proof_tracer (StatTracer *tracer) { - LOG_API_CALL_BEGIN ("disconnect proof tracer"); - REQUIRE_VALID_STATE (); - REQUIRE (tracer, "can not disconnect zero tracer"); - bool res = internal->disconnect_proof_tracer (tracer); - LOG_API_CALL_RETURNS ("disconnect proof tracer", res); - return res; -} - -bool Solver::disconnect_proof_tracer (FileTracer *tracer) { - LOG_API_CALL_BEGIN ("disconnect proof tracer"); - REQUIRE_VALID_STATE (); - REQUIRE (tracer, "can not disconnect zero tracer"); - bool res = internal->disconnect_proof_tracer (tracer); - LOG_API_CALL_RETURNS ("disconnect proof tracer", res); - return res; -} - -/*------------------------------------------------------------------------*/ - -void Solver::conclude () { - TRACE ("conclude"); - REQUIRE_VALID_STATE (); - REQUIRE (state () == UNSATISFIED || state () == SATISFIED || - state () == INCONCLUSIVE, - "can only conclude in satisfied, unsatisfied or inconclusive state"); - if (state () == UNSATISFIED) - internal->conclude_unsat (); - else if (state () == SATISFIED) - external->conclude_sat (); - else if (state () == INCONCLUSIVE) - external->conclude_unknown (); - CADICAL_assert (state () == UNSATISFIED || state () == SATISFIED || - state () == INCONCLUSIVE); - LOG_API_CALL_END ("conclude"); -} - -/*------------------------------------------------------------------------*/ - -void Solver::build (FILE *file, const char *prefix) { - - CADICAL_assert (file == stdout || file == stderr); - - Terminal *terminal; - - if (file == stdout) - terminal = &tout; - else if (file == stderr) - terminal = &terr; - else - terminal = 0; - - const char *v = CaDiCaL::version (); - const char *i = identifier (); - const char *c = compiler (); - const char *b = date (); - const char *f = flags (); - - CADICAL_assert (v); - - fputs (prefix, file); - if (terminal) - terminal->magenta (); - fputs ("Version ", file); - if (terminal) - terminal->normal (); - fputs (v, file); - if (i) { - if (terminal) - terminal->magenta (); - fputc (' ', file); - fputs (i, file); - if (terminal) - terminal->normal (); - } - fputc ('\n', file); - - if (c) { - fputs (prefix, file); - if (terminal) - terminal->magenta (); - fputs (c, file); - if (f) { - fputc (' ', file); - fputs (f, file); - } - if (terminal) - terminal->normal (); - fputc ('\n', file); - } - - if (b) { - fputs (prefix, file); - if (terminal) - terminal->magenta (); - fputs (b, file); - if (terminal) - terminal->normal (); - fputc ('\n', file); - } - - fflush (file); -} - -const char *Solver::version () { return CaDiCaL::version (); } - -const char *Solver::signature () { return CaDiCaL::signature (); } - -void Solver::options () { - REQUIRE_VALID_STATE (); - internal->opts.print (); -} - -void Solver::usage () { Options::usage (); } - -void Solver::configurations () { Config::usage (); } - -void Solver::statistics () { - if (state () == DELETING) - return; - TRACE ("stats"); - REQUIRE_VALID_OR_SOLVING_STATE (); - internal->print_statistics (); - LOG_API_CALL_END ("stats"); -} - -void Solver::resources () { - if (state () == DELETING) - return; - TRACE ("resources"); - REQUIRE_VALID_OR_SOLVING_STATE (); - internal->print_resource_usage (); - LOG_API_CALL_END ("resources"); -} - -/*------------------------------------------------------------------------*/ - -const char *Solver::read_dimacs (File *file, int &vars, int strict, - bool *incremental, vector *cubes) { - REQUIRE_VALID_STATE (); - REQUIRE (state () == CONFIGURING, - "can only read DIMACS file right after initialization"); - Parser *parser = new Parser (this, file, incremental, cubes); - const char *err = parser->parse_dimacs (vars, strict); - delete parser; - return err; -} - -const char *Solver::read_dimacs (FILE *external_file, const char *name, - int &vars, int strict) { - LOG_API_CALL_BEGIN ("read_dimacs", name); - REQUIRE_VALID_STATE (); - REQUIRE (state () == CONFIGURING, - "can only read DIMACS file right after initialization"); - File *file = File::read (internal, external_file, name); - CADICAL_assert (file); - const char *err = read_dimacs (file, vars, strict); - delete file; - LOG_API_CALL_RETURNS ("read_dimacs", name, err); - return err; -} - -const char *Solver::read_dimacs (const char *path, int &vars, int strict) { - LOG_API_CALL_BEGIN ("read_dimacs", path); - REQUIRE_VALID_STATE (); - REQUIRE (state () == CONFIGURING, - "can only read DIMACS file right after initialization"); - File *file = File::read (internal, path); - if (!file) - return internal->error_message.init ("failed to read DIMACS file '%s'", - path); - const char *err = read_dimacs (file, vars, strict); - delete file; - LOG_API_CALL_RETURNS ("read_dimacs", path, err); - return err; -} - -const char *Solver::read_dimacs (FILE *external_file, const char *name, - int &vars, int strict, bool &incremental, - vector &cubes) { - LOG_API_CALL_BEGIN ("read_dimacs", name); - REQUIRE_VALID_STATE (); - REQUIRE (state () == CONFIGURING, - "can only read DIMACS file right after initialization"); - File *file = File::read (internal, external_file, name); - CADICAL_assert (file); - const char *err = read_dimacs (file, vars, strict, &incremental, &cubes); - delete file; - LOG_API_CALL_RETURNS ("read_dimacs", name, err); - return err; -} - -const char *Solver::read_dimacs (const char *path, int &vars, int strict, - bool &incremental, vector &cubes) { - LOG_API_CALL_BEGIN ("read_dimacs", path); - REQUIRE_VALID_STATE (); - REQUIRE (state () == CONFIGURING, - "can only read DIMACS file right after initialization"); - File *file = File::read (internal, path); - if (!file) - return internal->error_message.init ("failed to read DIMACS file '%s'", - path); - const char *err = read_dimacs (file, vars, strict, &incremental, &cubes); - delete file; - LOG_API_CALL_RETURNS ("read_dimacs", path, err); - return err; -} - -const char *Solver::read_solution (const char *path) { - LOG_API_CALL_BEGIN ("solution", path); - REQUIRE_VALID_STATE (); - File *file = File::read (internal, path); - if (!file) - return internal->error_message.init ( - "failed to read solution file '%s'", path); - Parser *parser = new Parser (this, file, 0, 0); - const char *err = parser->parse_solution (); - delete parser; - delete file; - if (!err) - external->check_assignment (&External::sol); - LOG_API_CALL_RETURNS ("read_solution", path, err); - return err; -} - -/*------------------------------------------------------------------------*/ - -void Solver::dump_cnf () { - TRACE ("dump"); - REQUIRE_INITIALIZED (); - internal->dump (); - LOG_API_CALL_END ("dump"); -} - -/*------------------------------------------------------------------------*/ - -ExternalPropagator *Solver::get_propagator () { - return external->propagator; -} - -bool Solver::observed (int lit) { - TRACE ("observed", lit); - REQUIRE_VALID_OR_SOLVING_STATE (); - REQUIRE_VALID_LIT (lit); - bool res = external->observed (lit); - LOG_API_CALL_RETURNS ("observed", lit, res); - return res; -} - -bool Solver::is_witness (int lit) { - TRACE ("is_witness", lit); - REQUIRE_VALID_OR_SOLVING_STATE (); - REQUIRE_VALID_LIT (lit); - bool res = external->is_witness (lit); - LOG_API_CALL_RETURNS ("is_witness", lit, res); - return res; -} - -bool Solver::is_decision (int lit) { - TRACE ("is_decision", lit); - REQUIRE_VALID_OR_SOLVING_STATE (); - REQUIRE_VALID_LIT (lit); - bool res = external->is_decision (lit); - LOG_API_CALL_RETURNS ("is_decision", lit, res); - return res; -} - -void Solver::force_backtrack (size_t new_level) { - TRACE ("force_backtrack", new_level); - REQUIRE_VALID_OR_SOLVING_STATE (); - external->force_backtrack (new_level); -} - -/*------------------------------------------------------------------------*/ - -bool Solver::traverse_clauses (ClauseIterator &it) const { - LOG_API_CALL_BEGIN ("traverse_clauses"); - REQUIRE_VALID_STATE (); - bool res = external->traverse_all_frozen_units_as_clauses (it) && - internal->traverse_clauses (it) && - internal->traverse_constraint (it); - LOG_API_CALL_RETURNS ("traverse_clauses", res); - return res; -} - -bool Solver::traverse_witnesses_backward (WitnessIterator &it) const { - LOG_API_CALL_BEGIN ("traverse_witnesses_backward"); - REQUIRE_VALID_STATE (); - bool res = external->traverse_all_non_frozen_units_as_witnesses (it) && - external->traverse_witnesses_backward (it); - LOG_API_CALL_RETURNS ("traverse_witnesses_backward", res); - return res; -} - -bool Solver::traverse_witnesses_forward (WitnessIterator &it) const { - LOG_API_CALL_BEGIN ("traverse_witnesses_forward"); - REQUIRE_VALID_STATE (); - bool res = external->traverse_witnesses_forward (it) && - external->traverse_all_non_frozen_units_as_witnesses (it); - LOG_API_CALL_RETURNS ("traverse_witnesses_forward", res); - return res; -} - -/*------------------------------------------------------------------------*/ - -class ClauseCounter : public ClauseIterator { -public: - int vars; - int64_t clauses; - ClauseCounter () : vars (0), clauses (0) {} - bool clause (const vector &c) { - for (const auto &lit : c) { - CADICAL_assert (lit != INT_MIN); - int idx = abs (lit); - if (idx > vars) - vars = idx; - } - clauses++; - return true; - } -}; - -class ClauseWriter : public ClauseIterator { - File *file; - -public: - ClauseWriter (File *f) : file (f) {} - bool clause (const vector &c) { - for (const auto &lit : c) { - if (!file->put (lit)) - return false; - if (!file->put (' ')) - return false; - } - return file->put ("0\n"); - } -}; - -const char *Solver::write_dimacs (const char *path, int min_max_var) { - LOG_API_CALL_BEGIN ("write_dimacs", path, min_max_var); - REQUIRE_VALID_STATE (); -#ifndef CADICAL_QUIET - const double start = internal->time (); -#endif - internal->restore_clauses (); - ClauseCounter counter; - (void) traverse_clauses (counter); - LOG ("found maximal variable %d and %" PRId64 " clauses", counter.vars, - counter.clauses); - File *file = File::write (internal, path); - const char *res = 0; - if (file) { - int actual_max_vars = max (min_max_var, counter.vars); - MSG ("writing %s'p cnf %d %" PRId64 "'%s header", tout.green_code (), - actual_max_vars, counter.clauses, tout.normal_code ()); - file->put ("p cnf "); - file->put (actual_max_vars); - file->put (' '); - file->put (counter.clauses); - file->put ('\n'); - ClauseWriter writer (file); - if (!traverse_clauses (writer)) - res = internal->error_message.init ( - "writing to DIMACS file '%s' failed", path); - delete file; - } else - res = internal->error_message.init ( - "failed to open DIMACS file '%s' for writing", path); -#ifndef CADICAL_QUIET - if (!res) { - const double end = internal->time (); - MSG ("wrote %" PRId64 " clauses in %.2f seconds %s time", - counter.clauses, end - start, - internal->opts.realtime ? "real" : "process"); - } -#endif - LOG_API_CALL_RETURNS ("write_dimacs", path, min_max_var, res); - return res; -} - -/*------------------------------------------------------------------------*/ - -struct WitnessWriter : public WitnessIterator { - File *file; - int64_t witnesses; - WitnessWriter (File *f) : file (f), witnesses (0) {} - bool write (const vector &a) { - for (const auto &lit : a) { - if (!file->put (lit)) - return false; - if (!file->put (' ')) - return false; - } - return file->put ('0'); - } - bool witness (const vector &c, const vector &w, int64_t) { - if (!write (c)) - return false; - if (!file->put (' ')) - return false; - if (!write (w)) - return false; - if (!file->put ('\n')) - return false; - witnesses++; - return true; - } -}; - -const char *Solver::write_extension (const char *path) { - LOG_API_CALL_BEGIN ("write_extension", path); - REQUIRE_VALID_STATE (); - const char *res = 0; -#ifndef CADICAL_QUIET - const double start = internal->time (); -#endif - File *file = File::write (internal, path); - WitnessWriter writer (file); - if (file) { - if (!traverse_witnesses_backward (writer)) - res = internal->error_message.init ( - "writing to DIMACS file '%s' failed", path); - delete file; - } else - res = internal->error_message.init ( - "failed to open extension file '%s' for writing", path); -#ifndef CADICAL_QUIET - if (!res) { - const double end = internal->time (); - MSG ("wrote %" PRId64 " witnesses in %.2f seconds %s time", - writer.witnesses, end - start, - internal->opts.realtime ? "real" : "process"); - } -#endif - LOG_API_CALL_RETURNS ("write_extension", path, res); - return res; -} - -/*------------------------------------------------------------------------*/ - -struct ClauseCopier : public ClauseIterator { - Solver &dst; - -public: - ClauseCopier (Solver &d) : dst (d) {} - bool clause (const vector &c) { - for (const auto &lit : c) - dst.add (lit); - dst.add (0); - return true; - } -}; - -struct WitnessCopier : public WitnessIterator { - External *dst; - -public: - WitnessCopier (External *d) : dst (d) {} - bool witness (const vector &c, const vector &w, int64_t id) { - dst->push_external_clause_and_witness_on_extension_stack (c, w, id); - return true; - } -}; - -void Solver::copy (Solver &other) const { - REQUIRE_READY_STATE (); - REQUIRE (other.state () & CONFIGURING, "target solver already modified"); - internal->opts.copy (other.internal->opts); - ClauseCopier clause_copier (other); - traverse_clauses (clause_copier); - WitnessCopier witness_copier (other.external); - traverse_witnesses_forward (witness_copier); - external->copy_flags (*other.external); -} - -/*------------------------------------------------------------------------*/ - -void Solver::section (const char *title) { - if (state () == DELETING) - return; -#ifdef CADICAL_QUIET - (void) title; -#endif - REQUIRE_INITIALIZED (); - SECTION (title); -} - -void Solver::message (const char *fmt, ...) { - if (state () == DELETING) - return; -#ifdef CADICAL_QUIET - (void) fmt; -#else - REQUIRE_INITIALIZED (); - va_list ap; - va_start (ap, fmt); - internal->vmessage (fmt, ap); - va_end (ap); -#endif -} - -void Solver::message () { - if (state () == DELETING) - return; - REQUIRE_INITIALIZED (); -#ifndef CADICAL_QUIET - internal->message (); -#endif -} - -void Solver::verbose (int level, const char *fmt, ...) { - if (state () == DELETING) - return; - REQUIRE_VALID_OR_SOLVING_STATE (); -#ifdef CADICAL_QUIET - (void) level; - (void) fmt; -#else - va_list ap; - va_start (ap, fmt); - internal->vverbose (level, fmt, ap); - va_end (ap); -#endif -} - -void Solver::error (const char *fmt, ...) { - if (state () == DELETING) - return; - REQUIRE_INITIALIZED (); - va_list ap; - va_start (ap, fmt); - internal->verror (fmt, ap); - va_end (ap); -} - -} // namespace CaDiCaL - -ABC_NAMESPACE_IMPL_END diff --git a/src/sat/cadical/cadical_stable.cpp b/src/sat/cadical/cadical_stable.cpp deleted file mode 100644 index f177139ac0..0000000000 --- a/src/sat/cadical/cadical_stable.cpp +++ /dev/null @@ -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 diff --git a/src/sat/cadical/cadical_stats.cpp b/src/sat/cadical/cadical_stats.cpp deleted file mode 100644 index 8b07745d68..0000000000 --- a/src/sat/cadical/cadical_stats.cpp +++ /dev/null @@ -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 diff --git a/src/sat/cadical/cadical_subsume.cpp b/src/sat/cadical/cadical_subsume.cpp deleted file mode 100644 index b75040ca8b..0000000000 --- a/src/sat/cadical/cadical_subsume.cpp +++ /dev/null @@ -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 &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 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 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 diff --git a/src/sat/cadical/cadical_sweep.cpp b/src/sat/cadical/cadical_sweep.cpp deleted file mode 100644 index 4dca2b4bc6..0000000000 --- a/src/sat/cadical/cadical_sweep.cpp +++ /dev/null @@ -1,1959 +0,0 @@ -#include "global.h" - -#include "internal.hpp" - -ABC_NAMESPACE_IMPL_START - -namespace CaDiCaL { - -Sweeper::Sweeper (Internal *i) - : internal (i), random (internal->opts.seed) { - random += internal->stats.sweep; // different seed every time - internal->init_sweeper (*this); -} - -Sweeper::~Sweeper () { - // this is already called actively - // internal->release_sweeper (this); - return; -} - -#define INVALID64 INT64_MAX -#define INVALID UINT_MAX - -int Internal::sweep_solve () { - START (sweepsolve); - cadical_kitten_randomize_phases (citten); - stats.sweep_solved++; - int res = cadical_kitten_solve (citten); - if (res == 10) - stats.sweep_sat++; - if (res == 20) - stats.sweep_unsat++; - STOP (sweepsolve); - return res; -} - -bool Internal::sweep_flip (int lit) { - START (sweepflip); - bool res = cadical_kitten_flip_signed_literal (citten, lit); - STOP (sweepflip); - return res; -} - -int Internal::sweep_flip_and_implicant (int lit) { - START (sweepimplicant); - int res = cadical_kitten_flip_and_implicant_for_signed_literal (citten, lit); - STOP (sweepimplicant); - return res; -} - -void Internal::sweep_set_cadical_kitten_ticks_limit (Sweeper &sweeper) { - uint64_t remaining = 0; - const uint64_t current = sweeper.current_ticks; - if (current < sweeper.limit.ticks) - remaining = sweeper.limit.ticks - current; - LOG ("'cadical_kitten_ticks' remaining %" PRIu64, remaining); - cadical_kitten_set_ticks_limit (citten, remaining); -} - -void Internal::sweep_update_noccs (Clause *c) { - if (c->redundant) - return; - for (const auto &lit : *c) { - noccs (lit)--; - } -} - -bool Internal::can_sweep_clause (Clause *c) { - if (c->garbage) - return false; - if (!c->redundant) - return true; - return c->size == 2; // && !c->hyper; // could ignore hyper -} - -// essentially do full occurence list as in elim.cpp -void Internal::sweep_dense_mode_and_watch_irredundant () { - reset_watches (); - - init_noccs (); - - // mark satisfied irredundant clauses as garbage - for (const auto &c : clauses) { - if (!can_sweep_clause (c)) - continue; - bool satisfied = false; - for (const auto &lit : *c) { - const signed char tmp = val (lit); - if (tmp <= 0) - continue; - if (tmp > 0) { - satisfied = true; - break; - } - } - if (satisfied) - mark_garbage (c); // forces more precise counts - else { - for (const auto &lit : *c) - noccs (lit)++; - } - } - - init_occs (); - - // Connect irredundant clauses. - // - for (const auto &c : clauses) { - if (!c->garbage) { - for (const auto &lit : *c) - if (active (lit)) - occs (lit).push_back (c); - } else if (c->size == 2) { - if (!c->flushed) { - if (proof) { - c->flushed = true; - proof->delete_clause (c); - } - } - } - } -} - -// go back to two watch scheme -void Internal::sweep_sparse_mode () { - reset_occs (); - reset_noccs (); - init_watches (); - connect_watches (); -} - -// propagate without watches but full occurence list -void Internal::sweep_dense_propagate (Sweeper &sweeper) { - vector &work = sweeper.propagate; - size_t i = 0; - uint64_t &ticks = sweeper.current_ticks; - while (i < work.size ()) { - int lit = work[i++]; - LOG ("sweeping propagation of %d", lit); - CADICAL_assert (val (lit) > 0); - ticks += 1 + cache_lines (occs (-lit).size (), sizeof (Clause *)); - const Occs &ns = occs (-lit); - for (const auto &c : ns) { - ticks++; - if (!can_sweep_clause (c)) - continue; - int unit = 0, satisfied = 0; - for (const auto &other : *c) { - const signed char tmp = val (other); - if (tmp < 0) - continue; - if (tmp > 0) { - satisfied = other; - break; - } - if (unit) - unit = INT_MIN; - else - unit = other; - } - if (satisfied) { - LOG (c, "sweeping propagation of %d finds %d satisfied", lit, - satisfied); - mark_garbage (c); - sweep_update_noccs (c); - } else if (!unit) { - LOG ("empty clause during sweeping propagation of %d", lit); - // need to set conflict = c for lrat - conflict = c; - learn_empty_clause (); - conflict = 0; - break; - } else if (unit != INT_MIN) { - LOG ("new unit %d during sweeping propagation of %d", unit, lit); - build_chain_for_units (unit, c, 0); - assign_unit (unit); - work.push_back (unit); - ticks++; - } - } - if (unsat) - break; - - // not necessary but should help - ticks += 1 + cache_lines (occs (lit).size (), sizeof (Clause *)); - const Occs &ps = occs (lit); - for (const auto &c : ps) { - ticks++; - if (c->garbage) - continue; - // if (c->redundant) // TODO I assume it does not hurt to mark - // everything here continue; - LOG (c, "sweeping propagation of %d produces satisfied", lit); - mark_garbage (c); - sweep_update_noccs (c); - } - } - work.clear (); -} - -bool Internal::cadical_kitten_ticks_limit_hit (Sweeper &sweeper, const char *when) { - const uint64_t current = - cadical_kitten_current_ticks (citten) + sweeper.current_ticks; - if (current >= sweeper.limit.ticks) { - LOG ("'cadical_kitten_ticks' limit of %" PRIu64 " ticks hit after %" PRIu64 - " ticks during %s", - sweeper.limit.ticks, current, when); - return true; - } -#ifndef LOGGING - (void) when; -#endif - return false; -} - -void Internal::init_sweeper (Sweeper &sweeper) { - sweeper.encoded = 0; - enlarge_zero (sweeper.depths, max_var + 1); - sweeper.reprs = new int[2 * max_var + 1]; - sweeper.reprs += max_var; - enlarge_zero (sweeper.prev, max_var + 1); - enlarge_zero (sweeper.next, max_var + 1); - for (const auto &lit : lits) - sweeper.reprs[lit] = lit; - sweeper.first = sweeper.last = 0; - sweeper.current_ticks = - 2 * - clauses - .size (); // initialize with the cost of building full occ list. - sweeper.current_ticks += - 2 + 2 * cache_lines (clauses.size (), sizeof (Clause *)); - CADICAL_assert (!citten); - citten = cadical_kitten_init (); - citten_clear_track_log_terminate (); - - sweep_dense_mode_and_watch_irredundant (); // full occurence list - - if (lrat) { - sweeper.prev_units.push_back (0); - for (const auto &idx : vars) { - sweeper.prev_units.push_back (val (idx) != 0); - } - } - - unsigned completed = stats.sweep_completed; - const unsigned max_completed = 32; - if (completed > max_completed) - completed = max_completed; - - uint64_t vars_limit = opts.sweepvars; - vars_limit <<= completed; - const unsigned max_vars_limit = opts.sweepmaxvars; - if (vars_limit > max_vars_limit) - vars_limit = max_vars_limit; - sweeper.limit.vars = vars_limit; - VERBOSE (3, "sweeper variable limit %u", sweeper.limit.vars); - - uint64_t depth_limit = stats.sweep_completed; - depth_limit += opts.sweepdepth; - const unsigned max_depth = opts.sweepmaxdepth; - if (depth_limit > max_depth) - depth_limit = max_depth; - sweeper.limit.depth = depth_limit; - VERBOSE (3, "sweeper depth limit %u", sweeper.limit.depth); - - uint64_t clause_limit = opts.sweepclauses; - clause_limit <<= completed; - const unsigned max_clause_limit = opts.sweepmaxclauses; - if (clause_limit > max_clause_limit) - clause_limit = max_clause_limit; - sweeper.limit.clauses = clause_limit; - VERBOSE (3, "sweeper clause limit %u", sweeper.limit.clauses); -} - -void Internal::release_sweeper (Sweeper &sweeper) { - - sweeper.reprs -= max_var; - delete[] sweeper.reprs; - - erase_vector (sweeper.depths); - erase_vector (sweeper.prev); - erase_vector (sweeper.next); - erase_vector (sweeper.vars); - erase_vector (sweeper.clause); - erase_vector (sweeper.backbone); - erase_vector (sweeper.partition); - erase_vector (sweeper.prev_units); - for (unsigned i = 0; i < 2; i++) - erase_vector (sweeper.core[i]); - - cadical_kitten_release (citten); - citten = 0; - stats.ticks.sweep += sweeper.current_ticks; - sweep_sparse_mode (); - return; -} - -void Internal::clear_sweeper (Sweeper &sweeper) { - LOG ("clearing sweeping environment"); - sweeper.current_ticks += cadical_kitten_current_ticks (citten); - - citten_clear_track_log_terminate (); - for (auto &idx : sweeper.vars) { - CADICAL_assert (sweeper.depths[idx]); - sweeper.depths[idx] = 0; - } - sweeper.vars.clear (); - for (auto c : sweeper.clauses) { - CADICAL_assert (c->swept); - c->swept = false; - } - sweeper.clauses.clear (); - sweeper.backbone.clear (); - sweeper.partition.clear (); - sweeper.encoded = 0; - sweep_set_cadical_kitten_ticks_limit (sweeper); -} - -int Internal::sweep_repr (Sweeper &sweeper, int lit) { - int res; - { - int prev = lit; - while ((res = sweeper.reprs[prev]) != prev) - prev = res; - } - if (res == lit) - return res; - LOG ("sweeping repr[%d] = %d", lit, res); - { - const int not_res = -res; - int next, prev = lit; - while ((next = sweeper.reprs[prev]) != res) { - const int not_prev = -prev; - sweeper.reprs[not_prev] = not_res; - sweeper.reprs[prev] = res; - prev = next; - } - CADICAL_assert (sweeper.reprs[-prev] == not_res); - } - return res; -} - -void Internal::add_literal_to_environment (Sweeper &sweeper, unsigned depth, - int lit) { - const int repr = sweep_repr (sweeper, lit); - if (repr != lit) - return; - const int idx = abs (lit); - if (sweeper.depths[idx]) - return; - CADICAL_assert (depth < UINT_MAX); - sweeper.depths[idx] = depth + 1; - CADICAL_assert (idx); - sweeper.vars.push_back (idx); - LOG ("sweeping[%u] adding literal %d", depth, lit); -} - -void Internal::sweep_add_clause (Sweeper &sweeper, unsigned depth) { - // TODO: CADICAL_assertion fails, check if this an issue or can be avoided - // CADICAL_assert (sweeper.clause.size () > 1); - for (const auto &lit : sweeper.clause) - add_literal_to_environment (sweeper, depth, lit); - citten_clause_with_id (citten, sweeper.clauses.size (), - sweeper.clause.size (), sweeper.clause.data ()); - sweeper.clause.clear (); - if (opts.sweepcountbinary || sweeper.clause.size () > 2) - sweeper.encoded++; -} - -void Internal::sweep_clause (Sweeper &sweeper, unsigned depth, Clause *c) { - if (c->swept) - return; - CADICAL_assert (can_sweep_clause (c)); - LOG (c, "sweeping[%u]", depth); - CADICAL_assert (sweeper.clause.empty ()); - for (const auto &lit : *c) { - const signed char tmp = val (lit); - if (tmp > 0) { - mark_garbage (c); - sweep_update_noccs (c); - sweeper.clause.clear (); - return; - } - if (tmp < 0) { - if (lrat) - sweeper.prev_units[abs (lit)] = true; - continue; - } - sweeper.clause.push_back (lit); - } - c->swept = true; - sweep_add_clause (sweeper, depth); - sweeper.clauses.push_back (c); -} - -extern "C" { -static void save_core_clause (void *state, unsigned id, bool learned, - size_t size, const unsigned *lits) { - Sweeper *sweeper = (Sweeper *) state; - Internal *internal = sweeper->internal; - if (internal->unsat) - return; - vector &core = sweeper->core[sweeper->save]; - sweep_proof_clause pc; - if (learned) { - pc.sweep_id = INVALID; // necessary - pc.cad_id = INVALID64; // delay giving ids - } else { - pc.sweep_id = id; // necessary - CADICAL_assert (id < sweeper->clauses.size ()); - pc.cad_id = sweeper->clauses[id]->id; - } - pc.kit_id = 0; - pc.learned = learned; - const unsigned *end = lits + size; - for (const unsigned *p = lits; p != end; p++) { - pc.literals.push_back (internal->citten2lit (*p)); // conversion - } -#ifdef LOGGING - LOG (pc.literals, "traced %s", - pc.learned == true ? "learned" : "original"); -#endif - core.push_back (pc); -} - -static void save_core_clause_with_lrat (void *state, unsigned cid, - unsigned id, bool learned, - size_t size, const unsigned *lits, - size_t chain_size, - const unsigned *chain) { - Sweeper *sweeper = (Sweeper *) state; - Internal *internal = sweeper->internal; - if (internal->unsat) - return; - vector &core = sweeper->core[sweeper->save]; - vector &clauses = sweeper->clauses; - sweep_proof_clause pc; - pc.kit_id = cid; - pc.learned = learned; - pc.sweep_id = INVALID; - pc.cad_id = INVALID64; - if (!learned) { - CADICAL_assert (size); - CADICAL_assert (!chain_size); - CADICAL_assert (id < clauses.size ()); - pc.sweep_id = id; - CADICAL_assert (id < clauses.size ()); - pc.cad_id = clauses[id]->id; - for (const auto &lit : *clauses[id]) { - pc.literals.push_back (lit); - } - } else { - CADICAL_assert (chain_size); - pc.sweep_id = INVALID; - pc.cad_id = INVALID64; // delay giving ids - 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--) { - pc.chain.push_back (*(p - 1)); - } - } -#ifdef LOGGING - if (learned) - LOG (pc.literals, "traced %s", - pc.learned == true ? "learned" : "original"); - else { - CADICAL_assert (id < clauses.size ()); - LOG (clauses[id], "traced"); - } -#endif - core.push_back (pc); -} - -static int citten_terminate (void *data) { - return ((Internal *) data)->terminated_asynchronously (); -} - -} // end extern C - -void Internal::citten_clear_track_log_terminate () { - CADICAL_assert (citten); - cadical_kitten_clear (citten); - cadical_kitten_track_antecedents (citten); - if (external->terminator) - cadical_kitten_set_terminator (citten, internal, citten_terminate); -#ifdef LOGGING - if (opts.log) - cadical_kitten_set_logging (citten); -#endif -} - -void Internal::add_core (Sweeper &sweeper, unsigned core_idx) { - if (unsat) - return; - LOG ("check and add extracted core[%u] lemmas to proof", core_idx); - CADICAL_assert (core_idx == 0 || core_idx == 1); - vector &core = sweeper.core[core_idx]; - - CADICAL_assert (!lrat || proof); - - unsigned unsat_size = 0; - for (auto &pc : core) { - unsat_size++; - - if (!pc.learned) { - LOG (pc.literals, - "not adding already present core[%u] cadical_kitten[%u] clause", - core_idx, pc.kit_id); - continue; - } - - LOG (pc.literals, "adding extracted core[%u] cadical_kitten[%u] lemma", - core_idx, pc.kit_id); - - const unsigned new_size = pc.literals.size (); - - if (lrat) { - CADICAL_assert (pc.cad_id == INVALID64); - for (auto &cid : pc.chain) { - int64_t id = 0; - for (const auto &cpc : core) { - if (cpc.kit_id == cid) { - if (cpc.learned) - id = cpc.cad_id; - else { - id = cpc.cad_id; - CADICAL_assert (cpc.cad_id == sweeper.clauses[cpc.sweep_id]->id); - CADICAL_assert (!sweeper.clauses[cpc.sweep_id]->garbage); - // avoid duplicate ids of units with seen flags - for (const auto &lit : cpc.literals) { - if (val (lit) >= 0) - continue; - if (flags (lit).seen) - continue; - const int idx = abs (lit); - if (sweeper.prev_units[idx]) { - int64_t uid = unit_id (-lit); - lrat_chain.push_back (uid); - analyzed.push_back (lit); - flags (lit).seen = true; - } - } - } - break; - } - } - CADICAL_assert (id); - if (id != INVALID64) - lrat_chain.push_back (id); - } - clear_analyzed_literals (); - } - - if (!new_size) { - LOG ("sweeping produced empty clause"); - learn_empty_clause (); - core.resize (unsat_size); - return; - } - - if (new_size == 1) { - int unit = pc.literals[0]; - if (val (unit) > 0) { - LOG ("already assigned sweeping unit %d", unit); - lrat_chain.clear (); - } else if (val (unit) < 0) { - LOG ("falsified sweeping unit %d leads to empty clause", unit); - if (lrat) { - int64_t id = unit_id (-unit); - lrat_chain.push_back (id); - } - learn_empty_clause (); - core.resize (unsat_size); - return; - } else { - LOG ("sweeping produced unit %d", unit); - assign_unit (unit); - stats.sweep_units++; - sweeper.propagate.push_back (unit); - } - if (proof && lrat) - pc.cad_id = unit_id (unit); - continue; - } - - CADICAL_assert (new_size > 1); - CADICAL_assert (pc.learned); - - if (proof) { - pc.cad_id = ++clause_id; - proof->add_derived_clause (pc.cad_id, true, pc.literals, lrat_chain); - lrat_chain.clear (); - } - } -} - -void Internal::save_core (Sweeper &sweeper, unsigned core) { - LOG ("saving extracted core[%u] lemmas", core); - CADICAL_assert (core == 0 || core == 1); - CADICAL_assert (sweeper.core[core].empty ()); - sweeper.save = core; - cadical_kitten_compute_clausal_core (citten, 0); - if (lrat) - cadical_kitten_trace_core (citten, &sweeper, save_core_clause_with_lrat); - else - cadical_kitten_traverse_core_clauses_with_id (citten, &sweeper, - save_core_clause); -} - -void Internal::clear_core (Sweeper &sweeper, unsigned core_idx) { - CADICAL_assert (core_idx == 0 || core_idx == 1); - LOG ("clearing core[%u] lemmas", core_idx); - vector &core = sweeper.core[core_idx]; - if (proof) { - LOG ("deleting sub-solver core clauses"); - for (auto &pc : core) { - if (pc.learned && pc.literals.size () > 1) - proof->delete_clause (pc.cad_id, true, pc.literals); - } - } - core.clear (); -} - -void Internal::save_add_clear_core (Sweeper &sweeper) { - save_core (sweeper, 0); - add_core (sweeper, 0); - clear_core (sweeper, 0); -} - -void Internal::init_backbone_and_partition (Sweeper &sweeper) { - LOG ("initializing backbone and equivalent literals candidates"); - sweeper.backbone.clear (); - sweeper.partition.clear (); - for (const auto &idx : sweeper.vars) { - if (!active (idx)) - continue; - CADICAL_assert (idx > 0); - const int lit = idx; - const int not_lit = -lit; - const signed char tmp = cadical_kitten_signed_value (citten, lit); - const int candidate = (tmp < 0) ? not_lit : lit; - LOG ("sweeping candidate %d", candidate); - sweeper.backbone.push_back (candidate); - sweeper.partition.push_back (candidate); - } - sweeper.partition.push_back (0); - - LOG (sweeper.backbone, "initialized backbone candidates"); - LOG (sweeper.partition, "initialized equivalence candidates"); -} - -void Internal::sweep_empty_clause (Sweeper &sweeper) { - CADICAL_assert (!unsat); - save_add_clear_core (sweeper); - CADICAL_assert (unsat); -} - -void Internal::sweep_refine_partition (Sweeper &sweeper) { - LOG ("refining partition"); - vector &old_partition = sweeper.partition; - vector new_partition; - auto old_begin = old_partition.begin (); - const auto old_end = old_partition.end (); -#ifdef LOGGING - unsigned old_classes = 0; - unsigned new_classes = 0; -#endif - for (auto p = old_begin, q = p; p != old_end; p = q + 1) { - unsigned assigned_true = 0; - int other; - for (q = p; (other = *q) != 0; q++) { - if (sweep_repr (sweeper, other) != other) - continue; - if (val (other)) - continue; - signed char value = cadical_kitten_signed_value (citten, other); - if (!value) - LOG ("dropping sub-solver unassigned %d", other); - else if (value > 0) { - new_partition.push_back (other); - assigned_true++; - } - } -#ifdef LOGGING - LOG ("refining class %u of size %zu", old_classes, (size_t) (q - p)); - old_classes++; -#endif - if (assigned_true == 0) - LOG ("no positive literal in class"); - else if (assigned_true == 1) { -#ifdef LOGGING - other = -#else - (void) -#endif - new_partition.back (); - new_partition.pop_back (); - LOG ("dropping singleton class %d", other); - } else { - LOG ("%u positive literal in class", assigned_true); - new_partition.push_back (0); -#ifdef LOGGING - new_classes++; -#endif - } - - unsigned assigned_false = 0; - for (q = p; (other = *q) != 0; q++) { - if (sweep_repr (sweeper, other) != other) - continue; - if (val (other)) - continue; - signed char value = cadical_kitten_signed_value (citten, other); - if (value < 0) { - new_partition.push_back (other); - assigned_false++; - } - } - - if (assigned_false == 0) - LOG ("no negative literal in class"); - else if (assigned_false == 1) { -#ifdef LOGGING - other = -#else - (void) -#endif - new_partition.back (); - new_partition.pop_back (); - LOG ("dropping singleton class %d", other); - } else { - LOG ("%u negative literal in class", assigned_false); - new_partition.push_back (0); -#ifdef LOGGING - new_classes++; -#endif - } - } - old_partition.swap (new_partition); - LOG ("refined %u classes into %u", old_classes, new_classes); -} - -void Internal::sweep_refine_backbone (Sweeper &sweeper) { - LOG ("refining backbone candidates"); - const auto end = sweeper.backbone.end (); - auto q = sweeper.backbone.begin (); - for (auto p = q; p != end; p++) { - const int lit = *p; - if (val (lit)) - continue; - signed char value = cadical_kitten_signed_value (citten, lit); - if (!value) - LOG ("dropping sub-solver unassigned %d", lit); - else if (value > 0) - *q++ = lit; - } - sweeper.backbone.resize (q - sweeper.backbone.begin ()); -} - -void Internal::sweep_refine (Sweeper &sweeper) { - CADICAL_assert (cadical_kitten_status (citten) == 10); - if (sweeper.backbone.empty ()) - LOG ("no need to refine empty backbone candidates"); - else - sweep_refine_backbone (sweeper); - if (sweeper.partition.empty ()) - LOG ("no need to refine empty partition candidates"); - else - sweep_refine_partition (sweeper); -} - -void Internal::flip_backbone_literals (Sweeper &sweeper) { - const unsigned max_rounds = opts.sweepfliprounds; - if (!max_rounds) - return; - CADICAL_assert (sweeper.backbone.size ()); - if (cadical_kitten_status (citten) != 10) - return; -#ifdef LOGGING - unsigned total_flipped = 0; -#endif - unsigned flipped, round = 0; - do { - round++; - flipped = 0; - bool refine = false; - auto begin = sweeper.backbone.begin (), q = begin, p = q; - const auto end = sweeper.backbone.end (); - bool limit_hit = false; - while (p != end) { - const int lit = *p++; - stats.sweep_flip_backbone++; - if (limit_hit || terminated_asynchronously ()) { - break; - } else if (sweep_flip (lit)) { - LOG ("flipping backbone candidate %d succeeded", lit); -#ifdef LOGGING - total_flipped++; -#endif - stats.sweep_flipped_backbone++; - flipped++; - } else { - LOG ("flipping backbone candidate %d failed", lit); - *q++ = lit; - } - } - while (p != end) - *q++ = *p++; - sweeper.backbone.resize (q - sweeper.backbone.begin ()); - LOG ("flipped %u backbone candidates in round %u", flipped, round); - - if (limit_hit) - break; - if (terminated_asynchronously ()) - break; - if (cadical_kitten_ticks_limit_hit (sweeper, "backbone flipping")) - break; - if (refine) - sweep_refine (sweeper); - } while (flipped && round < max_rounds); - LOG ("flipped %u backbone candidates in total in %u rounds", - total_flipped, round); -} - -bool Internal::sweep_extract_fixed (Sweeper &sweeper, int lit) { - const int not_lit = -lit; - stats.sweep_solved_backbone++; - cadical_kitten_assume_signed (citten, not_lit); - int res = sweep_solve (); - if (!res) { - stats.sweep_unknown_backbone++; - return false; - } - CADICAL_assert (res == 20); - LOG ("sweep unit %d", lit); - save_add_clear_core (sweeper); - stats.sweep_unsat_backbone++; - return true; -} - -bool Internal::sweep_backbone_candidate (Sweeper &sweeper, int lit) { - LOG ("trying backbone candidate %d", lit); - signed char value = cadical_kitten_fixed_signed (citten, lit); - if (value) { - stats.sweep_fixed_backbone++; - CADICAL_assert (value > 0); - if (val (lit) <= 0) { - return sweep_extract_fixed (sweeper, lit); - } else - LOG ("literal %d already fixed", lit); - return false; - } - - int res = cadical_kitten_status (citten); - if (res != 10) { - LOG ("not flipping due to status %d != 10", res); - } - stats.sweep_flip_backbone++; - if (res == 10 && sweep_flip (lit)) { - stats.sweep_flipped_backbone++; - LOG ("flipping %d succeeded", lit); - // LOGBACKBONE ("refined backbone candidates"); - return false; - } - - LOG ("flipping %d failed", lit); - const int not_lit = -lit; - stats.sweep_solved_backbone++; - cadical_kitten_assume_signed (citten, not_lit); - res = sweep_solve (); - if (res == 10) { - LOG ("sweeping backbone candidate %d failed", lit); - sweep_refine (sweeper); - stats.sweep_sat_backbone++; - return false; - } - - if (res == 20) { - LOG ("sweep unit %d", lit); - save_add_clear_core (sweeper); - CADICAL_assert (val (lit)); - stats.sweep_unsat_backbone++; - return true; - } - - stats.sweep_unknown_backbone++; - - LOG ("sweeping backbone candidate %d failed", lit); - return false; -} - -// at this point the binary (lit or other) is already present -// in the proof via 'add_core'. -// We just copy it as an irredundant clause, call weaken minus -// and push it on the extension stack. -// -int64_t Internal::add_sweep_binary (sweep_proof_clause pc, int lit, - int other) { - CADICAL_assert (!unsat); - if (unsat) - return 0; // sanity check, should be fuzzed - - CADICAL_assert (!val (lit) && !val (other)); - if (val (lit) || val (other)) - return 0; - - if (lrat) { - for (const auto &plit : pc.literals) { - if (val (plit)) { - int64_t id = unit_id (-plit); - lrat_chain.push_back (id); - } - } - lrat_chain.push_back (pc.cad_id); - } - clause.push_back (lit); - clause.push_back (other); - const int64_t id = ++clause_id; - if (proof) { - proof->add_derived_clause (id, false, clause, lrat_chain); - proof->weaken_minus (id, clause); - } - external->push_binary_clause_on_extension_stack (id, lit, other); - clause.clear (); - lrat_chain.clear (); - return id; -} - -void Internal::delete_sweep_binary (const sweep_binary &sb) { - if (unsat) - return; - if (!proof) - return; - vector bin; - bin.push_back (sb.lit); - bin.push_back (sb.other); - proof->delete_clause (sb.id, false, bin); -} - -bool Internal::scheduled_variable (Sweeper &sweeper, int idx) { - return sweeper.prev[idx] != 0 || sweeper.first == idx; -} - -void Internal::schedule_inner (Sweeper &sweeper, int idx) { - CADICAL_assert (idx); - if (!active (idx)) - return; - const int next = sweeper.next[idx]; - if (next != 0) { - LOG ("rescheduling inner %d as last", idx); - const unsigned prev = sweeper.prev[idx]; - CADICAL_assert (sweeper.prev[next] == idx); - sweeper.prev[next] = prev; - if (prev == 0) { - CADICAL_assert (sweeper.first == idx); - sweeper.first = next; - } else { - CADICAL_assert (sweeper.next[prev] == idx); - sweeper.next[prev] = next; - } - const unsigned last = sweeper.last; - if (last == 0) { - CADICAL_assert (sweeper.first == 0); - sweeper.first = idx; - } else { - CADICAL_assert (sweeper.next[last] == 0); - sweeper.next[last] = idx; - } - sweeper.prev[idx] = last; - sweeper.next[idx] = 0; - sweeper.last = idx; - } else if (sweeper.last != idx) { - LOG ("scheduling inner %d as last", idx); - const unsigned last = sweeper.last; - if (last == 0) { - CADICAL_assert (sweeper.first == 0); - sweeper.first = idx; - } else { - CADICAL_assert (sweeper.next[last] == 0); - sweeper.next[last] = idx; - } - CADICAL_assert (sweeper.next[idx] == 0); - sweeper.prev[idx] = last; - sweeper.last = idx; - } else - LOG ("keeping inner %d scheduled as last", idx); -} - -void Internal::schedule_outer (Sweeper &sweeper, int idx) { - CADICAL_assert (!scheduled_variable (sweeper, idx)); - CADICAL_assert (active (idx)); - const int first = sweeper.first; - if (first == 0) { - CADICAL_assert (sweeper.last == 0); - sweeper.last = idx; - } else { - CADICAL_assert (sweeper.prev[first] == 0); - sweeper.prev[first] = idx; - } - CADICAL_assert (sweeper.prev[idx] == 0); - sweeper.next[idx] = first; - sweeper.first = idx; - LOG ("scheduling outer %d as first", idx); -} - -int Internal::next_scheduled (Sweeper &sweeper) { - int res = sweeper.last; - if (res == 0) { - LOG ("no more scheduled variables left"); - return 0; - } - CADICAL_assert (res > 0); - LOG ("dequeuing next scheduled %d", res); - const unsigned prev = sweeper.prev[res]; - CADICAL_assert (sweeper.next[res] == 0); - sweeper.prev[res] = 0; - if (prev == 0) { - CADICAL_assert (sweeper.first == res); - sweeper.first = 0; - } else { - CADICAL_assert (sweeper.next[prev] == res); - sweeper.next[prev] = 0; - } - sweeper.last = prev; - return res; -} - -void Internal::sweep_substitute_lrat (Clause *c, int64_t id) { - if (!lrat) - return; - for (const auto &lit : *c) { - CADICAL_assert (val (lit) <= 0); - if (val (lit) < 0) { - int64_t id = unit_id (-lit); - lrat_chain.push_back (id); - } - } - lrat_chain.push_back (id); - lrat_chain.push_back (c->id); -} - -#define all_scheduled(IDX) \ - int IDX = sweeper.first, NEXT_##IDX; \ - IDX != 0 && (NEXT_##IDX = sweeper.next[IDX], true); \ - IDX = NEXT_##IDX - -// Substitute equivalences in clauses (see -// 'sweep_substitute_new_equivalences' for explanation) -void Internal::substitute_connected_clauses (Sweeper &sweeper, int lit, - int repr, int64_t id) { - if (unsat) - return; - if (val (lit)) - return; - if (val (repr)) - return; - LOG ("substituting %d with %d in all irredundant clauses", lit, repr); - - CADICAL_assert (lit != repr); - CADICAL_assert (lit != -repr); - - CADICAL_assert (active (lit)); - CADICAL_assert (active (repr)); - - uint64_t &ticks = sweeper.current_ticks; - - { - ticks += 1 + cache_lines (occs (lit).size (), sizeof (Clause *)); - Occs &ns = occs (lit); - auto const begin = ns.begin (); - const auto end = ns.end (); - auto q = begin; - auto p = q; - while (p != end) { - Clause *c = *q++ = *p++; - ticks++; - if (c->garbage) - continue; - CADICAL_assert (clause.empty ()); - bool satisfied = false; - bool repr_already_watched = false; - const int not_repr = -repr; -#ifndef CADICAL_NDEBUG - bool found = false; -#endif - for (const auto &other : *c) { - if (other == lit) { -#ifndef CADICAL_NDEBUG - CADICAL_assert (!found); - found = true; -#endif - clause.push_back (repr); - continue; - } - CADICAL_assert (other != -lit); - if (other == repr) { - CADICAL_assert (!repr_already_watched); - repr_already_watched = true; - continue; - } - if (other == not_repr) { - satisfied = true; - break; - } - const signed char tmp = val (other); - if (tmp < 0) - continue; - if (tmp > 0) { - satisfied = true; - break; - } - clause.push_back (other); - } - if (satisfied) { - clause.clear (); - mark_garbage (c); - sweep_update_noccs (c); - continue; - } - CADICAL_assert (found); - const unsigned new_size = clause.size (); - sweep_substitute_lrat (c, id); - if (new_size == 0) { - LOG (c, "substituted empty clause"); - CADICAL_assert (!unsat); - learn_empty_clause (); - break; - } - ticks++; - if (new_size == 1) { - LOG (c, "reduces to unit"); - const int unit = clause[0]; - clause.clear (); - assign_unit (unit); - sweeper.propagate.push_back (unit); - mark_garbage (c); - sweep_update_noccs (c); - stats.sweep_units++; - break; - } - CADICAL_assert (c->size >= 2); - if (!c->redundant) - mark_removed (c); - uint64_t new_id = ++clause_id; - if (proof) { - proof->add_derived_clause (new_id, c->redundant, clause, - lrat_chain); - proof->delete_clause (c); - } - c->id = new_id; - lrat_chain.clear (); - size_t l; - int *literals = c->literals; - for (l = 0; l < clause.size (); l++) - literals[l] = clause[l]; - int flushed = c->size - (int) l; - if (flushed) { - LOG ("flushed %d literals", flushed); - (void) shrink_clause (c, l); - } else if (likely_to_be_kept_clause (c)) - mark_added (c); - LOG (c, "substituted"); - if (!repr_already_watched) { - occs (repr).push_back (c); - noccs (repr)++; - } - clause.clear (); - q--; - } - while (p != end) - *q++ = *p++; - ns.resize (q - ns.begin ()); - } -} - -// In contrast to kissat we substitute the equivalences explicitely after -// every successful round of sweeping. This is necessary in order to extract -// valid LRAT proofs for subsequent rounds of sweeping. -void Internal::sweep_substitute_new_equivalences (Sweeper &sweeper) { - if (unsat) - return; - - unsigned count = 0; - CADICAL_assert (lrat_chain.empty ()); - - for (const auto &sb : sweeper.binaries) { - count++; - const auto lit = sb.lit; - const auto other = sb.other; - if (abs (lit) < abs (other)) { - substitute_connected_clauses (sweeper, -other, lit, sb.id); - } else { - substitute_connected_clauses (sweeper, -lit, other, sb.id); - } - CADICAL_assert (lrat_chain.empty ()); - if (val (lit) < 0) { - if (lrat) { - const int64_t lid = unit_id (-lit); - lrat_chain.push_back (lid); - } - if (!val (other)) { - if (lrat) - lrat_chain.push_back (sb.id); - assign_unit (other); - } else if (val (other) < 0) { - if (lrat) { - const int64_t oid = unit_id (-other); - lrat_chain.push_back (oid); - lrat_chain.push_back (sb.id); - } - learn_empty_clause (); - return; - } - } else if (val (other) < 0) { - if (!val (lit)) { - if (lrat) { - const int64_t oid = unit_id (-other); - lrat_chain.push_back (oid); - lrat_chain.push_back (sb.id); - } - assign_unit (lit); - } else - CADICAL_assert (val (lit) > 0); - } - lrat_chain.clear (); - delete_sweep_binary (sb); - if (count == 2) { - if (!val (lit) && !val (other)) { - const auto idx = abs (lit) < abs (other) ? abs (other) : abs (lit); - if (!flags (idx).fixed ()) - mark_substituted (idx); - } - count = 0; - } - CADICAL_assert (lrat_chain.empty ()); - } - sweeper.binaries.clear (); -} - -void Internal::sweep_remove (Sweeper &sweeper, int lit) { - CADICAL_assert (sweeper.reprs[lit] != lit); - vector &partition = sweeper.partition; - const auto begin_partition = partition.begin (); - auto p = begin_partition; - const auto end_partition = partition.end (); - for (; *p != lit; p++) - CADICAL_assert (p + 1 != end_partition); - auto begin_class = p; - while (begin_class != begin_partition && begin_class[-1] != 0) - begin_class--; - auto end_class = p; - while (*end_class != 0) - end_class++; - const unsigned size = end_class - begin_class; - LOG ("removing non-representative %d from equivalence class of size %u", - lit, size); - CADICAL_assert (size > 1); - auto q = begin_class; - if (size == 2) { - LOG ("completely squashing equivalence class of %d", lit); - for (auto r = end_class + 1; r != end_partition; r++) - *q++ = *r; - } else { - for (auto r = begin_class; r != end_partition; r++) - if (r != p) - *q++ = *r; - } - partition.resize (q - partition.begin ()); -} - -void Internal::flip_partition_literals (Sweeper &sweeper) { - const unsigned max_rounds = opts.sweepfliprounds; - if (!max_rounds) - return; - CADICAL_assert (sweeper.partition.size ()); - if (cadical_kitten_status (citten) != 10) - return; -#ifdef LOGGING - unsigned total_flipped = 0; -#endif - unsigned flipped, round = 0; - do { - round++; - flipped = 0; - bool refine = false; - bool limit_hit = false; - auto begin = sweeper.partition.begin (), dst = begin, src = dst; - const auto end = sweeper.partition.end (); - while (src != end) { - auto end_src = src; - while (CADICAL_assert (end_src != end), *end_src != 0) - end_src++; - unsigned size = end_src - src; - CADICAL_assert (size > 1); - auto q = dst; - for (auto p = src; p != end_src; p++) { - const int lit = *p; - if (limit_hit) { - *q++ = lit; - continue; - } else if (cadical_kitten_ticks_limit_hit (sweeper, "partition flipping")) { - *q++ = lit; - limit_hit = true; - continue; - } else if (sweep_flip (lit)) { - LOG ("flipping equivalence candidate %d succeeded", lit); -#ifdef LOGGING - total_flipped++; -#endif - flipped++; - if (--size < 2) - break; - } else { - LOG ("flipping equivalence candidate %d failed", lit); - *q++ = lit; - } - stats.sweep_flip_equivalences++; - } - if (size > 1) { - *q++ = 0; - dst = q; - } - src = end_src + 1; - } - stats.sweep_flipped_equivalences += flipped; - sweeper.partition.resize (dst - sweeper.partition.begin ()); - LOG ("flipped %u equivalence candidates in round %u", flipped, round); - if (terminated_asynchronously ()) - break; - if (cadical_kitten_ticks_limit_hit (sweeper, "partition flipping")) - break; - if (refine) - sweep_refine (sweeper); - } while (flipped && round < max_rounds); - LOG ("flipped %u equivalence candidates in total in %u rounds", - total_flipped, round); -} - -bool Internal::sweep_equivalence_candidates (Sweeper &sweeper, int lit, - int other) { - LOG ("trying equivalence candidates %d = %d", lit, other); - const auto begin = sweeper.partition.begin (); - auto const end = sweeper.partition.end (); - CADICAL_assert (begin + 3 <= end); - CADICAL_assert (end[-3] == lit); - CADICAL_assert (end[-2] == other); - const int third = (end - begin == 3) ? 0 : end[-4]; - int res = cadical_kitten_status (citten); - if (res == 10) { - stats.sweep_flip_equivalences++; - if (sweep_flip (lit)) { - stats.sweep_flipped_equivalences++; - LOG ("flipping %d succeeded", lit); - if (third == 0) { - LOG ("squashing equivalence class of %d", lit); - sweeper.partition.resize (sweeper.partition.size () - 3); - } else { - LOG ("removing %d from equivalence class of %d", lit, other); - end[-3] = other; - end[-2] = 0; - sweeper.partition.resize (sweeper.partition.size () - 1); - } - return false; - } - stats.sweep_flip_equivalences++; - if (sweep_flip (other)) { - stats.sweep_flipped_equivalences++; - LOG ("flipping %d succeeded", other); - if (third == 0) { - LOG ("squashing equivalence class of %d", lit); - sweeper.partition.resize (sweeper.partition.size () - 3); - } else { - LOG ("removing %d from equivalence class of %d", other, lit); - end[-2] = 0; - sweeper.partition.resize (sweeper.partition.size () - 1); - } - return false; - } - } - // frozen variables are not allowed to be eliminated. - // It might still be beneficial to learn the binaries, if they - // really are equivalent, but we avoid the issue by not trying - // for equivalence at all if the non-representative is frozen. - // i.e., the higher absolute value - if (abs (lit) > abs (other) && frozen (lit)) { - if (third == 0) { - LOG ("squashing equivalence class of %d", lit); - sweeper.partition.resize (sweeper.partition.size () - 3); - } else { - LOG ("removing %d from equivalence class of %d", lit, other); - end[-3] = other; - end[-2] = 0; - sweeper.partition.resize (sweeper.partition.size () - 1); - } - return false; - } else if (abs (other) > abs (lit) && frozen (other)) { - if (third == 0) { - LOG ("squashing equivalence class of %d", lit); - sweeper.partition.resize (sweeper.partition.size () - 3); - } else { - LOG ("removing %d from equivalence class of %d", lit, other); - end[-2] = 0; - sweeper.partition.resize (sweeper.partition.size () - 1); - } - return false; - } - - const int not_other = -other; - const int not_lit = -lit; - LOG ("flipping %d and %d both failed", lit, other); - cadical_kitten_assume_signed (citten, not_lit); - cadical_kitten_assume_signed (citten, other); - stats.sweep_solved_equivalences++; - res = sweep_solve (); - if (res == 10) { - stats.sweep_sat_equivalences++; - LOG ("first sweeping implication %d -> %d failed", other, lit); - sweep_refine (sweeper); - } else if (!res) { - stats.sweep_unknown_equivalences++; - LOG ("first sweeping implication %d -> %d hit ticks limit", other, lit); - } - - if (res != 20) - return false; - - stats.sweep_unsat_equivalences++; - LOG ("first sweeping implication %d -> %d succeeded", other, lit); - - save_core (sweeper, 0); - - cadical_kitten_assume_signed (citten, lit); - cadical_kitten_assume_signed (citten, not_other); - res = sweep_solve (); - stats.sweep_solved_equivalences++; - if (res == 10) { - stats.sweep_sat_equivalences++; - LOG ("second sweeping implication %d <- %d failed", other, lit); - sweep_refine (sweeper); - } else if (!res) { - stats.sweep_unknown_equivalences++; - LOG ("second sweeping implication %d <- %d hit ticks limit", other, - lit); - } - - if (res != 20) { - sweeper.core[0].clear (); - return false; - } - - CADICAL_assert (res == 20); - - stats.sweep_unsat_equivalences++; - LOG ("second sweeping implication %d <- %d succeeded too", other, lit); - - save_core (sweeper, 1); - - LOG ("sweep equivalence %d = %d", lit, other); - - // If cadical_kitten behaves as expected, the two binaries of the equivalence - // should be stored at sweeper.core[i].back () for i in {0, 1}. - // We pick the smaller absolute valued literal as representative and - // store the equivalence . - add_core (sweeper, 0); - add_core (sweeper, 1); - if (!val (lit) && !val (other)) { - CADICAL_assert (sweeper.core[0].size ()); - CADICAL_assert (sweeper.core[1].size ()); - stats.sweep_equivalences++; - sweep_binary bin1; - sweep_binary bin2; - if (abs (lit) > abs (other)) { - bin1.lit = lit; - bin1.other = not_other; - bin2.lit = not_lit; - bin2.other = other; - bin1.id = add_sweep_binary (sweeper.core[0].back (), lit, not_other); - bin2.id = add_sweep_binary (sweeper.core[1].back (), not_lit, other); - } else { - bin1.lit = not_other; - bin1.other = lit; - bin2.lit = other; - bin2.other = not_lit; - bin1.id = add_sweep_binary (sweeper.core[0].back (), not_other, lit); - bin2.id = add_sweep_binary (sweeper.core[1].back (), other, not_lit); - } - if (bin1.id && bin2.id) { - sweeper.binaries.push_back (bin1); - sweeper.binaries.push_back (bin2); - } - } - - int repr; - if (abs (lit) < abs (other)) { - repr = sweeper.reprs[other] = lit; - sweeper.reprs[not_other] = not_lit; - sweep_remove (sweeper, other); - } else { - repr = sweeper.reprs[lit] = other; - sweeper.reprs[not_lit] = not_other; - sweep_remove (sweeper, lit); - } - clear_core (sweeper, 0); - clear_core (sweeper, 1); - - const int repr_idx = abs (repr); - schedule_inner (sweeper, repr_idx); - - return true; -} - -const char *Internal::sweep_variable (Sweeper &sweeper, int idx) { - CADICAL_assert (!unsat); - if (!active (idx)) - return "inactive variable"; - const int start = idx; - if (sweeper.reprs[start] != start) - return "non-representative variable"; - CADICAL_assert (sweeper.vars.empty ()); - CADICAL_assert (sweeper.clauses.empty ()); - CADICAL_assert (sweeper.backbone.empty ()); - CADICAL_assert (sweeper.partition.empty ()); - CADICAL_assert (!sweeper.encoded); - - stats.sweep_variables++; - - LOG ("sweeping %d", idx); - CADICAL_assert (!val (start)); - LOG ("starting sweeping[0]"); - add_literal_to_environment (sweeper, 0, start); - LOG ("finished sweeping[0]"); - LOG ("starting sweeping[1]"); - - bool limit_reached = false; - size_t expand = 0, next = 1; - bool success = false; - unsigned depth = 1; - - uint64_t &ticks = sweeper.current_ticks; - while (!limit_reached) { - if (sweeper.encoded >= sweeper.limit.clauses) { - LOG ("environment clause limit reached"); - limit_reached = true; - break; - } - if (expand == next) { - LOG ("finished sweeping[%u]", depth); - if (depth >= sweeper.limit.depth) { - LOG ("environment depth limit reached"); - break; - } - next = sweeper.vars.size (); - if (expand == next) { - LOG ("completely copied all clauses"); - break; - } - depth++; - LOG ("starting sweeping[%u]", depth); - } - const unsigned choices = next - expand; - if (opts.sweeprand && choices > 1) { - const unsigned swaps = sweeper.random.pick_int (0, choices - 1); - if (swaps) { - CADICAL_assert (expand + swaps < sweeper.vars.size ()); - swap (sweeper.vars[expand], sweeper.vars[expand + swaps]); - } - } - const int idx = sweeper.vars[expand]; - LOG ("traversing and adding clauses of %d", idx); - for (unsigned sign = 0; sign < 2; sign++) { - const int lit = sign ? -idx : idx; - ticks += 1 + cache_lines (occs (lit).size (), sizeof (Clause *)); - Occs &ns = occs (lit); - for (auto c : ns) { - ticks++; - if (!can_sweep_clause (c)) - continue; - sweep_clause (sweeper, depth, c); - if (sweeper.vars.size () >= sweeper.limit.vars) { - LOG ("environment variable limit reached"); - limit_reached = true; - break; - } - } - if (limit_reached) - break; - } - expand++; - } - stats.sweep_depth += depth; - stats.sweep_clauses += sweeper.encoded; - stats.sweep_environment += sweeper.vars.size (); - VERBOSE (3, - "sweeping variable %d environment of " - "%zu variables %u clauses depth %u", - externalize (idx), sweeper.vars.size (), sweeper.encoded, depth); - - int res; - if (sweeper.vars.size () == 1) { - LOG ("not sweeping literal %d with environment size 1", idx); - goto DONE; - } - res = sweep_solve (); - LOG ("sub-solver returns '%d'", res); - if (res == 10) { - init_backbone_and_partition (sweeper); -#ifndef CADICAL_QUIET - uint64_t units = stats.sweep_units; - uint64_t solved = stats.sweep_solved; -#endif - START (sweepbackbone); - while (sweeper.backbone.size ()) { - if (unsat || terminated_asynchronously () || - cadical_kitten_ticks_limit_hit (sweeper, "backbone refinement")) { - limit_reached = true; - STOP_SWEEP_BACKBONE: - STOP (sweepbackbone); - goto DONE; - } - flip_backbone_literals (sweeper); - if (terminated_asynchronously () || - cadical_kitten_ticks_limit_hit (sweeper, "backbone refinement")) { - limit_reached = true; - goto STOP_SWEEP_BACKBONE; - } - if (sweeper.backbone.empty ()) - break; - const int lit = sweeper.backbone.back (); - sweeper.backbone.pop_back (); - if (!active (lit)) - continue; - if (sweep_backbone_candidate (sweeper, lit)) - success = true; - } - STOP (sweepbackbone); -#ifndef CADICAL_QUIET - units = stats.sweep_units - units; - solved = stats.sweep_solved - solved; -#endif - VERBOSE (3, - "complete swept variable %d backbone with %" PRIu64 - " units in %" PRIu64 " solver calls", - externalize (idx), units, solved); - CADICAL_assert (sweeper.backbone.empty ()); -#ifndef CADICAL_QUIET - uint64_t equivalences = stats.sweep_equivalences; - solved = stats.sweep_solved; -#endif - START (sweepequivalences); - while (sweeper.partition.size ()) { - if (unsat || terminated_asynchronously () || - cadical_kitten_ticks_limit_hit (sweeper, "partition refinement")) { - limit_reached = true; - STOP_SWEEP_EQUIVALENCES: - STOP (sweepequivalences); - goto DONE; - } - flip_partition_literals (sweeper); - if (terminated_asynchronously () || - cadical_kitten_ticks_limit_hit (sweeper, "backbone refinement")) { - limit_reached = true; - goto STOP_SWEEP_EQUIVALENCES; - } - if (sweeper.partition.empty ()) - break; - if (sweeper.partition.size () > 2) { - const auto end = sweeper.partition.end (); - CADICAL_assert (end[-1] == 0); - int lit = end[-3]; - int other = end[-2]; - if (sweep_equivalence_candidates (sweeper, lit, other)) - success = true; - } else - sweeper.partition.clear (); - } - STOP (sweepequivalences); -#ifndef CADICAL_QUIET - equivalences = stats.sweep_equivalences - equivalences; - solved = stats.sweep_solved - solved; - if (equivalences) - VERBOSE (3, - "complete swept variable %d partition with %" PRIu64 - " equivalences in %" PRIu64 " solver calls", - externalize (idx), equivalences, solved); -#endif - } else if (res == 20) - sweep_empty_clause (sweeper); - -DONE: - clear_sweeper (sweeper); - - if (!unsat) - sweep_substitute_new_equivalences (sweeper); - if (!unsat) - sweep_dense_propagate (sweeper); - - if (success && limit_reached) - return "successfully despite reaching limit"; - if (!success && !limit_reached) - return "unsuccessfully without reaching limit"; - else if (success && !limit_reached) - return "successfully without reaching limit"; - CADICAL_assert (!success && limit_reached); - return "unsuccessfully and reached limit"; -} - -struct sweep_candidate { - unsigned rank; - int idx; -}; - -struct rank_sweep_candidate { - bool operator() (sweep_candidate a, sweep_candidate b) const { - CADICAL_assert (a.rank && b.rank); - CADICAL_assert (a.idx > 0 && b.idx > 0); - if (a.rank < b.rank) - return true; - if (b.rank < a.rank) - return false; - return a.idx < b.idx; - } -}; - -bool Internal::scheduable_variable (Sweeper &sweeper, int idx, - size_t *occ_ptr) { - const int lit = idx; - const size_t pos = noccs (lit); - if (!pos) - return false; - const unsigned max_occurrences = sweeper.limit.clauses; - if (pos > max_occurrences) - return false; - const int not_lit = -lit; - const size_t neg = noccs (not_lit); - if (!neg) - return false; - if (neg > max_occurrences) - return false; - *occ_ptr = pos + neg; - return true; -} - -unsigned Internal::schedule_all_other_not_scheduled_yet (Sweeper &sweeper) { - vector fresh; - for (const auto &idx : vars) { - Flags &f = flags (idx); - if (!f.active ()) - continue; - if (sweep_incomplete && !f.sweep) - continue; - if (scheduled_variable (sweeper, idx)) - continue; - size_t occ; - if (!scheduable_variable (sweeper, idx, &occ)) { - f.sweep = false; - continue; - } - sweep_candidate cand; - cand.rank = occ; - cand.idx = idx; - fresh.push_back (cand); - } - const size_t size = fresh.size (); - CADICAL_assert (size <= UINT_MAX); - sort (fresh.begin (), fresh.end (), rank_sweep_candidate ()); - for (auto &cand : fresh) - schedule_outer (sweeper, cand.idx); - return size; -} - -unsigned Internal::reschedule_previously_remaining (Sweeper &sweeper) { - unsigned rescheduled = 0; - for (const auto &idx : sweep_schedule) { - Flags &f = flags (idx); - if (!f.active ()) - continue; - if (scheduled_variable (sweeper, idx)) - continue; - size_t occ; - if (!scheduable_variable (sweeper, idx, &occ)) { - f.sweep = false; - continue; - } - schedule_inner (sweeper, idx); - rescheduled++; - } - sweep_schedule.clear (); - return rescheduled; -} - -unsigned Internal::incomplete_variables () { - unsigned res = 0; - for (const auto &idx : vars) { - Flags &f = flags (idx); - if (!f.active ()) - continue; - if (f.sweep) - res++; - } - return res; -} - -void Internal::mark_incomplete (Sweeper &sweeper) { - unsigned marked = 0; - for (all_scheduled (idx)) { - if (!flags (idx).sweep) { - flags (idx).sweep = true; - marked++; - } - } - sweep_incomplete = true; -#ifndef CADICAL_QUIET - VERBOSE (2, "marked %u scheduled sweeping variables as incomplete", - marked); -#else - (void) marked; -#endif -} - -unsigned Internal::schedule_sweeping (Sweeper &sweeper) { - const unsigned rescheduled = reschedule_previously_remaining (sweeper); - const unsigned fresh = schedule_all_other_not_scheduled_yet (sweeper); - const unsigned scheduled = fresh + rescheduled; - const unsigned incomplete = incomplete_variables (); -#ifndef CADICAL_QUIET - PHASE ("sweep", stats.sweep, - "scheduled %u variables %.0f%% " - "(%u rescheduled %.0f%%, %u incomplete %.0f%%)", - scheduled, percent (scheduled, active ()), rescheduled, - percent (rescheduled, scheduled), incomplete, - percent (incomplete, scheduled)); -#endif - if (incomplete) - CADICAL_assert (sweep_incomplete); - else { - if (sweep_incomplete) - stats.sweep_completed++; - mark_incomplete (sweeper); - } - return scheduled; -} - -void Internal::unschedule_sweeping (Sweeper &sweeper, unsigned swept, - unsigned scheduled) { -#ifdef CADICAL_QUIET - (void) scheduled, (void) swept; -#endif - CADICAL_assert (sweep_schedule.empty ()); - CADICAL_assert (sweep_incomplete); - for (all_scheduled (idx)) - if (active (idx)) { - sweep_schedule.push_back (idx); - LOG ("untried scheduled %d", idx); - } -#ifndef CADICAL_QUIET - const unsigned retained = sweep_schedule.size (); -#endif - VERBOSE (3, "retained %u variables %.0f%% to be swept next time", - retained, percent (retained, active ())); - const unsigned incomplete = incomplete_variables (); - if (incomplete) - VERBOSE (3, "need to sweep %u more variables %.0f%% for completion", - incomplete, percent (incomplete, active ())); - else { - VERBOSE (3, "no more variables needed to complete sweep"); - sweep_incomplete = false; - stats.sweep_completed++; - } - PHASE ("sweep", stats.sweep, "swept %u variables (%u remain %.0f%%)", - swept, incomplete, percent (incomplete, scheduled)); -} - -bool Internal::sweep () { - if (!opts.sweep) - return false; - if (unsat) - return false; - if (terminated_asynchronously ()) - return false; - if (delaying_sweep.bumpreasons.delay ()) { // TODO need to fix Delay - last.sweep.ticks = stats.ticks.search[0] + stats.ticks.search[1]; - return false; - } - delaying_sweep.bumpreasons.bypass_delay (); - SET_EFFORT_LIMIT (tickslimit, sweep, !opts.sweepcomplete); - delaying_sweep.bumpreasons.unbypass_delay (); - - CADICAL_assert (!level); - START_SIMPLIFIER (sweep, SWEEP); - stats.sweep++; - uint64_t equivalences = stats.sweep_equivalences; - uint64_t units = stats.sweep_units; - Sweeper sweeper = Sweeper (this); - if (opts.sweepcomplete) - sweeper.limit.ticks = INT64_MAX; - else - sweeper.limit.ticks = tickslimit - stats.ticks.sweep; - sweep_set_cadical_kitten_ticks_limit (sweeper); - const unsigned scheduled = schedule_sweeping (sweeper); - uint64_t swept = 0, limit = 10; - for (;;) { - if (unsat) - break; - if (terminated_asynchronously ()) - break; - if (cadical_kitten_ticks_limit_hit (sweeper, "sweeping loop")) - break; - int idx = next_scheduled (sweeper); - if (idx == 0) - break; - flags (idx).sweep = false; -#ifndef CADICAL_QUIET - const char *res = -#endif - sweep_variable (sweeper, idx); - VERBOSE (2, "swept[%" PRIu64 "] external variable %d %s", swept, - externalize (idx), res); - if (++swept == limit) { - VERBOSE (2, - "found %" PRIu64 " equivalences and %" PRIu64 - " units after sweeping %" PRIu64 " variables ", - stats.sweep_equivalences - equivalences, - stats.sweep_units - units, swept); - limit *= 10; - } - } - VERBOSE (2, "swept %" PRIu64 " variables", swept); - equivalences = stats.sweep_equivalences - equivalences, - units = stats.sweep_units - units; - PHASE ("sweep", stats.sweep, - "found %" PRIu64 " equivalences and %" PRIu64 " units", - equivalences, units); - unschedule_sweeping (sweeper, swept, scheduled); - release_sweeper (sweeper); - - if (!unsat) { - propagated = 0; - if (!propagate ()) { - learn_empty_clause (); - } - } - - uint64_t eliminated = equivalences + units; - report ('=', !eliminated); - - if (relative (eliminated, swept) < 0.001) { - delaying_sweep.bumpreasons.bump_delay (); - } else { - delaying_sweep.bumpreasons.reduce_delay (); - } - STOP_SIMPLIFIER (sweep, SWEEP); - return eliminated; -} - -} // namespace CaDiCaL - -ABC_NAMESPACE_IMPL_END diff --git a/src/sat/cadical/cadical_terminal.cpp b/src/sat/cadical/cadical_terminal.cpp deleted file mode 100644 index c8aeea2ab5..0000000000 --- a/src/sat/cadical/cadical_terminal.cpp +++ /dev/null @@ -1,49 +0,0 @@ -#include "global.h" - -#include "internal.hpp" - -#ifdef WIN32 -#include -#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 diff --git a/src/sat/cadical/cadical_ternary.cpp b/src/sat/cadical/cadical_ternary.cpp deleted file mode 100644 index 9f99e22791..0000000000 --- a/src/sat/cadical/cadical_ternary.cpp +++ /dev/null @@ -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 diff --git a/src/sat/cadical/cadical_tier.cpp b/src/sat/cadical/cadical_tier.cpp deleted file mode 100644 index 92faf507f1..0000000000 --- a/src/sat/cadical/cadical_tier.cpp +++ /dev/null @@ -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 diff --git a/src/sat/cadical/cadical_transred.cpp b/src/sat/cadical/cadical_transred.cpp deleted file mode 100644 index 82a961deea..0000000000 --- a/src/sat/cadical/cadical_transred.cpp +++ /dev/null @@ -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 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 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 diff --git a/src/sat/cadical/cadical_unstable.cpp b/src/sat/cadical/cadical_unstable.cpp deleted file mode 100644 index c3162d70aa..0000000000 --- a/src/sat/cadical/cadical_unstable.cpp +++ /dev/null @@ -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 diff --git a/src/sat/cadical/cadical_util.cpp b/src/sat/cadical/cadical_util.cpp deleted file mode 100644 index fd9b5b29d3..0000000000 --- a/src/sat/cadical/cadical_util.cpp +++ /dev/null @@ -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 diff --git a/src/sat/cadical/cadical_var.cpp b/src/sat/cadical/cadical_var.cpp deleted file mode 100644 index e2348da7b9..0000000000 --- a/src/sat/cadical/cadical_var.cpp +++ /dev/null @@ -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 diff --git a/src/sat/cadical/cadical_veripbtracer.cpp b/src/sat/cadical/cadical_veripbtracer.cpp deleted file mode 100644 index b1f7c95a70..0000000000 --- a/src/sat/cadical/cadical_veripbtracer.cpp +++ /dev/null @@ -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 &clause, - const vector &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 &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 &clause, - const vector &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 &) { - 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 &) { - 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 diff --git a/src/sat/cadical/cadical_version.cpp b/src/sat/cadical/cadical_version.cpp deleted file mode 100644 index a0cceef63d..0000000000 --- a/src/sat/cadical/cadical_version.cpp +++ /dev/null @@ -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() -#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 diff --git a/src/sat/cadical/cadical_vivify.cpp b/src/sat/cadical/cadical_vivify.cpp deleted file mode 100644 index 8d99c29323..0000000000 --- a/src/sat/cadical/cadical_vivify.cpp +++ /dev/null @@ -1,1962 +0,0 @@ -#include "global.h" - -#include "vivify.hpp" -#include "internal.hpp" -#include "util.hpp" -#include -#include -#include - -ABC_NAMESPACE_IMPL_START - -namespace CaDiCaL { - -/*------------------------------------------------------------------------*/ - -// Vivification is a special case of asymmetric tautology elimination (ATE) -// and asymmetric literal elimination (ALE). It strengthens and removes -// clauses proven redundant through unit propagation. -// -// The original algorithm is due to a paper by Piette, Hamadi and Sais -// published at ECAI'08. We have an inprocessing version, e.g., it does not -// necessarily run-to-completion. Our version also performs conflict -// analysis and uses a new heuristic for selecting clauses to vivify. - -// Our idea is to focus on clauses with many occurrences of its literals in -// other clauses first. This both complements nicely our implementation of -// subsume, which is bounded, e.g., subsumption attempts are skipped for -// very long clauses with literals with many occurrences and also is -// stronger in the sense that it enables to remove more clauses due to unit -// propagation (AT checks). - -// While first focusing on irredundant clause we then added a separate phase -// upfront which focuses on strengthening also redundant clauses in spirit -// of the ideas presented in the IJCAI'17 paper by M. Luo, C.-M. Li, F. -// Xiao, F. Manya, and Z. Lu. - -// There is another very similar approach called 'distilliation' published -// by Han and Somenzi in DAC'07, which reorganizes the CNF in a trie data -// structure to reuse decisions and propagations along the trie. We used -// that as an inspiration but instead of building a trie we simple sort -// clauses and literals in such a way that we get the same effect. If a new -// clause is 'distilled' or 'vivified' we first check how many of the -// decisions (which are only lazily undone) can be reused for that clause. -// Reusing can be improved by picking a global literal order and sorting the -// literals in all clauses with respect to that order. We favor literals -// with more occurrences first. Then we sort clauses lexicographically with -// respect to that literal order. - -/*------------------------------------------------------------------------*/ - -// Candidate clause 'subsumed' is subsumed by 'subsuming'. - -inline void Internal::vivify_subsume_clause (Clause *subsuming, - Clause *subsumed) { - stats.subsumed++; - stats.vivifysubs++; -#ifndef CADICAL_NDEBUG - CADICAL_assert (subsuming); - CADICAL_assert (subsumed); - CADICAL_assert (subsuming != subsumed); - CADICAL_assert (!subsumed->garbage); - // size after removeing units; - int real_size_subsuming = 0, real_size_subsumed = 0; - for (auto lit : *subsuming) { - if (!val (lit) || var (lit).level) - ++real_size_subsuming; - else - CADICAL_assert (val (lit) < 0); - } - for (auto lit : *subsumed) { - if (!val (lit) || var (lit).level) - ++real_size_subsumed; - else - CADICAL_assert (val (lit) < 0); - } - CADICAL_assert (real_size_subsuming <= real_size_subsumed); -#endif - LOG (subsumed, "subsumed"); - if (subsumed->redundant) { - stats.subred++; - ++stats.vivifysubred; - } else { - stats.subirr++; - ++stats.vivifysubirr; - } - if (subsuming->garbage) { - CADICAL_assert (subsuming->size == 2); - LOG (subsuming, - "binary subsuming clause was already deleted, so undeleting"); - subsuming->garbage = false; - subsuming->glue = 1; - ++stats.current.total; - if (subsuming->redundant) - stats.current.redundant++; - else - stats.current.irredundant++, stats.irrlits += subsuming->size; - } - 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); - mark_added (subsuming); - 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'. -} - -// demoting a clause (opposite is promote from subsume.cpp) - -inline void Internal::demote_clause (Clause *c) { - stats.subsumed++; - stats.vivifydemote++; - LOG (c, "demoting"); - CADICAL_assert (!c->redundant); - mark_removed (c); - c->redundant = true; - CADICAL_assert (stats.current.irredundant > 0); - stats.current.irredundant--; - CADICAL_assert (stats.added.irredundant > 0); - stats.added.irredundant--; - stats.irrlits -= c->size; - stats.current.redundant++; - stats.added.redundant++; - c->glue = c->size - 1; - // ... and keep 'stats.added.total'. -} - -/*------------------------------------------------------------------------*/ -// For vivification we have a separate dedicated propagation routine, which -// prefers to propagate binary clauses first. It also uses its own -// assignment procedure 'vivify_assign', which does not mess with phase -// saving during search nor the conflict and other statistics and further -// can be inlined separately here. The propagation routine needs to ignore -// (large) clauses which are currently vivified. - -inline void Internal::vivify_assign (int lit, Clause *reason) { - require_mode (VIVIFY); - const int idx = vidx (lit); - CADICAL_assert (!vals[idx]); - CADICAL_assert (!flags (idx).eliminated () || !reason); - Var &v = var (idx); - v.level = level; // required to reuse decisions - v.trail = (int) trail.size (); // used in 'vivify_better_watch' - CADICAL_assert ((int) num_assigned < max_var); - num_assigned++; - v.reason = level ? reason : 0; // for conflict analysis - if (!level) - learn_unit_clause (lit); - const signed char tmp = sign (lit); - vals[idx] = tmp; - vals[-idx] = -tmp; - CADICAL_assert (val (lit) > 0); - CADICAL_assert (val (-lit) < 0); - trail.push_back (lit); - LOG (reason, "vivify assign %d", lit); -} - -// Assume negated literals in candidate clause. - -void Internal::vivify_assume (int lit) { - require_mode (VIVIFY); - level++; - control.push_back (Level (lit, trail.size ())); - LOG ("vivify decide %d", lit); - CADICAL_assert (level > 0); - CADICAL_assert (propagated == trail.size ()); - vivify_assign (lit, 0); -} - -// Dedicated routine similar to 'propagate' in 'propagate.cpp' and -// 'probe_propagate' with 'probe_propagate2' in 'probe.cpp'. Please refer -// to that code for more explanation on how propagation is implemented. - -bool Internal::vivify_propagate (int64_t &ticks) { - require_mode (VIVIFY); - CADICAL_assert (!unsat); - START (propagate); - int64_t before = propagated2 = propagated; - for (;;) { - if (propagated2 != trail.size ()) { - const int lit = -trail[propagated2++]; - LOG ("vivify propagating %d over binary clauses", -lit); - Watches &ws = watches (lit); - ticks += - 1 + cache_lines (ws.size (), sizeof (const_watch_iterator *)); - for (const auto &w : ws) { - if (!w.binary ()) - continue; - const signed char b = val (w.blit); - if (b > 0) - continue; - if (b < 0) - conflict = w.clause; // but continue - else { - ticks++; - build_chain_for_units (w.blit, w.clause, 0); - vivify_assign (w.blit, w.clause); - lrat_chain.clear (); - } - } - } else if (!conflict && propagated != trail.size ()) { - const int lit = -trail[propagated++]; - LOG ("vivify propagating %d over large clauses", -lit); - Watches &ws = watches (lit); - const const_watch_iterator eow = ws.end (); - const_watch_iterator i = ws.begin (); - ticks += 1 + cache_lines (ws.size (), sizeof (*i)); - watch_iterator j = ws.begin (); - while (i != eow) { - const Watch w = *j++ = *i++; - if (w.binary ()) - continue; - if (val (w.blit) > 0) - continue; - ticks++; - if (w.clause->garbage) { - j--; - continue; - } - literal_iterator lits = w.clause->begin (); - const int other = lits[0] ^ lits[1] ^ lit; - const signed char u = val (other); - if (u > 0) - j[-1].blit = other; - else { - const int size = w.clause->size; - const const_literal_iterator end = lits + size; - const literal_iterator middle = lits + w.clause->pos; - literal_iterator k = middle; - signed char v = -1; - int r = 0; - while (k != end && (v = val (r = *k)) < 0) - k++; - if (v < 0) { - k = lits + 2; - CADICAL_assert (w.clause->pos <= size); - while (k != middle && (v = val (r = *k)) < 0) - k++; - } - w.clause->pos = k - lits; - CADICAL_assert (lits + 2 <= k), CADICAL_assert (k <= w.clause->end ()); - if (v > 0) - j[-1].blit = r; - else if (!v) { - LOG (w.clause, "unwatch %d in", r); - lits[0] = other; - lits[1] = r; - *k = lit; - ticks++; - watch_literal (r, lit, w.clause); - j--; - } else if (!u) { - if (w.clause == ignore) { - LOG ("ignoring propagation due to clause to vivify"); - continue; - } - ticks++; - CADICAL_assert (v < 0); - vivify_chain_for_units (other, w.clause); - vivify_assign (other, w.clause); - lrat_chain.clear (); - } else { - if (w.clause == ignore) { - LOG ("ignoring conflict due to clause to vivify"); - continue; - } - CADICAL_assert (u < 0); - CADICAL_assert (v < 0); - conflict = w.clause; - break; - } - } - } - if (j != i) { - while (i != eow) - *j++ = *i++; - ws.resize (j - ws.begin ()); - } - } else - break; - } - int64_t delta = propagated2 - before; - stats.propagations.vivify += delta; - if (conflict) - LOG (conflict, "conflict"); - STOP (propagate); - return !conflict; -} - -/*------------------------------------------------------------------------*/ - -// Check whether a literal occurs less often. In the implementation below -// (search for 'int64_t score = ...' or '@4') we actually compute a -// weighted occurrence count similar to the Jeroslow Wang heuristic. - -struct vivify_more_noccs { - - Internal *internal; - - vivify_more_noccs (Internal *i) : internal (i) {} - - bool operator() (int a, int b) { - int64_t n = internal->noccs (a); - int64_t m = internal->noccs (b); - if (n > m) - return true; // larger occurrences / score first - if (n < m) - return false; // smaller occurrences / score last - if (a == -b) - return a > 0; // positive literal first - return abs (a) < abs (b); // smaller index first - } -}; - -struct vivify_more_noccs_kissat { - - Internal *internal; - - vivify_more_noccs_kissat (Internal *i) : internal (i) {} - - bool operator() (int a, int b) { - unsigned t = internal->noccs (a); - unsigned s = internal->noccs (b); - return ((t - s) | ((b - a) & ~(s - t))) >> 31; - } -}; - -// Sort candidate clauses by the number of occurrences (actually by their -// score) of their literals, with clauses to be vivified first last. We -// assume that clauses are sorted w.r.t. more occurring (higher score) -// literals first (with respect to 'vivify_more_noccs'). -// -// For example if there are the following (long irredundant) clauses -// -// 1 -3 -4 (A) -// -1 -2 3 4 (B) -// 2 -3 4 (C) -// -// then we have the following literal scores using Jeroslow Wang scores and -// normalizing it with 2^12 (which is the same as 1<<12): -// -// nocc ( 1) = 2^12 * (2^-3 ) = 512 3. -// nocc (-1) = 2^12 * (2^-4 ) = 256 6. -// nocc ( 2) = 2^12 * (2^-3 ) = 512 4. -// nocc (-2) = 2^12 * (2^-4 ) = 256 7. @1 -// nocc ( 3) = 2^12 * (2^-4 ) = 256 8. -// nocc (-3) = 2^12 * (2^-3 + 2^-3) = 1024 1. -// nocc ( 4) = 2^12 * (2^-3 + 2^-4) = 768 2. -// nocc (-4) = 2^12 * (2^-3 ) = 512 5. -// -// which gives the literal order (according to 'vivify_more_noccs') -// -// -3, 4, 1, 2, -4, -1, -2, 3 -// -// Then sorting the literals in each clause gives -// -// -3 1 -4 (A') -// 4 -1 -2 3 (B') @2 -// -3 4 2 (C') -// -// and finally sorting those clauses lexicographically w.r.t. scores is -// -// -3 4 2 (C') -// -3 1 -4 (A') @3 -// 4 -1 -2 3 (B') -// -// This order is defined by 'vivify_clause_later' which returns 'true' if -// the first clause should be vivified later than the second. - -struct vivify_clause_later { - - Internal *internal; - - vivify_clause_later (Internal *i) : internal (i) {} - - bool operator() (Clause *a, Clause *b) const { - - if (a == b) - return false; - - // First focus on clauses scheduled in the last vivify round but not - // checked yet since then. - // - if (!a->vivify && b->vivify) - return true; - if (a->vivify && !b->vivify) - return false; - - // Among redundant clauses (in redundant mode) prefer small glue. - // - if (a->redundant) { - CADICAL_assert (b->redundant); - if (a->glue > b->glue) - return true; - if (a->glue < b->glue) - return false; - } - - // Then prefer shorter size. - // - if (a->size > b->size) - return true; - if (a->size < b->size) - return false; - - // Now compare literals in the clauses lexicographically with respect to - // the literal order 'vivify_more_noccs' assuming literals are sorted - // decreasingly with respect to that order. - // - const auto eoa = a->end (), eob = b->end (); - auto j = b->begin (); - for (auto i = a->begin (); i != eoa && j != eob; i++, j++) - if (*i != *j) - return vivify_more_noccs (internal) (*j, *i); - - return j == eob; // Prefer shorter clauses to be vivified first. - } -}; - -/*------------------------------------------------------------------------*/ - -// Attempting on-the-fly subsumption during sorting when the last line is -// reached in 'vivify_clause_later' above turned out to be trouble some for -// identical clauses. This is the single point where 'vivify_clause_later' -// is not asymmetric and would require 'stable' sorting for determinism. It -// can also not be made 'complete' on-the-fly. Instead of on-the-fly -// subsumption we thus go over the sorted scheduled in a linear scan -// again and remove certain subsumed clauses (the subsuming clause is -// syntactically a prefix of the subsumed clause), which includes -// those troublesome syntactically identical clauses. - -struct vivify_flush_smaller { - - bool operator() (Clause *a, Clause *b) const { - - const auto eoa = a->end (), eob = b->end (); - auto i = a->begin (), j = b->begin (); - for (; i != eoa && j != eob; i++, j++) - if (*i != *j) - return *i < *j; - - return j == eob && i != eoa; - } -}; - -void Internal::flush_vivification_schedule (std::vector &schedule, - int64_t &ticks) { - ticks += 1 + 3 * cache_lines (schedule.size (), sizeof (Clause *)); - stable_sort (schedule.begin (), schedule.end (), vivify_flush_smaller ()); - - const auto end = schedule.end (); - auto j = schedule.begin (), i = j; - - Clause *prev = 0; - int64_t subsumed = 0; - for (; i != end; i++) { - ticks++; - Clause *c = *j++ = *i; - if (!prev || c->size < prev->size) { - prev = c; - continue; - } - const auto eop = prev->end (); - auto k = prev->begin (); - for (auto l = c->begin (); k != eop; k++, l++) - if (*k != *l) - break; - if (k == eop) { - LOG (c, "found subsumed"); - LOG (prev, "subsuming"); - CADICAL_assert (!c->garbage); - CADICAL_assert (!prev->garbage); - CADICAL_assert (c->redundant || !prev->redundant); - mark_garbage (c); - subsumed++; - j--; - } else - prev = c; - } - - if (subsumed) - PHASE ("vivify", stats.vivifications, - "flushed %" PRId64 " subsumed scheduled clauses", subsumed); - - stats.vivifysubs += subsumed; - - if (subsumed) { - schedule.resize (j - schedule.begin ()); - shrink_vector (schedule); - } else - CADICAL_assert (j == end); -} - -/*------------------------------------------------------------------------*/ - -// Depending on whether we try to vivify redundant or irredundant clauses, -// we schedule a clause to be vivified. For redundant clauses we initially -// only try to vivify them if they are likely to survive the next 'reduce' -// operation, but this left the last schedule empty most of the time. - -bool Internal::consider_to_vivify_clause (Clause *c) { - if (c->garbage) - return false; - if (opts.vivifyonce >= 1 && c->redundant && c->vivified) - return false; - if (opts.vivifyonce >= 2 && !c->redundant && c->vivified) - return false; - if (!c->redundant) - return true; - CADICAL_assert (c->redundant); - - // likely_to_be_kept_clause is too aggressive at removing tier-3 clauses - return true; -} - -/*------------------------------------------------------------------------*/ - -// In a strengthened clause the idea is to move non-false literals to the -// front, followed by false literals. Literals are further sorted by -// reverse assignment order. The goal is to use watches which require to -// backtrack as few as possible decision levels. - -struct vivify_better_watch { - - Internal *internal; - - vivify_better_watch (Internal *i) : internal (i) {} - - bool operator() (int a, int b) { - - const signed char av = internal->val (a), bv = internal->val (b); - - if (av >= 0 && bv < 0) - return true; - if (av < 0 && bv >= 0) - return false; - - return internal->var (a).trail > internal->var (b).trail; - } -}; - -// Common code to actually strengthen a candidate clause. The resulting -// strengthened clause is communicated through the global 'clause'. - -void Internal::vivify_strengthen (Clause *c) { - - CADICAL_assert (!clause.empty ()); - - if (clause.size () == 1) { - - backtrack_without_updating_phases (); - const int unit = clause[0]; - LOG (c, "vivification shrunken to unit %d", unit); - CADICAL_assert (!val (unit)); - assign_unit (unit); - // lrat_chain.clear (); done in search_assign - stats.vivifyunits++; - - bool ok = propagate (); - if (!ok) - learn_empty_clause (); - - } else { - - // See explanation before 'vivify_better_watch' above. - // - sort (clause.begin (), clause.end (), vivify_better_watch (this)); - - int new_level = level; - - const int lit0 = clause[0]; - signed char val0 = val (lit0); - if (val0 < 0) { - const int level0 = var (lit0).level; - LOG ("1st watch %d negative at level %d", lit0, level0); - new_level = level0 - 1; - } - - const int lit1 = clause[1]; - const signed char val1 = val (lit1); - if (val1 < 0 && !(val0 > 0 && var (lit0).level <= var (lit1).level)) { - const int level1 = var (lit1).level; - LOG ("2nd watch %d negative at level %d", lit1, level1); - new_level = level1 - 1; - } - - CADICAL_assert (new_level >= 0); - if (new_level < level) - backtrack (new_level); - - CADICAL_assert (val (lit0) >= 0); - CADICAL_assert (val (lit1) >= 0 || (val (lit0) > 0 && val (lit1) < 0 && - var (lit0).level <= var (lit1).level)); - - Clause *d = new_clause_as (c); - LOG (c, "before vivification"); - LOG (d, "after vivification"); - (void) d; - } - clause.clear (); - mark_garbage (c); - lrat_chain.clear (); - ++stats.vivifystrs; -} - -void Internal::vivify_sort_watched (Clause *c) { - - sort (c->begin (), c->end (), vivify_better_watch (this)); - - int new_level = level; - - const int lit0 = c->literals[0]; - signed char val0 = val (lit0); - if (val0 < 0) { - const int level0 = var (lit0).level; - LOG ("1st watch %d negative at level %d", lit0, level0); - new_level = level0 - 1; - } - - const int lit1 = c->literals[1]; - const signed char val1 = val (lit1); - if (val1 < 0 && !(val0 > 0 && var (lit0).level <= var (lit1).level)) { - const int level1 = var (lit1).level; - LOG ("2nd watch %d negative at level %d", lit1, level1); - new_level = level1 - 1; - } - - CADICAL_assert (new_level >= 0); - if (new_level < level) - backtrack (new_level); - - CADICAL_assert (val (lit0) >= 0); - CADICAL_assert (val (lit1) >= 0 || (val (lit0) > 0 && val (lit1) < 0 && - var (lit0).level <= var (lit1).level)); -} -// Conflict analysis from 'start' which learns a decision only clause. -// -// We cannot use the stack-based implementation of Kissat, because we need -// to iterate over the conflict in topological ordering to produce a valid -// LRAT proof - -void Internal::vivify_analyze (Clause *start, bool &subsumes, - Clause **subsuming, - const Clause *const candidate, int implied, - bool &redundant) { - const auto &t = &trail; // normal trail, so next_trail is wrong - int i = t->size (); // Start at end-of-trail. - Clause *reason = start; - CADICAL_assert (reason); - CADICAL_assert (!trail.empty ()); - int uip = trail.back (); - bool mark_implied = (implied); - - while (i >= 0) { - if (reason) { - redundant = (redundant || reason->redundant); - subsumes = (start != reason && reason->size <= start->size); - LOG (reason, "resolving on %d with", uip); - for (auto other : *reason) { - const Var v = var (other); - Flags &f = flags (other); - if (!marked2 (other) && v.level) { - LOG ("not subsuming due to lit %d", other); - subsumes = false; - } - if (!val (other)) { - LOG ("skipping unset lit %d", other); - continue; - } - if (other == uip) { - continue; - } - if (!v.level) { - if (f.seen || !lrat || reason == start) - continue; - LOG ("unit reason for %d", other); - int64_t id = unit_id (-other); - LOG ("adding unit reason %zd for %d", id, other); - unit_chain.push_back (id); - f.seen = true; - analyzed.push_back (other); - continue; - } - if (mark_implied && other != implied) { - LOG ("skipping non-implied literal %d on current level", other); - continue; - } - - CADICAL_assert (val (other)); - if (f.seen) - continue; - LOG ("pushing lit %d", other); - analyzed.push_back (other); - f.seen = true; - } - if (start->redundant) { - const int new_glue = recompute_glue (start); - promote_clause (start, new_glue); - } - if (subsumes) { - CADICAL_assert (reason); - LOG (reason, "clause found subsuming"); - LOG (candidate, "clause found subsumed"); - *subsuming = reason; - return; - } - } else { - LOG ("vivify analyzed decision %d", uip); - clause.push_back (-uip); - } - mark_implied = false; - - uip = 0; - while (!uip && i > 0) { - CADICAL_assert (i > 0); - const int lit = (*t)[--i]; - if (!var (lit).level) - continue; - if (flags (lit).seen) - uip = lit; - } - if (!uip) - break; - LOG ("uip is %d", uip); - Var &w = var (uip); - reason = w.reason; - if (lrat && reason) - lrat_chain.push_back (reason->id); - } - (void) candidate; -} - -void Internal::vivify_deduce (Clause *candidate, Clause *conflict, - int implied, Clause **subsuming, - bool &redundant) { - CADICAL_assert (lrat_chain.empty ()); - bool subsumes; - Clause *reason; - - CADICAL_assert (clause.empty ()); - if (implied) { - reason = candidate; - mark2 (candidate); - const int not_implied = -implied; - CADICAL_assert (var (not_implied).level); - Flags &f = flags (not_implied); - f.seen = true; - LOG ("pushing implied lit %d", not_implied); - analyzed.push_back (not_implied); - clause.push_back (implied); - } else { - reason = (conflict ? conflict : candidate); - CADICAL_assert (reason); - CADICAL_assert (!reason->garbage); - mark2 (candidate); - subsumes = (candidate != reason); - redundant = reason->redundant; - LOG (reason, "resolving with"); - if (lrat) - lrat_chain.push_back (reason->id); - for (auto lit : *reason) { - const Var &v = var (lit); - Flags &f = flags (lit); - CADICAL_assert (val (lit) < 0); - if (!v.level) { - if (!lrat) - continue; - LOG ("adding unit %d", lit); - if (!f.seen) { - // nevertheless we can use var (l) as if l was still assigned - // because var is updated lazily - int64_t id = unit_id (-lit); - LOG ("adding unit reason %zd for %d", id, lit); - unit_chain.push_back (id); - } - f.seen = true; - analyzed.push_back (lit); - continue; - } - CADICAL_assert (v.level); - if (!marked2 (lit)) { - LOG ("lit %d is not marked", lit); - subsumes = false; - } - LOG ("analyzing lit %d", lit); - LOG ("pushing lit %d", lit); - analyzed.push_back (lit); - f.seen = true; - } - if (reason != candidate && reason->redundant) { - const int new_glue = recompute_glue (reason); - promote_clause (reason, new_glue); - } - if (subsumes) { - CADICAL_assert (candidate != reason); -#ifndef CADICAL_NDEBUG - int nonfalse_reason = 0; - for (auto lit : *reason) - if (!fixed (lit)) - ++nonfalse_reason; - - int nonfalse_candidate = 0; - for (auto lit : *candidate) - if (!fixed (lit)) - ++nonfalse_candidate; - - CADICAL_assert (nonfalse_reason <= nonfalse_candidate); -#endif - LOG (candidate, "vivify subsumed 0"); - LOG (reason, "vivify subsuming 0"); - *subsuming = reason; - unmark (candidate); - if (lrat) - lrat_chain.clear (); - return; - } - } - - vivify_analyze (reason, subsumes, subsuming, candidate, implied, - redundant); - unmark (candidate); - if (subsumes) { - CADICAL_assert (*subsuming); - LOG (candidate, "vivify subsumed"); - LOG (*subsuming, "vivify subsuming"); - if (lrat) - lrat_chain.clear (); - } -} -/*------------------------------------------------------------------------*/ - -bool Internal::vivify_shrinkable (const std::vector &sorted, - Clause *conflict) { - - unsigned count_implied = 0; - for (auto lit : sorted) { - const signed char value = val (lit); - if (!value) { - LOG ("vivification unassigned %d", lit); - return true; - } - if (value > 0) { - LOG ("vivification implied satisfied %d", lit); - if (conflict) - return true; - if (count_implied++) { - LOG ("at least one implied literal with conflict thus shrinking"); - return true; - } - } else { - CADICAL_assert (value < 0); - const Var &v = var (lit); - const Flags &f = flags (lit); - if (!v.level) - continue; - if (!f.seen) { - LOG ("vivification non-analyzed %d", lit); - return true; - } - if (v.reason) { - LOG ("vivification implied falsified %d", lit); - return true; - } - } - } - return false; -} -/*------------------------------------------------------------------------*/ - -inline void Internal::vivify_increment_stats (const Vivifier &vivifier) { - switch (vivifier.tier) { - case Vivify_Mode::TIER1: - ++stats.vivifystred1; - break; - case Vivify_Mode::TIER2: - ++stats.vivifystred2; - break; - case Vivify_Mode::TIER3: - ++stats.vivifystred3; - break; - default: - CADICAL_assert (vivifier.tier == Vivify_Mode::IRREDUNDANT); - ++stats.vivifystrirr; - break; - } -} -/*------------------------------------------------------------------------*/ -// instantiate last literal (see the description of the hack track 2023), -// fix the watches and -// backtrack two level back -bool Internal::vivify_instantiate ( - const std::vector &sorted, Clause *c, - std::vector> &lrat_stack, - int64_t &ticks) { - LOG ("now trying instantiation"); - conflict = nullptr; - const int lit = sorted.back (); - LOG ("vivify instantiation"); - CADICAL_assert (!var (lit).reason); - CADICAL_assert (var (lit).level); - CADICAL_assert (val (lit)); - backtrack (level - 1); - CADICAL_assert (val (lit) == 0); - stats.vivifydecs++; - vivify_assume (lit); - bool ok = vivify_propagate (ticks); - if (!ok) { - LOG (c, "instantiate success with literal %d in", lit); - stats.vivifyinst++; - // strengthen clause - if (lrat) { - clear_analyzed_literals (); - CADICAL_assert (lrat_chain.empty ()); - vivify_build_lrat (0, c, lrat_stack); - vivify_build_lrat (0, conflict, lrat_stack); - clear_analyzed_literals (); - } - int remove = lit; - conflict = nullptr; - unwatch_clause (c); - backtrack_without_updating_phases (level - 2); - strengthen_clause (c, remove); - vivify_sort_watched (c); - watch_clause (c); - CADICAL_assert (!conflict); - return true; - } else { - LOG ("vivify instantiation failed"); - return false; - } -} - -/*------------------------------------------------------------------------*/ - -// Main function: try to vivify this candidate clause in the given mode. - -bool Internal::vivify_clause (Vivifier &vivifier, Clause *c) { - - CADICAL_assert (c->size > 2); // see (NO-BINARY) below - CADICAL_assert (analyzed.empty ()); - - c->vivify = false; // mark as checked / tried - c->vivified = true; // and globally remember - - CADICAL_assert (!c->garbage); - - auto &lrat_stack = vivifier.lrat_stack; - auto &ticks = vivifier.ticks; - ticks++; - - // First check whether the candidate clause is already satisfied and at - // the same time copy its non fixed literals to 'sorted'. The literals - // in the candidate clause might not be sorted anymore due to replacing - // watches during propagation, even though we sorted them initially - // while pushing the clause onto the schedule and sorting the schedule. - // - auto &sorted = vivifier.sorted; - sorted.clear (); - - for (const auto &lit : *c) { - const int tmp = fixed (lit); - if (tmp > 0) { - LOG (c, "satisfied by propagated unit %d", lit); - mark_garbage (c); - return false; - } else if (!tmp) - sorted.push_back (lit); - } - - CADICAL_assert (sorted.size () > 1); - if (sorted.size () == 2) { - LOG ("skipping actual binary"); - return false; - } - - sort (sorted.begin (), sorted.end (), vivify_more_noccs_kissat (this)); - - // The actual vivification checking is performed here, by assuming the - // negation of each of the remaining literals of the clause in turn and - // propagating it. If a conflict occurs or another literal in the - // clause becomes assigned during propagation, we can stop. - // - LOG (c, "vivification checking"); - stats.vivifychecks++; - - // If the decision 'level' is non-zero, then we can reuse decisions for - // the previous candidate, and avoid re-propagating them. In preliminary - // experiments this saved between 30%-50% decisions (and thus - // propagations), which in turn lets us also vivify more clauses within - // the same propagation bounds, or terminate earlier if vivify runs to - // completion. - // - if (level) { -#ifdef LOGGING - int orig_level = level; -#endif - // First check whether this clause is actually a reason for forcing - // one of its literals to true and then backtrack one level before - // that happened. Otherwise this clause might be incorrectly - // considered to be redundant or if this situation is checked then - // redundancy by other clauses using this forced literal becomes - // impossible. - // - int forced = 0; - - // This search could be avoided if we would eagerly set the 'reason' - // boolean flag of clauses, which however we do not want to do for - // binary clauses (during propagation) and thus would still require - // a version of 'protect_reason' for binary clauses during 'reduce' - // (well binary clauses are not collected during 'reduce', but again - // this exception from the exception is pretty complex and thus a - // simply search here is probably easier to understand). - - for (const auto &lit : *c) { - const signed char tmp = val (lit); - if (tmp < 0) - continue; - if (tmp > 0 && var (lit).reason == c) - forced = lit; - break; - } - if (forced) { - LOG ("clause is reason forcing %d", forced); - CADICAL_assert (var (forced).level); - backtrack_without_updating_phases (var (forced).level - 1); - } - - // As long the (remaining) literals of the sorted clause match - // decisions on the trail we just reuse them. - // - if (level) { - - int l = 1; // This is the decision level we want to reuse. - - for (const auto &lit : sorted) { - CADICAL_assert (!fixed (lit)); - const int decision = control[l].decision; - if (-lit == decision) { - LOG ("reusing decision %d at decision level %d", decision, l); - stats.vivifyreused++; - if (++l > level) - break; - } else { - LOG ("literal %d does not match decision %d at decision level %d", - lit, decision, l); - backtrack_without_updating_phases (l - 1); - break; - } - } - } - - LOG ("reused %d decision levels from %d", level, orig_level); - } - - LOG (sorted, "sorted size %zd probing schedule", sorted.size ()); - - // Make sure to ignore this clause during propagation. This is not that - // easy for binary clauses (NO-BINARY), e.g., ignoring binary clauses, - // without changing 'propagate'. Actually, we do not want to remove binary - // clauses which are subsumed. Those are hyper binary resolvents and - // should be kept as learned clauses instead, unless they are transitive - // in the binary implication graph, which in turn is detected during - // transitive reduction in 'transred'. - // - ignore = c; - - int subsume = 0; // determined to be redundant / subsumed - - // If the candidate is redundant, i.e., we are in redundant mode, the - // clause is subsumed (in one of the two cases below where 'subsume' is - // assigned) and further all reasons involved are only binary clauses, - // then this redundant clause is what we once called a hidden tautology, - // and even for redundant clauses it makes sense to remove the candidate. - // It does not add anything to propagation power of the formula. This is - // the same argument as removing transitive clauses in the binary - // implication graph during transitive reduction. - // - - // Go over the literals in the candidate clause in sorted order. - // - for (const auto &lit : sorted) { - - // Exit loop as soon a literal is positively implied (case '@5' below) - // or propagation of the negation of a literal fails ('@6'). - // - if (subsume) - break; - - // We keep on assigning literals, even though we know already that we - // can remove one (was negatively implied), since we either might run - // into the 'subsume' case above or more false literals become implied. - // In any case this might result in stronger vivified clauses. As a - // consequence continue with this loop even if 'remove' is non-zero. - - const signed char tmp = val (lit); - - if (tmp) { // literal already assigned - - const Var &v = var (lit); - CADICAL_assert (v.level); - if (!v.reason) { - LOG ("skipping decision %d", lit); - continue; - } - - if (tmp < 0) { - CADICAL_assert (v.level); - LOG ("literal %d is already false and can be removed", lit); - continue; - } - - CADICAL_assert (tmp > 0); - LOG ("subsumed since literal %d already true", lit); - subsume = lit; // will be able to subsume candidate '@5' - break; - } - - CADICAL_assert (!tmp); - - stats.vivifydecs++; - vivify_assume (-lit); - LOG ("negated decision %d score %" PRId64 "", lit, noccs (lit)); - - if (!vivify_propagate (ticks)) { - break; // hot-spot - } - } - - if (subsume) { - int better_subsume_trail = var (subsume).trail; - for (auto lit : sorted) { - if (val (lit) <= 0) - continue; - const Var v = var (lit); - if (v.trail < better_subsume_trail) { - LOG ("improving subsume from %d at %d to %d at %d", subsume, - better_subsume_trail, lit, v.trail); - better_subsume_trail = v.trail; - subsume = lit; - } - } - } - - Clause *subsuming = nullptr; - bool redundant = false; - const int level_after_assumptions = level; - CADICAL_assert (level_after_assumptions); - vivify_deduce (c, conflict, subsume, &subsuming, redundant); - - bool res; - - // reverse lrat_chain. We could probably work with reversed iterators - // (views) to be more efficient but we would have to distinguish in proof - // - if (lrat) { - for (auto id : unit_chain) - lrat_chain.push_back (id); - unit_chain.clear (); - reverse (lrat_chain.begin (), lrat_chain.end ()); - } - - if (subsuming) { - CADICAL_assert (c != subsuming); - LOG (c, "deleting subsumed clause"); - if (c->redundant && subsuming->redundant && c->glue < subsuming->glue) { - promote_clause (c, c->glue); - } - vivify_subsume_clause (subsuming, c); - res = false; - // stats.vivifysubs++; // already done in vivify_subsume_clause - } else if (vivify_shrinkable (sorted, conflict)) { - vivify_increment_stats (vivifier); - LOG ("vivify succeeded, learning new clause"); - clear_analyzed_literals (); - LOG (lrat_chain, "lrat"); - LOG (clause, "learning clause"); - conflict = nullptr; // TODO dup from below - vivify_strengthen (c); - res = true; - } else if (subsume && c->redundant) { - LOG (c, "vivification implied"); - mark_garbage (c); - ++stats.vivifyimplied; - res = true; - } else if ((conflict || subsume) && !c->redundant && !redundant) { - LOG ("demote clause from irredundant to redundant"); - if (opts.vivifydemote) { - demote_clause (c); - const int new_glue = recompute_glue (c); - promote_clause (c, new_glue); - res = false; - } else { - mark_garbage (c); - ++stats.vivifyimplied; - res = true; - } - } else if (subsume) { - LOG (c, "no vivification instantiation with implied literal %d", - subsume); - CADICAL_assert (!c->redundant); - CADICAL_assert (redundant); - res = false; - ++stats.vivifyimplied; - } else { - CADICAL_assert (level > 2); - CADICAL_assert ((size_t) level == sorted.size ()); - LOG (c, "vivification failed on"); - lrat_chain.clear (); - CADICAL_assert (!subsume); - if (!subsume && opts.vivifyinst) { - res = vivify_instantiate (sorted, c, lrat_stack, ticks); - CADICAL_assert (!conflict); - } else { - LOG ("cannot apply instantiation"); - res = false; - } - } - - if (conflict && level == level_after_assumptions) { - LOG ("forcing backtracking at least one level after conflict"); - backtrack_without_updating_phases (level - 1); - } - - clause.clear (); - clear_analyzed_literals (); // TODO why needed? - lrat_chain.clear (); - conflict = nullptr; - return res; -} - -// when we can strengthen clause c we have to build lrat. -// uses f.seen so do not forget to clear flags afterwards. -// this can happen in three cases. (1), (2) are only sound in redundant mode -// (1) literal l in c is positively implied. in this case we call the -// function with (l, l.reason). This justifies the reduction because the new -// clause c' will include l and all decisions so l.reason is a conflict -// assuming -c' (2) conflict during vivify propagation. function is called -// with (0, conflict) similar to (1) but more direct. (3) some literals in c -// are negatively implied and can therefore be removed. in this case we call -// the function with (0, c). originally we justified each literal in c on -// its own but this is not actually necessary. -// - -// Non-recursive version, as some bugs have been found. DFS over the -// reasons with preordering (aka we explore the entire reason before -// exploring deeper) -void Internal::vivify_build_lrat ( - int lit, Clause *reason, - std::vector> &stack) { - CADICAL_assert (stack.empty ()); - stack.push_back ({lit, reason, false}); - while (!stack.empty ()) { - int lit; - Clause *reason; - bool finished; - std::tie (lit, reason, finished) = stack.back (); - LOG ("VIVIFY LRAT justifying %d", lit); - stack.pop_back (); - if (lit && flags (lit).seen) { - LOG ("skipping already justified"); - continue; - } - if (finished) { - lrat_chain.push_back (reason->id); - if (lit && reason) { - Flags &f = flags (lit); - f.seen = true; - analyzed.push_back (lit); // CADICAL_assert (val (other) < 0); - CADICAL_assert (flags (lit).seen); - } - continue; - } else - stack.push_back ({lit, reason, true}); - for (const auto &other : *reason) { - if (other == lit) - continue; - Var &v = var (other); - Flags &f = flags (other); - if (f.seen) - continue; - if (!v.level) { - const int64_t id = unit_id (-other); - lrat_chain.push_back (id); - f.seen = true; - analyzed.push_back (other); - continue; - } - if (v.reason) { // recursive justification - LOG ("VIVIFY LRAT pushing %d", other); - stack.push_back ({other, v.reason, false}); - } - } - } - stack.clear (); -} - -// calculate lrat_chain -// -inline void Internal::vivify_chain_for_units (int lit, Clause *reason) { - if (!lrat) - return; - // LOG ("building chain for units"); bad line for debugging - // equivalence if (opts.chrono && assignment_level (lit, reason)) return; - if (level) - return; // not decision level 0 - CADICAL_assert (lrat_chain.empty ()); - for (auto &reason_lit : *reason) { - if (lit == reason_lit) - continue; - CADICAL_assert (val (reason_lit)); - 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); -} - -vivify_ref create_ref (Internal *internal, Clause *c) { - LOG (c, "creating vivify_refs of clause"); - vivify_ref ref; - ref.clause = c; - ref.size = c->size; - for (int i = 0; i < COUNTREF_COUNTS; ++i) - ref.count[i] = 0; - ref.vivify = c->vivify; - int lits[COUNTREF_COUNTS] = {0}; - for (int i = 0; i != std::min (COUNTREF_COUNTS, c->size); ++i) { - int best = 0; - unsigned best_count = 0; - for (auto lit : *c) { - LOG ("to find best number of occurrences for literal %d, looking at " - "literal %d", - i, lit); - for (int j = 0; j != i; ++j) { - LOG ("comparing %d with literal %d", lit, lits[j]); - if (lits[j] == lit) - goto CONTINUE_WITH_NEXT_LITERAL; - } - { - const int64_t lit_count = internal->noccs (lit); - CADICAL_assert (lit_count); - LOG ("checking literal %d with %zd occurrences", lit, lit_count); - if (lit_count <= best_count) - continue; - best_count = lit_count; - best = lit; - } - CONTINUE_WITH_NEXT_LITERAL:; - } - CADICAL_assert (best); - CADICAL_assert (best_count); - CADICAL_assert (best_count < UINT32_MAX); - ref.count[i] = - ((uint64_t) best_count << 32) + (uint64_t) internal->vlit (best); - LOG ("final count at position %d is %d - %d: %lu", i, best, best_count, - ref.count[i]); - lits[i] = best; - } - return ref; -} -/*------------------------------------------------------------------------*/ -inline void -Internal::vivify_prioritize_leftovers ([[maybe_unused]] char tag, - size_t prioritized, - std::vector &schedule) { - if (prioritized) { - PHASE ("vivify", stats.vivifications, - "[phase %c] leftovers of %" PRId64 " clause", tag, prioritized); - } else { - PHASE ("vivify", stats.vivifications, - "[phase %c] prioritizing all clause", tag); - for (auto c : schedule) - c->vivify = true; - } - const size_t max = opts.vivifyschedmax; - if (schedule.size () > max) { - if (prioritized) { - std::partition (begin (schedule), end (schedule), - [] (Clause *c) { return c->vivify; }); - } - schedule.resize (max); - } - // let's try to save a bit of memory - shrink_vector (schedule); -} - -void Internal::vivify_initialize (Vivifier &vivifier, int64_t &ticks) { - - const int tier1 = vivifier.tier1_limit; - const int tier2 = vivifier.tier2_limit; - // Count the number of occurrences of literals in all clauses, - // particularly binary clauses, which are usually responsible - // for most of the propagations. - // - init_noccs (); - - // Disconnect all watches since we sort literals within clauses. - // - CADICAL_assert (watching ()); -#if 0 - clear_watches (); -#endif - - size_t prioritized_irred = 0, prioritized_tier1 = 0, - prioritized_tier2 = 0, prioritized_tier3 = 0; - for (const auto &c : clauses) { - ++ticks; - if (c->size == 2) - continue; // see also (NO-BINARY) above - if (!consider_to_vivify_clause (c)) - continue; - - // This computes an approximation of the Jeroslow Wang heuristic - // score - // - // nocc (L) = sum 2^(12-|C|) - // L in C in F - // - // but we cap the size at 12, that is all clauses of size 12 and - // larger contribute '1' to the score, which allows us to use 'long' - // numbers. See the example above (search for '@1'). - // - const int shift = 12 - c->size; - const int64_t score = shift < 1 ? 1 : (1l << shift); // @4 - for (const auto lit : *c) { - noccs (lit) += score; - } - LOG (c, "putting clause in candidates"); - if (!c->redundant) - vivifier.schedule_irred.push_back (c), - prioritized_irred += (c->vivify); - else if (c->glue <= tier1) - vivifier.schedule_tier1.push_back (c), - prioritized_tier1 += (c->vivify); - else if (c->glue <= tier2) - vivifier.schedule_tier2.push_back (c), - prioritized_tier2 += (c->vivify); - else - vivifier.schedule_tier3.push_back (c), - prioritized_tier3 += (c->vivify); - ++ticks; - } - - vivify_prioritize_leftovers ('x', prioritized_irred, - vivifier.schedule_irred); - vivify_prioritize_leftovers ('u', prioritized_tier1, - vivifier.schedule_tier1); - vivify_prioritize_leftovers ('v', prioritized_tier2, - vivifier.schedule_tier2); - vivify_prioritize_leftovers ('w', prioritized_tier3, - vivifier.schedule_tier3); - - if (opts.vivifyflush) { - clear_watches (); - for (auto &sched : vivifier.schedules) { - for (const auto &c : sched) { - // Literals in scheduled clauses are sorted with their highest score - // literals first (as explained above in the example at '@2'). This - // is also needed in the prefix subsumption checking below. We do an - // approximation below that is done only in the vivify_ref structure - // below. - // - sort (c->begin (), c->end (), vivify_more_noccs (this)); - } - // Flush clauses subsumed by another clause with the same prefix, - // which also includes flushing syntactically identical clauses. - // - flush_vivification_schedule (sched, ticks); - } - connect_watches (); // watch all relevant clauses - } -#if 0 - connect_watches (); // watch all relevant clauses - vivify_propagate (ticks); -#endif - vivify_propagate (ticks); -} - -inline std::vector ¤t_refs_schedule (Vivifier &vivifier) { - switch (vivifier.tier) { - case Vivify_Mode::TIER1: - return vivifier.refs_schedule_tier1; - break; - case Vivify_Mode::TIER2: - return vivifier.refs_schedule_tier2; - break; - case Vivify_Mode::TIER3: - return vivifier.refs_schedule_tier3; - break; - default: - return vivifier.refs_schedule_irred; - break; - } -#ifdef WIN32 - __assume(false); -#else - __builtin_unreachable (); -#endif -} - -inline std::vector ¤t_schedule (Vivifier &vivifier) { - switch (vivifier.tier) { - case Vivify_Mode::TIER1: - return vivifier.schedule_tier1; - break; - case Vivify_Mode::TIER2: - return vivifier.schedule_tier2; - break; - case Vivify_Mode::TIER3: - return vivifier.schedule_tier3; - break; - default: - return vivifier.schedule_irred; - break; - } -#ifdef WIN32 - __assume(false); -#else - __builtin_unreachable (); -#endif -} - -struct vivify_refcount_rank { - int offset; - vivify_refcount_rank (int j) : offset (j) { - CADICAL_assert (offset < COUNTREF_COUNTS); - } - typedef uint64_t Type; - Type operator() (const vivify_ref &a) const { return a.count[offset]; } -}; - -struct vivify_refcount_smaller { - int offset; - vivify_refcount_smaller (int j) : offset (j) { - CADICAL_assert (offset < COUNTREF_COUNTS); - } - bool operator() (const vivify_ref &a, const vivify_ref &b) const { - const auto s = vivify_refcount_rank (offset) (a); - const auto t = vivify_refcount_rank (offset) (b); - return s < t; - } -}; - -struct vivify_inversesize_rank { - vivify_inversesize_rank () {} - typedef uint64_t Type; - Type operator() (const vivify_ref &a) const { return ~a.size; } -}; - -struct vivify_inversesize_smaller { - vivify_inversesize_smaller () {} - bool operator() (const vivify_ref &a, const vivify_ref &b) const { - const auto s = vivify_inversesize_rank () (a); - const auto t = vivify_inversesize_rank () (b); - return s < t; - } -}; - -/*------------------------------------------------------------------------*/ -// There are two modes of vivification, one using all clauses and one -// focusing on irredundant clauses only. The latter variant working on -// irredundant clauses only can also remove irredundant asymmetric -// tautologies (clauses subsumed through unit propagation), which in -// redundant mode is incorrect (due to propagating over redundant clauses). - -void Internal::vivify_round (Vivifier &vivifier, int64_t ticks_limit) { - - if (unsat) - return; - if (terminated_asynchronously ()) - return; - - PHASE ("vivify", stats.vivifications, - "starting %c vivification round ticks limit %" PRId64 "", - vivifier.tag, ticks_limit); - - PHASE ("vivify", stats.vivifications, - "starting %c vivification round ticks limit %" PRId64 "", - vivifier.tag, ticks_limit); - - CADICAL_assert (watching ()); - - auto &refs_schedule = current_refs_schedule (vivifier); - auto &schedule = current_schedule (vivifier); - - int64_t ticks = 1 + schedule.size (); - - // Sort candidates, with first to be tried candidate clause last, i.e., - // many occurrences and high score literals) as in the example explained - // above (search for '@3'). - // - if (vivifier.tier != Vivify_Mode::IRREDUNDANT || - irredundant () / 10 < redundant ()) { - // Literals in scheduled clauses are sorted with their highest score - // literals first (as explained above in the example at '@2'). This is - // also needed in the prefix subsumption checking below. We do an - // approximation below that is done only in the vivify_ref structure - // below. - // - - // first build the schedule with vivifier_refs - auto end_schedule = end (schedule); - refs_schedule.resize (schedule.size ()); - std::transform (begin (schedule), end_schedule, begin (refs_schedule), - [&] (Clause *c) { return create_ref (this, c); }); - // now sort by size - MSORT (opts.radixsortlim, refs_schedule.begin (), refs_schedule.end (), - vivify_inversesize_rank (), vivify_inversesize_smaller ()); - // now (stable) sort by number of occurrences - for (int i = 0; i < COUNTREF_COUNTS; ++i) { - const int offset = COUNTREF_COUNTS - 1 - i; - MSORT (opts.radixsortlim, refs_schedule.begin (), - refs_schedule.end (), vivify_refcount_rank (offset), - vivify_refcount_smaller (offset)); - } - // force left-overs at the end - std::stable_partition (begin (refs_schedule), end (refs_schedule), - [] (vivify_ref c) { return !c.vivify; }); - std::transform (begin (refs_schedule), end (refs_schedule), - begin (schedule), - [] (vivify_ref c) { return c.clause; }); - erase_vector (refs_schedule); - LOG ("clause after sorting final:"); - } else { - // skip sorting but still put clauses with the vivify tag at the end to - // be done first Kissat does this implicitely by going twice over all - // clauses - std::stable_partition (begin (schedule), end (schedule), - [] (Clause *c) { return !c->vivify; }); - } - - // Remember old values of counters to summarize after each round with - // verbose messages what happened in that round. - // - int64_t checked = stats.vivifychecks; - int64_t subsumed = stats.vivifysubs; - int64_t strengthened = stats.vivifystrs; - int64_t units = stats.vivifyunits; - - int64_t scheduled = schedule.size (); - stats.vivifysched += scheduled; - - PHASE ("vivify", stats.vivifications, - "scheduled %" PRId64 " clauses to be vivified %.0f%%", scheduled, - percent (scheduled, stats.current.irredundant)); - - // Limit the number of propagations during vivification as in 'probe'. - // - const int64_t limit = ticks_limit - stats.ticks.vivify; - CADICAL_assert (limit >= 0); - - // the clauses might still contain set literals, so propagation since the - // beginning - propagated2 = propagated = 0; - - if (!unsat && !propagate ()) { - LOG ("propagation after connecting watches in inconsistency"); - learn_empty_clause (); - } - - vivifier.ticks = ticks; - int retry = 0; - while (!unsat && !terminated_asynchronously () && !schedule.empty () && - vivifier.ticks < limit) { - Clause *c = schedule.back (); // Next candidate. - schedule.pop_back (); - if (vivify_clause (vivifier, c) && !c->garbage && c->size > 2 && - retry < opts.vivifyretry) { - ++retry; - schedule.push_back (c); - } else - retry = 0; - } - - if (level) - backtrack_without_updating_phases (); - - if (!unsat) { - int64_t still_need_to_be_vivified = schedule.size (); -#if 0 - // in the current round we have new_clauses_to_vivify @ leftovers from previous round There are - // now two possibilities: (i) we consider all clauses as leftovers, or (ii) only the leftovers - // from previous round are considered leftovers. - // - // CaDiCaL had the first version before. If - // commented out we go to the second version. - for (auto c : schedule) - c->vivify = true; -#elif 1 - // if we have gone through all the leftovers, the current clauses are - // leftovers for the next round - if (!schedule.empty () && !schedule.front ()->vivify && - schedule.back ()->vivify) - for (auto c : schedule) - c->vivify = true; -#else - // do nothing like in kissat and use the candidates for next time. -#endif - // Preference clauses scheduled but not vivified yet next time. - // - if (still_need_to_be_vivified) - PHASE ("vivify", stats.vivifications, - "still need to vivify %" PRId64 " clauses %.02f%% of %" PRId64 - " scheduled", - still_need_to_be_vivified, - percent (still_need_to_be_vivified, scheduled), scheduled); - else { - PHASE ("vivify", stats.vivifications, - "no previously not yet vivified clause left"); - } - - erase_vector (schedule); // Reclaim memory early. - } - - if (!unsat) { - - // Since redundant clause were disconnected during propagating vivified - // units in redundant mode, and further irredundant clauses are - // arbitrarily sorted, we have to propagate all literals again after - // connecting the first two literals in the clauses, in order to - // reestablish the watching invariant. - // - propagated2 = propagated = 0; - - if (!propagate ()) { - LOG ("propagating vivified units leads to conflict"); - learn_empty_clause (); - } - } - - checked = stats.vivifychecks - checked; - subsumed = stats.vivifysubs - subsumed; - strengthened = stats.vivifystrs - strengthened; - units = stats.vivifyunits - units; - - PHASE ("vivify", stats.vivifications, - "checked %" PRId64 " clauses %.02f%% of %" PRId64 - " scheduled using %" PRIu64 " ticks", - checked, percent (checked, scheduled), scheduled, vivifier.ticks); - if (units) - PHASE ("vivify", stats.vivifications, - "found %" PRId64 " units %.02f%% of %" PRId64 " checked", units, - percent (units, checked), checked); - if (subsumed) - PHASE ("vivify", stats.vivifications, - "subsumed %" PRId64 " clauses %.02f%% of %" PRId64 " checked", - subsumed, percent (subsumed, checked), checked); - if (strengthened) - PHASE ("vivify", stats.vivifications, - "strengthened %" PRId64 " clauses %.02f%% of %" PRId64 - " checked", - strengthened, percent (strengthened, checked), checked); - - stats.subsumed += subsumed; - stats.strengthened += strengthened; - stats.ticks.vivify += vivifier.ticks; - - bool unsuccessful = !(subsumed + strengthened + units); - report (vivifier.tag, unsuccessful); -} - -void set_vivifier_mode (Vivifier &vivifier, Vivify_Mode tier) { - vivifier.tier = tier; - switch (tier) { - case Vivify_Mode::TIER1: - vivifier.tag = 'u'; - break; - case Vivify_Mode::TIER2: - vivifier.tag = 'v'; - break; - case Vivify_Mode::TIER3: - vivifier.tag = 'w'; - break; - default: - CADICAL_assert (tier == Vivify_Mode::IRREDUNDANT); - vivifier.tag = 'x'; - break; - } -} -/*------------------------------------------------------------------------*/ - -void Internal::compute_tier_limits (Vivifier &vivifier) { - if (!opts.vivifycalctier) { - vivifier.tier1_limit = 2; - vivifier.tier2_limit = 6; - return; - } - vivifier.tier1_limit = tier1[false]; - vivifier.tier2_limit = tier2[false]; -} - -/*------------------------------------------------------------------------*/ - -bool Internal::vivify () { - - if (unsat) - return false; - if (terminated_asynchronously ()) - return false; - if (!opts.vivify) - return false; - if (!stats.current.irredundant) - return false; - if (level) - backtrack (); - CADICAL_assert (opts.vivify); - CADICAL_assert (!level); - - SET_EFFORT_LIMIT (totallimit, vivify, true); - - private_steps = true; - - START_SIMPLIFIER (vivify, VIVIFY); - stats.vivifications++; - - // the effort is normalized by dividing by sumeffort below, hence no need - // to multiply by 1e-3 (also making the precision better) - double tier1effort = !opts.vivifytier1 ? 0 : (double) opts.vivifytier1eff; - double tier2effort = !opts.vivifytier2 ? 0 : (double) opts.vivifytier2eff; - double tier3effort = !opts.vivifytier3 ? 0 : (double) opts.vivifytier3eff; - double irreffort = - delaying_vivify_irredundant.bumpreasons.delay () || !opts.vivifyirred - ? 0 - : (double) opts.vivifyirredeff; - double sumeffort = tier1effort + tier2effort + tier3effort + irreffort; - if (!stats.current.redundant) - tier1effort = tier2effort = tier3effort = 0; - if (!sumeffort) - sumeffort = irreffort = 1; - int64_t total = totallimit - stats.ticks.vivify; - - PHASE ("vivify", stats.vivifications, - "vivification limit of %" PRId64 " ticks", total); - Vivifier vivifier (Vivify_Mode::TIER1); - compute_tier_limits (vivifier); - - if (vivifier.tier1_limit == vivifier.tier2_limit) { - tier1effort += tier2effort; - tier2effort = 0; - LOG ("vivification tier1 matches tier2 " - "thus using tier2 budget for tier1"); - } - int64_t init_ticks = 0; - - // Refill the schedule every time. Unchecked clauses are 'saved' by - // setting their 'vivify' bit, such that they can be tried next time. - // - // TODO: count against ticks.vivify directly instead of this unholy - // shifting. - vivify_initialize (vivifier, init_ticks); - stats.ticks.vivify += init_ticks; - int64_t limit = stats.ticks.vivify; - const double shared_effort = (double) init_ticks / 4.0; - if (opts.vivifytier1) { - set_vivifier_mode (vivifier, Vivify_Mode::TIER1); - if (limit < stats.ticks.vivify) - limit = stats.ticks.vivify; - const double effort = (total * tier1effort) / sumeffort; - CADICAL_assert (std::numeric_limits::max () - (int64_t) effort >= - limit); - limit += effort; - if (limit - shared_effort > stats.ticks.vivify) { - limit -= shared_effort; - CADICAL_assert (limit >= 0); - vivify_round (vivifier, limit); - } else { - LOG ("building the schedule already used our entire ticks budget for " - "tier1"); - } - } - - if (!unsat && tier2effort) { - erase_vector ( - vivifier.schedule_tier1); // save memory (well, not really as we - // already reached the peak memory) - if (limit < stats.ticks.vivify) - limit = stats.ticks.vivify; - const double effort = (total * tier2effort) / sumeffort; - CADICAL_assert (std::numeric_limits::max () - (int64_t) effort >= - limit); - limit += effort; - if (limit - shared_effort > stats.ticks.vivify) { - limit -= shared_effort; - CADICAL_assert (limit >= 0); - set_vivifier_mode (vivifier, Vivify_Mode::TIER2); - vivify_round (vivifier, limit); - } else { - LOG ("building the schedule already used our entire ticks budget for " - "tier2"); - } - } - - if (!unsat && tier3effort) { - erase_vector (vivifier.schedule_tier2); - if (limit < stats.ticks.vivify) - limit = stats.ticks.vivify; - const double effort = (total * tier3effort) / sumeffort; - CADICAL_assert (std::numeric_limits::max () - (int64_t) effort >= - limit); - limit += effort; - if (limit - shared_effort > stats.ticks.vivify) { - limit -= shared_effort; - CADICAL_assert (limit >= 0); - set_vivifier_mode (vivifier, Vivify_Mode::TIER3); - vivify_round (vivifier, limit); - } else { - LOG ("building the schedule already used our entire ticks budget for " - "tier3"); - } - } - - if (!unsat && irreffort) { - erase_vector (vivifier.schedule_tier3); - if (limit < stats.ticks.vivify) - limit = stats.ticks.vivify; - const double effort = (total * irreffort) / sumeffort; - CADICAL_assert (std::numeric_limits::max () - (int64_t) effort >= - limit); - limit += effort; - if (limit - shared_effort > stats.ticks.vivify) { - limit -= shared_effort; - CADICAL_assert (limit >= 0); - set_vivifier_mode (vivifier, Vivify_Mode::IRREDUNDANT); - const int old = stats.vivifystrirr; - const int old_tried = stats.vivifychecks; - vivify_round (vivifier, limit); - if (stats.vivifychecks - old_tried == 0 || - (float) (stats.vivifystrirr - old) / - (float) (stats.vivifychecks - old_tried) < - 0.01) { - delaying_vivify_irredundant.bumpreasons.bump_delay (); - } else { - delaying_vivify_irredundant.bumpreasons.reduce_delay (); - } - } else { - delaying_vivify_irredundant.bumpreasons.bump_delay (); - LOG ("building the schedule already used our entire ticks budget for " - "irredundant"); - } - } - - reset_noccs (); - STOP_SIMPLIFIER (vivify, VIVIFY); - - private_steps = false; - - return true; -} - -} // namespace CaDiCaL - -ABC_NAMESPACE_IMPL_END diff --git a/src/sat/cadical/cadical_walk.cpp b/src/sat/cadical/cadical_walk.cpp deleted file mode 100644 index 13b1bf9f46..0000000000 --- a/src/sat/cadical/cadical_walk.cpp +++ /dev/null @@ -1,710 +0,0 @@ -#include "global.h" - -#include "internal.hpp" - -ABC_NAMESPACE_IMPL_START - -namespace CaDiCaL { - -/*------------------------------------------------------------------------*/ - -// Random walk local search based on 'ProbSAT' ideas. - -struct Walker { - - Internal *internal; - - Random random; // local random number generator - int64_t propagations; // number of propagations - int64_t limit; // limit on number of propagations - vector broken; // currently unsatisfied clauses - double epsilon; // smallest considered score - vector table; // break value to score table - vector scores; // scores of candidate literals - - double score (unsigned); // compute score from break count - - Walker (Internal *, double size, int64_t limit); -}; - -// These are in essence the CB values from Adrian Balint's thesis. They -// denote the inverse 'cb' of the base 'b' of the (probability) weight -// 'b^-i' for picking a literal with the break value 'i' (first column is -// the 'size', second the 'CB' value). - -static double cbvals[][2] = { - {0.0, 2.00}, {3.0, 2.50}, {4.0, 2.85}, {5.0, 3.70}, - {6.0, 5.10}, {7.0, 7.40}, // Adrian has '5.4', but '7.4' looks better. -}; - -static const int ncbvals = sizeof cbvals / sizeof cbvals[0]; - -// We interpolate the CB values for uniform random SAT formula to the non -// integer situation of average clause size by piecewise linear functions. -// -// y2 - y1 -// ------- * (x - x1) + y1 -// x2 - x1 -// -// where 'x' is the average size of clauses and 'y' the CB value. - -inline static double fitcbval (double size) { - int i = 0; - while (i + 2 < ncbvals && - (cbvals[i][0] > size || cbvals[i + 1][0] < size)) - i++; - const double x2 = cbvals[i + 1][0], x1 = cbvals[i][0]; - const double y2 = cbvals[i + 1][1], y1 = cbvals[i][1]; - const double dx = x2 - x1, dy = y2 - y1; - CADICAL_assert (dx); - const double res = dy * (size - x1) / dx + y1; - CADICAL_assert (res > 0); - return res; -} - -// Initialize the data structures for one local search round. - -Walker::Walker (Internal *i, double size, int64_t l) - : internal (i), random (internal->opts.seed), // global random seed - propagations (0), limit (l) { - random += internal->stats.walk.count; // different seed every time - - // This is the magic constant in ProbSAT (also called 'CB'), which we pick - // according to the average size every second invocation and otherwise - // just the default '2.0', which turns into the base '0.5'. - // - const bool use_size_based_cb = (internal->stats.walk.count & 1); - const double cb = use_size_based_cb ? fitcbval (size) : 2.0; - CADICAL_assert (cb); - const double base = 1 / cb; // scores are 'base^0,base^1,base^2,... - - double next = 1; - for (epsilon = next; next; next = epsilon * base) - table.push_back (epsilon = next); - - PHASE ("walk", internal->stats.walk.count, - "CB %.2f with inverse %.2f as base and table size %zd", cb, base, - table.size ()); -} - -// The scores are tabulated for faster computation (to avoid 'pow'). - -inline double Walker::score (unsigned i) { - const double res = (i < table.size () ? table[i] : epsilon); - LOG ("break %u mapped to score %g", i, res); - return res; -} - -/*------------------------------------------------------------------------*/ - -Clause *Internal::walk_pick_clause (Walker &walker) { - require_mode (WALK); - CADICAL_assert (!walker.broken.empty ()); - int64_t size = walker.broken.size (); - if (size > INT_MAX) - size = INT_MAX; - int pos = walker.random.pick_int (0, size - 1); - Clause *res = walker.broken[pos]; - LOG (res, "picking random position %d", pos); - return res; -} - -/*------------------------------------------------------------------------*/ - -// Compute the number of clauses which would be become unsatisfied if 'lit' -// is flipped and set to false. This is called the 'break-count' of 'lit'. - -unsigned Internal::walk_break_value (int lit) { - - require_mode (WALK); - CADICAL_assert (val (lit) > 0); - - unsigned res = 0; // The computed break-count of 'lit'. - - for (auto &w : watches (lit)) { - CADICAL_assert (w.blit != lit); - if (val (w.blit) > 0) - continue; - if (w.binary ()) { - res++; - continue; - } - - Clause *c = w.clause; - CADICAL_assert (lit == c->literals[0]); - - // Now try to find a second satisfied literal starting at 'literals[1]' - // shifting all the traversed literals to right by one position in order - // to move such a second satisfying literal to 'literals[1]'. This move - // to front strategy improves the chances to find the second satisfying - // literal earlier in subsequent break-count computations. - // - auto begin = c->begin () + 1; - const auto end = c->end (); - auto i = begin; - int prev = 0; - while (i != end) { - const int other = *i; - *i++ = prev; - prev = other; - if (val (other) < 0) - continue; - - // Found 'other' as second satisfying literal. - - w.blit = other; // Update 'blit' - *begin = other; // and move to front. - - break; - } - - if (i != end) - continue; // Double satisfied! - - // Otherwise restore literals (undo shift to the right). - // - while (i != begin) { - const int other = *--i; - *i = prev; - prev = other; - } - - res++; // Literal 'lit' single satisfies clause 'c'. - } - - return res; -} - -/*------------------------------------------------------------------------*/ - -// Given an unsatisfied clause 'c', in which we want to flip a literal, we -// first determine the exponential score based on the break-count of its -// literals and then sample the literals based on these scores. The CB -// value is smaller than one and thus the score is exponentially decreasing -// with the break-count increasing. The sampling works as in 'ProbSAT' and -// 'YalSAT' by summing up the scores and then picking a random limit in the -// range of zero to the sum, then summing up the scores again and picking -// the first literal which reaches the limit. Note, that during incremental -// SAT solving we can not flip assumed variables. Those are assigned at -// decision level one, while the other variables are assigned at two. - -int Internal::walk_pick_lit (Walker &walker, Clause *c) { - LOG ("picking literal by break-count"); - CADICAL_assert (walker.scores.empty ()); - double sum = 0; - int64_t propagations = 0; - for (const auto lit : *c) { - CADICAL_assert (active (lit)); - if (var (lit).level == 1) { - LOG ("skipping assumption %d for scoring", -lit); - continue; - } - CADICAL_assert (active (lit)); - propagations++; - unsigned tmp = walk_break_value (-lit); - double score = walker.score (tmp); - LOG ("literal %d break-count %u score %g", lit, tmp, score); - walker.scores.push_back (score); - sum += score; - } - LOG ("scored %zd literals", walker.scores.size ()); - CADICAL_assert (!walker.scores.empty ()); - walker.propagations += propagations; - stats.propagations.walk += propagations; - CADICAL_assert (walker.scores.size () <= (size_t) c->size); - const double lim = sum * walker.random.generate_double (); - LOG ("score sum %g limit %g", sum, lim); - const auto end = c->end (); - auto i = c->begin (); - auto j = walker.scores.begin (); - int res; - for (;;) { - CADICAL_assert (i != end); - res = *i++; - if (var (res).level > 1) - break; - LOG ("skipping assumption %d without score", -res); - } - sum = *j++; - while (sum <= lim && i != end) { - res = *i++; - if (var (res).level == 1) { - LOG ("skipping assumption %d without score", -res); - continue; - } - sum += *j++; - } - walker.scores.clear (); - LOG ("picking literal %d by break-count", res); - return res; -} - -/*------------------------------------------------------------------------*/ - -void Internal::walk_flip_lit (Walker &walker, int lit) { - - require_mode (WALK); - LOG ("flipping assign %d", lit); - CADICAL_assert (val (lit) < 0); - - // First flip the literal value. - // - const int tmp = sign (lit); - const int idx = abs (lit); - set_val (idx, tmp); - CADICAL_assert (val (lit) > 0); - - // Then remove 'c' and all other now satisfied (made) clauses. - { - // Simply go over all unsatisfied (broken) clauses. - - LOG ("trying to make %zd broken clauses", walker.broken.size ()); - - // We need to measure (and bound) the memory accesses during traversing - // broken clauses in terms of 'propagations'. This is tricky since we - // are not actually propagating literals. Instead we use the clause - // variable 'ratio' as an approximation to the number of clauses used - // during propagating a literal. Note that we use a one-watch scheme. - // Accordingly the number of broken clauses traversed divided by that - // ratio is an approximation of the number of propagation this would - // correspond to (in terms of memory access). To eagerly update these - // statistics we simply increment the propagation counter after every - // 'ratio' traversed clause. These propagations are particularly - // expensive if the number of broken clauses is large which usually - // happens initially. - // - const double ratio = clause_variable_ratio (); - const auto eou = walker.broken.end (); - auto j = walker.broken.begin (), i = j; -#ifdef LOGGING - int64_t made = 0; -#endif - int64_t count = 0; - - while (i != eou) { - - Clause *d = *j++ = *i++; - - int *literals = d->literals, prev = 0; - - // Find 'lit' in 'd'. - // - const int size = d->size; - for (int i = 0; i < size; i++) { - const int other = literals[i]; - CADICAL_assert (active (other)); - literals[i] = prev; - prev = other; - if (other == lit) - break; - CADICAL_assert (val (other) < 0); - } - - // If 'lit' is in 'd' then move it to the front to watch it. - // - if (prev == lit) { - literals[0] = lit; - LOG (d, "made"); - watch_literal (literals[0], literals[1], d); -#ifdef LOGGING - made++; -#endif - j--; - - } else { // Otherwise the clause is not satisfied, undo shift. - - for (int i = size - 1; i >= 0; i--) { - int other = literals[i]; - literals[i] = prev; - prev = other; - } - } - - if (count--) - continue; - - // Update these counters eagerly. Otherwise if we delay the update - // until all clauses are traversed, interrupting the solver has a high - // chance of giving bogus statistics on the number of 'propagations' - // in 'walk', if it is interrupted in this loop. - - count = ratio; // Starting counting down again. - walker.propagations++; - stats.propagations.walk++; - } - LOG ("made %" PRId64 " clauses by flipping %d", made, lit); - walker.broken.resize (j - walker.broken.begin ()); - } - - // Finally add all new unsatisfied (broken) clauses. - { - walker.propagations++; // This really corresponds now to one - stats.propagations.walk++; // propagation (in a one-watch scheme). - -#ifdef LOGGING - int64_t broken = 0; -#endif - Watches &ws = watches (-lit); - - LOG ("trying to break %zd watched clauses", ws.size ()); - - for (const auto &w : ws) { - Clause *d = w.clause; - LOG (d, "unwatch %d in", -lit); - int *literals = d->literals, replacement = 0, prev = -lit; - CADICAL_assert (literals[0] == -lit); - const int size = d->size; - for (int i = 1; i < size; i++) { - const int other = literals[i]; - CADICAL_assert (active (other)); - literals[i] = prev; // shift all to right - prev = other; - const signed char tmp = val (other); - if (tmp < 0) - continue; - replacement = other; // satisfying literal - break; - } - if (replacement) { - literals[1] = -lit; - literals[0] = replacement; - CADICAL_assert (-lit != replacement); - watch_literal (replacement, -lit, d); - } else { - for (int i = size - 1; i > 0; i--) { // undo shift - const int other = literals[i]; - literals[i] = prev; - prev = other; - } - CADICAL_assert (literals[0] == -lit); - LOG (d, "broken"); - walker.broken.push_back (d); -#ifdef LOGGING - broken++; -#endif - } - } - LOG ("broken %" PRId64 " clauses by flipping %d", broken, lit); - ws.clear (); - } -} - -/*------------------------------------------------------------------------*/ - -// Check whether to save the current phases as new global minimum. - -inline void Internal::walk_save_minimum (Walker &walker) { - int64_t broken = walker.broken.size (); - if (broken >= stats.walk.minimum) - return; - VERBOSE (3, "new global minimum %" PRId64 "", broken); - stats.walk.minimum = broken; - for (auto i : vars) { - const signed char tmp = vals[i]; - if (tmp) - phases.min[i] = phases.saved[i] = tmp; - } -} - -/*------------------------------------------------------------------------*/ - -int Internal::walk_round (int64_t limit, bool prev) { - - backtrack (); - if (propagated < trail.size () && !propagate ()) { - LOG ("empty clause after root level propagation"); - learn_empty_clause (); - return 20; - } - - stats.walk.count++; - - clear_watches (); - - // Remove all fixed variables first (assigned at decision level zero). - // - if (last.collect.fixed < stats.all.fixed) - garbage_collection (); - -#ifndef CADICAL_QUIET - // We want to see more messages during initial local search. - // - if (localsearching) { - CADICAL_assert (!force_phase_messages); - force_phase_messages = true; - } -#endif - - PHASE ("walk", stats.walk.count, - "random walk limit of %" PRId64 " propagations", limit); - - // First compute the average clause size for picking the CB constant. - // - double size = 0; - int64_t n = 0; - for (const auto c : clauses) { - if (c->garbage) - continue; - if (c->redundant) { - if (!opts.walkredundant) - continue; - if (!likely_to_be_kept_clause (c)) - continue; - } - size += c->size; - n++; - } - double average_size = relative (size, n); - - PHASE ("walk", stats.walk.count, - "%" PRId64 " clauses average size %.2f over %d variables", n, - average_size, active ()); - - // Instantiate data structures for this local search round. - // - Walker walker (internal, average_size, limit); - - bool failed = false; // Inconsistent assumptions? - - level = 1; // Assumed variables assigned at level 1. - - if (assumptions.empty ()) { - LOG ("no assumptions so assigning all variables to decision phase"); - } else { - LOG ("assigning assumptions to their forced phase first"); - for (const auto lit : assumptions) { - signed char tmp = val (lit); - if (tmp > 0) - continue; - if (tmp < 0) { - LOG ("inconsistent assumption %d", lit); - failed = true; - break; - } - if (!active (lit)) - continue; - tmp = sign (lit); - const int idx = abs (lit); - LOG ("initial assign %d to assumption phase", tmp < 0 ? -idx : idx); - set_val (idx, tmp); - CADICAL_assert (level == 1); - var (idx).level = 1; - } - if (!failed) - LOG ("now assigning remaining variables to their decision phase"); - } - - level = 2; // All other non assumed variables assigned at level 2. - - if (!failed) { - - for (auto idx : vars) { - if (!active (idx)) { - LOG ("skipping inactive variable %d", idx); - continue; - } - if (vals[idx]) { - CADICAL_assert (var (idx).level == 1); - LOG ("skipping assumed variable %d", idx); - continue; - } - int tmp = 0; - if (prev) - tmp = phases.prev[idx]; - if (!tmp) - tmp = sign (decide_phase (idx, true)); - CADICAL_assert (tmp == 1 || tmp == -1); - set_val (idx, tmp); - CADICAL_assert (level == 2); - var (idx).level = 2; - LOG ("initial assign %d to decision phase", tmp < 0 ? -idx : idx); - } - - LOG ("watching satisfied and registering broken clauses"); -#ifdef LOGGING - int64_t watched = 0; -#endif - for (const auto c : clauses) { - - if (c->garbage) - continue; - if (c->redundant) { - if (!opts.walkredundant) - continue; - if (!likely_to_be_kept_clause (c)) - continue; - } - - bool satisfiable = false; // contains not only assumptions - int satisfied = 0; // clause satisfied? - - int *lits = c->literals; - const int size = c->size; - - // Move to front satisfied literals and determine whether there - // is at least one (non-assumed) literal that can be flipped. - // - for (int i = 0; satisfied < 2 && i < size; i++) { - const int lit = lits[i]; - CADICAL_assert (active (lit)); // Due to garbage collection. - if (val (lit) > 0) { - swap (lits[satisfied], lits[i]); - if (!satisfied++) - LOG ("first satisfying literal %d", lit); - } else if (!satisfiable && var (lit).level > 1) { - LOG ("non-assumption potentially satisfying literal %d", lit); - satisfiable = true; - } - } - - if (!satisfied && !satisfiable) { - LOG (c, "due to assumptions unsatisfiable"); - LOG ("stopping local search since assumptions falsify a clause"); - failed = true; - break; - } - - if (satisfied) { - watch_literal (lits[0], lits[1], c); -#ifdef LOGGING - watched++; -#endif - } else { - CADICAL_assert (satisfiable); // at least one non-assumed variable ... - LOG (c, "broken"); - walker.broken.push_back (c); - } - } -#ifdef LOGGING - if (!failed) { - int64_t broken = walker.broken.size (); - int64_t total = watched + broken; - LOG ("watching %" PRId64 " clauses %.0f%% " - "out of %" PRId64 " (watched and broken)", - watched, percent (watched, total), total); - } -#endif - } - - int64_t old_global_minimum = stats.walk.minimum; - - int res; // Tells caller to continue with local search. - - if (!failed) { - - int64_t broken = walker.broken.size (); - - PHASE ("walk", stats.walk.count, - "starting with %" PRId64 " unsatisfied clauses " - "(%.0f%% out of %" PRId64 ")", - broken, percent (broken, stats.current.irredundant), - stats.current.irredundant); - - walk_save_minimum (walker); - - int64_t minimum = broken; -#ifndef CADICAL_QUIET - int64_t flips = 0; -#endif - while (!terminated_asynchronously () && !walker.broken.empty () && - walker.propagations < walker.limit) { -#ifndef CADICAL_QUIET - flips++; -#endif - stats.walk.flips++; - stats.walk.broken += broken; - Clause *c = walk_pick_clause (walker); - const int lit = walk_pick_lit (walker, c); - walk_flip_lit (walker, lit); - broken = walker.broken.size (); - LOG ("now have %" PRId64 " broken clauses in total", broken); - if (broken >= minimum) - continue; - minimum = broken; - VERBOSE (3, "new phase minimum %" PRId64 " after %" PRId64 " flips", - minimum, flips); - walk_save_minimum (walker); - } - - if (minimum < old_global_minimum) - PHASE ("walk", stats.walk.count, - "%snew global minimum %" PRId64 "%s in %" PRId64 " flips and " - "%" PRId64 " propagations", - tout.bright_yellow_code (), minimum, tout.normal_code (), - flips, walker.propagations); - else - PHASE ("walk", stats.walk.count, - "best phase minimum %" PRId64 " in %" PRId64 " flips and " - "%" PRId64 " propagations", - minimum, flips, walker.propagations); - - if (opts.profile >= 2) { - PHASE ("walk", stats.walk.count, - "%.2f million propagations per second", - relative (1e-6 * walker.propagations, - time () - profiles.walk.started)); - - PHASE ("walk", stats.walk.count, "%.2f thousand flips per second", - relative (1e-3 * flips, time () - profiles.walk.started)); - - } else { - PHASE ("walk", stats.walk.count, "%.2f million propagations", - 1e-6 * walker.propagations); - - PHASE ("walk", stats.walk.count, "%.2f thousand flips", 1e-3 * flips); - } - - if (minimum > 0) { - LOG ("minimum %" PRId64 " non-zero thus potentially continue", - minimum); - res = 0; - } else { - LOG ("minimum is zero thus stop local search"); - res = 10; - } - - } else { - - res = 20; - - PHASE ("walk", stats.walk.count, - "aborted due to inconsistent assumptions"); - } - - copy_phases (phases.prev); - - for (auto idx : vars) - if (active (idx)) - set_val (idx, 0); - - CADICAL_assert (level == 2); - level = 0; - - clear_watches (); - connect_watches (); - -#ifndef CADICAL_QUIET - if (localsearching) { - CADICAL_assert (force_phase_messages); - force_phase_messages = false; - } -#endif - - return res; -} - -void Internal::walk () { - START_INNER_WALK (); - int64_t limit = stats.propagations.search; - limit *= 1e-3 * opts.walkeffort; - if (limit < opts.walkmineff) - limit = opts.walkmineff; - if (limit > opts.walkmaxeff) - limit = opts.walkmaxeff; - (void) walk_round (limit, false); - STOP_INNER_WALK (); -} - -} // namespace CaDiCaL - -ABC_NAMESPACE_IMPL_END diff --git a/src/sat/cadical/cadical_watch.cpp b/src/sat/cadical/cadical_watch.cpp deleted file mode 100644 index 84c51ef2c5..0000000000 --- a/src/sat/cadical/cadical_watch.cpp +++ /dev/null @@ -1,132 +0,0 @@ -#include "global.h" - -#include "internal.hpp" - -ABC_NAMESPACE_IMPL_START - -namespace CaDiCaL { - -void Internal::init_watches () { - CADICAL_assert (wtab.empty ()); - if (wtab.size () < 2 * vsize) - wtab.resize (2 * vsize, Watches ()); - LOG ("initialized watcher tables"); -} - -void Internal::clear_watches () { - for (auto lit : lits) - watches (lit).clear (); -} - -void Internal::reset_watches () { - CADICAL_assert (!wtab.empty ()); - erase_vector (wtab); - LOG ("reset watcher tables"); -} - -// This can be quite costly since lots of memory is accessed in a rather -// random fashion, and thus we optionally profile it. - -void Internal::connect_watches (bool irredundant_only) { - START (connect); - CADICAL_assert (watching ()); - - LOG ("watching all %sclauses", irredundant_only ? "irredundant " : ""); - - // First connect binary clauses. - // - for (const auto &c : clauses) { - if (irredundant_only && c->redundant) - continue; - if (c->garbage || c->size > 2) - continue; - watch_clause (c); - } - - // Then connect non-binary clauses. - // - for (const auto &c : clauses) { - if (irredundant_only && c->redundant) - continue; - if (c->garbage || c->size == 2) - continue; - watch_clause (c); - if (!level) { - const int lit0 = c->literals[0]; - const int lit1 = c->literals[1]; - const signed char tmp0 = val (lit0); - const signed char tmp1 = val (lit1); - if (tmp0 > 0) - continue; - if (tmp1 > 0) - continue; - if (tmp0 < 0) { - const size_t pos0 = var (lit0).trail; - if (pos0 < propagated) { - propagated = pos0; - LOG ("literal %d resets propagated to %zd", lit0, pos0); - } - } - if (tmp1 < 0) { - const size_t pos1 = var (lit1).trail; - if (pos1 < propagated) { - propagated = pos1; - LOG ("literal %d resets propagated to %zd", lit1, pos1); - } - } - } - } - - STOP (connect); -} - -// This can be quite costly since lots of memory is accessed in a rather -// random fashion, and thus we optionally profile it. - -void Internal::connect_binary_watches () { - START (connect); - CADICAL_assert (watching ()); - - LOG ("watching binary clauses"); - - // First connect binary clauses. - // - for (const auto &c : clauses) { - if (c->garbage || c->size > 2) - continue; - watch_clause (c); - } - - STOP (connect); -} - -void Internal::sort_watches () { - CADICAL_assert (watching ()); - LOG ("sorting watches"); - Watches saved; - for (auto lit : lits) { - Watches &ws = watches (lit); - - const const_watch_iterator end = ws.end (); - watch_iterator j = ws.begin (); - const_watch_iterator i; - - CADICAL_assert (saved.empty ()); - - for (i = j; i != end; i++) { - const Watch w = *i; - if (w.binary ()) - *j++ = w; - else - saved.push_back (w); - } - - std::copy (saved.cbegin (), saved.cend (), j); - - saved.clear (); - } -} - -} // namespace CaDiCaL - -ABC_NAMESPACE_IMPL_END diff --git a/src/sat/cadical/ccadical.h b/src/sat/cadical/ccadical.h deleted file mode 100644 index ec5292a79d..0000000000 --- a/src/sat/cadical/ccadical.h +++ /dev/null @@ -1,74 +0,0 @@ -#ifndef _ccadical_h_INCLUDED -#define _ccadical_h_INCLUDED - -#include "global.h" - -/*------------------------------------------------------------------------*/ -ABC_NAMESPACE_HEADER_START -/*------------------------------------------------------------------------*/ - -#include -#include - -// C wrapper for CaDiCaL's C++ API following IPASIR. - -typedef struct CCaDiCaL CCaDiCaL; - -const char *ccadical_signature (void); -CCaDiCaL *ccadical_init (void); -void ccadical_release (CCaDiCaL *); - -void ccadical_add (CCaDiCaL *, int lit); -void ccadical_assume (CCaDiCaL *, int lit); -int ccadical_solve (CCaDiCaL *); -int ccadical_val (CCaDiCaL *, int lit); -int ccadical_failed (CCaDiCaL *, int lit); - -void ccadical_set_terminate (CCaDiCaL *, void *state, - int (*terminate) (void *state)); - -void ccadical_set_learn (CCaDiCaL *, void *state, int max_length, - void (*learn) (void *state, int *clause)); - -/*------------------------------------------------------------------------*/ - -// Non-IPASIR conformant 'C' functions. - -void ccadical_constrain (CCaDiCaL *, int lit); -int ccadical_constraint_failed (CCaDiCaL *); -void ccadical_set_option (CCaDiCaL *, const char *name, int val); -void ccadical_limit (CCaDiCaL *, const char *name, int limit); -int ccadical_get_option (CCaDiCaL *, const char *name); -void ccadical_print_statistics (CCaDiCaL *); -int64_t ccadical_active (CCaDiCaL *); -int64_t ccadical_irredundant (CCaDiCaL *); -int ccadical_fixed (CCaDiCaL *, int lit); -int ccadical_trace_proof (CCaDiCaL *, FILE *, const char *); -void ccadical_close_proof (CCaDiCaL *); -void ccadical_conclude (CCaDiCaL *); -void ccadical_terminate (CCaDiCaL *); -void ccadical_freeze (CCaDiCaL *, int lit); -int ccadical_frozen (CCaDiCaL *, int lit); -void ccadical_melt (CCaDiCaL *, int lit); -int ccadical_simplify (CCaDiCaL *); -int ccadical_vars (CCaDiCaL *); -int ccadical_reserve_difference (CCaDiCaL *, int number_of_vars); - -// Extra - -void ccadical_reserve(CCaDiCaL *, int min_max_var); -int ccadical_is_inconsistent(CCaDiCaL *); - -/*------------------------------------------------------------------------*/ - -// Support legacy names used before moving to more IPASIR conforming names. - -#define ccadical_reset ccadical_release -#define ccadical_sat ccadical_solve -#define ccadical_deref ccadical_val - -/*------------------------------------------------------------------------*/ -ABC_NAMESPACE_HEADER_END -/*------------------------------------------------------------------------*/ - -#endif diff --git a/src/sat/cadical/checker.hpp b/src/sat/cadical/checker.hpp deleted file mode 100644 index 8c351e3269..0000000000 --- a/src/sat/cadical/checker.hpp +++ /dev/null @@ -1,178 +0,0 @@ -#ifndef _checker_hpp_INCLUDED -#define _checker_hpp_INCLUDED - -#include "global.h" - -#include "tracer.hpp" // Alphabetically after 'checker'. - -#include - -ABC_NAMESPACE_CXX_HEADER_START - -namespace CaDiCaL { - -/*------------------------------------------------------------------------*/ - -// This checker implements an online forward DRUP proof checker enabled by -// 'opts.checkproof' (requires 'opts.check' also to be enabled). This is -// useful for model basted testing (and delta-debugging), where we can not -// rely on an external proof checker such as 'drat-trim'. We also do not -// have yet a flow for offline incremental proof checking, while this -// checker here can also be used in an incremental setting. -// -// In essence the checker implements is a simple propagation online SAT -// solver with an additional hash table to find clauses fast for -// 'delete_clause'. It requires its own data structure for clauses -// ('CheckerClause') and watches ('CheckerWatch'). -// -// In our experiments the checker slows down overall SAT solving time by a -// factor of 3, which we contribute to its slightly less efficient -// implementation. - -/*------------------------------------------------------------------------*/ - -struct CheckerClause { - CheckerClause *next; // collision chain link for hash table - uint64_t hash; // previously computed full 64-bit hash - unsigned size; // zero if this is a garbage clause - int literals[2]; // otherwise 'literals' of length 'size' -}; - -struct CheckerWatch { - int blit; - unsigned size; - CheckerClause *clause; - CheckerWatch () {} - CheckerWatch (int b, CheckerClause *c) - : blit (b), size (c->size), clause (c) {} -}; - -typedef vector CheckerWatcher; - -/*------------------------------------------------------------------------*/ - -class Checker : public StatTracer { - - Internal *internal; - - // Capacity of variable values. - // - int64_t size_vars; - - // For the assignment we want to have an as fast access as possible and - // thus we use an array which can also be indexed by negative literals and - // is actually valid in the range [-size_vars+1, ..., size_vars-1]. - // - signed char *vals; - - // The 'watchers' and 'marks' data structures are not that time critical - // and thus we access them by first mapping a literal to 'unsigned'. - // - static unsigned l2u (int lit); - vector watchers; // watchers of literals - vector marks; // mark bits of literals - - signed char &mark (int lit); - CheckerWatcher &watcher (int lit); - - bool inconsistent; // found or added empty clause - - uint64_t num_clauses; // number of clauses in hash table - uint64_t num_garbage; // number of garbage clauses - uint64_t size_clauses; // size of clause hash table - CheckerClause **clauses; // hash table of clauses - CheckerClause *garbage; // linked list of garbage clauses - - vector unsimplified; // original clause for reporting - vector simplified; // clause for sorting - - vector trail; // for propagation - - unsigned next_to_propagate; // next to propagate on trail - - void enlarge_vars (int64_t idx); - void import_literal (int lit); - void import_clause (const vector &); - bool tautological (); - - static const unsigned num_nonces = 4; - - uint64_t nonces[num_nonces]; // random numbers for hashing - uint64_t last_hash; // last computed hash value of clause - int64_t last_id; - uint64_t compute_hash (); // compute and save hash value of clause - - // Reduce hash value to the actual size. - // - static uint64_t reduce_hash (uint64_t hash, uint64_t size); - - void enlarge_clauses (); // enlarge hash table for clauses - void insert (); // insert clause in hash table - CheckerClause **find (); // find clause position in hash table - - void add_clause (const char *type); - - void collect_garbage_clauses (); - - CheckerClause *new_clause (); - void delete_clause (CheckerClause *); - - signed char val (int lit); // returns '-1', '0' or '1' - - bool clause_satisfied (CheckerClause *); - - void assign (int lit); // assign a literal to true - void assume (int lit); // assume a literal - bool propagate (); // propagate and check for conflicts - void backtrack (unsigned); // prepare for next clause - bool check (); // check simplified clause is implied - bool check_blocked (); // check if clause is blocked - - struct { - - int64_t added; // number of added clauses - int64_t original; // number of added original clauses - int64_t derived; // number of added derived clauses - - int64_t deleted; // number of deleted clauses - - int64_t assumptions; // number of assumed literals - int64_t propagations; // number of propagated literals - - int64_t insertions; // number of clauses added to hash table - int64_t collisions; // number of hash collisions in 'find' - int64_t searches; // number of searched clauses in 'find' - - int64_t checks; // number of implication checks - - int64_t collections; // garbage collections - int64_t units; - - } stats; - -public: - Checker (Internal *); - virtual ~Checker (); - - void connect_internal (Internal *i) override; - - void add_original_clause (int64_t, bool, const vector &, - bool = false) override; - void add_derived_clause (int64_t, bool, const vector &, - const vector &) override; - void delete_clause (int64_t, bool, const vector &) override; - - void finalize_clause (int64_t, const vector &) override {} // skip - void report_status (int, int64_t) override {} // skip - void begin_proof (int64_t) override {} // skip - void add_assumption_clause (int64_t, const vector &, - const vector &) override; - void print_stats () override; - void dump (); // for debugging purposes only -}; - -} // namespace CaDiCaL - -ABC_NAMESPACE_CXX_HEADER_END - -#endif diff --git a/src/sat/cadical/clause.hpp b/src/sat/cadical/clause.hpp deleted file mode 100644 index e5d0290f63..0000000000 --- a/src/sat/cadical/clause.hpp +++ /dev/null @@ -1,194 +0,0 @@ -#ifndef _clause_hpp_INCLUDED -#define _clause_hpp_INCLUDED - -#include "global.h" - -#include "util.hpp" -#include -#include -#include - -ABC_NAMESPACE_CXX_HEADER_START - -namespace CaDiCaL { - -/*------------------------------------------------------------------------*/ - -typedef int *literal_iterator; -typedef const int *const_literal_iterator; - -/*------------------------------------------------------------------------*/ - -// The 'Clause' data structure is very important. There are usually many -// clauses and accessing them is a hot-spot. Thus we use common -// optimizations to reduce memory and improve cache usage, even though this -// induces some complexity in understanding the code. -// -// The most important optimization is to 'embed' the actual literals in the -// clause. This requires a variadic size structure and thus strictly is not -// 'C' conform, but supported by all compilers we used. The alternative is -// to store the actual literals somewhere else, which not only needs more -// memory but more importantly also requires another memory access and thus -// is very costly. - -struct Clause { - union { - int64_t id; // Used to create LRAT-style proofs - Clause *copy; // Only valid if 'moved', then that's where to. - // - // The 'copy' field is only valid for 'moved' clauses in the moving - // garbage collector 'copy_non_garbage_clauses' for keeping clauses - // compactly in a contiguous memory arena. Otherwise, so almost all of - // the time, 'id' is valid. See 'collect.cpp' for details. - }; - bool conditioned : 1; // Tried for globally blocked clause elimination. - bool covered : 1; // Already considered for covered clause elimination. - bool enqueued : 1; // Enqueued on backward queue. - bool frozen : 1; // Temporarily frozen (in covered clause elimination). - bool garbage : 1; // can be garbage collected unless it is a 'reason' - bool gate : 1; // Clause part of a gate (function definition). - bool hyper : 1; // redundant hyper binary or ternary resolved - bool instantiated : 1; // tried to instantiate - bool moved : 1; // moved during garbage collector ('copy' valid) - bool reason : 1; // reason / antecedent clause can not be collected - bool redundant : 1; // aka 'learned' so not 'irredundant' (original) - bool transred : 1; // already checked for transitive reduction - bool subsume : 1; // not checked in last subsumption round - bool swept : 1; // clause used to sweep equivalences - bool flushed : 1; // garbage in proof deleted binaries - unsigned used : 8; // resolved in conflict analysis since last 'reduce' - bool vivified : 1; // clause already vivified - bool vivify : 1; // clause scheduled to be vivified - - // The glucose level ('LBD' or short 'glue') is a heuristic value for the - // expected usefulness of a learned clause, where smaller glue is consider - // more useful. During learning the 'glue' is determined as the number of - // decisions in the learned clause. Thus the glue of a clause is a strict - // upper limit on the smallest number of decisions needed to make it - // propagate. For instance a binary clause will propagate if one of its - // literals is set to false. Similarly a learned clause with glue 1 can - // propagate after one decision, one with glue 2 after 2 decisions etc. - // In some sense the glue is an abstraction of the size of the clause. - // - // See the IJCAI'09 paper by Audemard & Simon for more details. We - // switched back and forth between keeping the glue stored in a clause and - // using it only initially to determine whether it is kept, that is - // survives clause reduction. The latter strategy is not bad but also - // does not allow to use glue values for instance in 'reduce'. - // - // More recently we also update the glue and promote clauses to lower - // level tiers during conflict analysis. The idea of using three tiers is - // also due to Chanseok Oh and thus used in all recent 'Maple...' solvers. - // Tier one are the always kept clauses with low glue at most - // 'opts.reducetier1glue' (default '2'). The second tier contains all - // clauses with glue larger than 'opts.reducetier1glue' but smaller or - // equal than 'opts.reducetier2glue' (default '6'). The third tier - // consists of clauses with glue larger than 'opts.reducetier2glue'. - // - // Clauses in tier one are not deleted in 'reduce'. Clauses in tier - // two require to be unused in two consecutive 'reduce' intervals before - // being collected while for clauses in tier three not being used since - // the last 'reduce' call makes them deletion candidates. Clauses derived - // by hyper binary or ternary resolution (even though small and thus with - // low glue) are always removed if they remain unused during one interval. - // See 'mark_useless_redundant_clauses_as_garbage' in 'reduce.cpp' and - // 'bump_clause' in 'analyze.cpp'. - // - int glue; - - int size; // Actual size of 'literals' (at least 2). - int pos; // Position of last watch replacement [Gent'13]. - - // This 'flexible array member' is of variadic 'size' (and actually - // shrunken if strengthened) and keeps the literals close to the header of - // the clause to avoid another pointer dereference, which would be costly. - - // In earlier versions we used 'literals[2]' to fake it (in order to - // support older Microsoft compilers even though this feature is in C99) - // and at the same time being able to overlay the first two literals with - // the 'copy' field above, as having a flexible array member inside a - // union is not allowed. Now compilers start to figure out that those - // literals can be accessed with indices larger than 1 and produce - // warnings. After having the 'id' field mandatory we now overlay that - // one with the copy field. - - // However, it turns out that even though flexible array members are in - // C99 they are not in C11++, and therefore pedantic compilation with - // '--pedantic' fails completely. Therefore we still support as - // alternative faked flexible array members, which unfortunately need - // then again more care when accessing the literals outside the faked - // virtual sizes and the compiler can somehow figure that out, because - // that would in turn produce a warning. - -#ifndef NFLEXIBLE - int literals[]; -#else - int literals[2]; -#endif - - // Supports simple range based for loops over clauses. - - literal_iterator begin () { return literals; } - literal_iterator end () { return literals + size; } - - const_literal_iterator begin () const { return literals; } - const_literal_iterator end () const { return literals + size; } - - static size_t bytes (int size) { - - // Memory sanitizer insists that clauses put into consecutive memory in - // the arena are still 8 byte aligned. We could also allocate 8 byte - // aligned memory there. However, assuming the real memory foot print - // of a clause is 8 bytes anyhow, we just allocate 8 byte aligned memory - // all the time (even if allocated outside of the arena). - // - CADICAL_assert (size > 1); - const size_t header_bytes = sizeof (Clause); - const size_t actual_literal_bytes = size * sizeof (int); - size_t combined_bytes = header_bytes + actual_literal_bytes; -#ifdef NFLEXIBLE - const size_t faked_literals_bytes = sizeof ((Clause *) 0)->literals; - combined_bytes -= faked_literals_bytes; -#endif - size_t aligned_bytes = align (combined_bytes, 8); - return aligned_bytes; - } - - size_t bytes () const { return bytes (size); } - - // Check whether this clause is ready to be collected and deleted. The - // 'reason' flag is only there to protect reason clauses in 'reduce', - // which does not backtrack to the root level. If garbage collection is - // triggered from a preprocessor, which backtracks to the root level, then - // 'reason' is false for sure. We want to use the same garbage collection - // code though for both situations and thus hide here this variance. - // - bool collect () const { return !reason && garbage; } -}; - -struct clause_smaller_size { - bool operator() (const Clause *a, const Clause *b) { - return a->size < b->size; - } -}; - -/*------------------------------------------------------------------------*/ - -// Place literals over the same variable close to each other. This would -// allow eager removal of identical literals and detection of tautological -// clauses but is only currently used for better logging (see also -// 'opts.logsort' in 'logging.cpp'). - -struct clause_lit_less_than { - bool operator() (int a, int b) const { - using namespace std; - int s = abs (a), t = abs (b); - return s < t || (s == t && a < b); - } -}; - -} // namespace CaDiCaL - -ABC_NAMESPACE_CXX_HEADER_END - -#endif diff --git a/src/sat/cadical/config.hpp b/src/sat/cadical/config.hpp deleted file mode 100644 index d49b05c840..0000000000 --- a/src/sat/cadical/config.hpp +++ /dev/null @@ -1,26 +0,0 @@ -#ifndef _config_hpp_INCLUDED -#define _config_hpp_INCLUDED - -#include "global.h" - -ABC_NAMESPACE_CXX_HEADER_START - -namespace CaDiCaL { - -class Options; - -struct Config { - - static bool has (const char *); - static bool set (Options &, const char *); - static void usage (); - - static const char **begin (); - static const char **end (); -}; - -} // namespace CaDiCaL - -ABC_NAMESPACE_CXX_HEADER_END - -#endif diff --git a/src/sat/cadical/congruence.hpp b/src/sat/cadical/congruence.hpp deleted file mode 100644 index e0acf9b00a..0000000000 --- a/src/sat/cadical/congruence.hpp +++ /dev/null @@ -1,720 +0,0 @@ -#ifndef _congruenc_hpp_INCLUDED -#define _congruenc_hpp_INCLUDED - -#include "global.h" - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "clause.hpp" -#include "inttypes.hpp" -#include "util.hpp" -#include "watch.hpp" - -ABC_NAMESPACE_CXX_HEADER_START - -namespace CaDiCaL { - -typedef int64_t LRAT_ID; - -// This implements the algorithm algorithm from SAT 2024. -// -// The idea is to: -// 0. handle binary clauses -// 1. detect gates and merge gates with same inputs ('lazy') -// 2. eagerly replace the equivalent literals and merge gates with same -// inputs -// 3. forward subsume -// -// In step 0 the normalization is fully lazy but we do not care about a -// normal form. Therefore we actually eagerly merge literals. -// -// In step 2 there is a subtility: we only replace with the equivalence -// chain as far as we propagated so far. This is the eager part. For LRAT we -// produce the equivalence up to the point we have propagated, no the full -// chain. This is important for merging literals. To merge literals we use -// union-find but we only compress paths when rewriting the literal, not -// before. The compression was not considered important in Kissat, but we do -// it aggressively as a mirror of the equivalences we have generated. -// -// We have two structures for merging: -// - the lazy ones contains alls merges, with functions like -// find_representative -// -// - the eager version that gets the merges one by one, with functions -// like find_eager_representatives -// -// The two structures are nicely separated and we only working on one of -// them except for: -// -// 1. When propagating one equivalence, we first important the -// equivalence from the lazy to the eager version, producing the full -// chain. -// -// 2. When merging the literals, we merge the literals given by the lazy -// structure, then we merge their representative in the eager version, -// updating only the lazy structure. We do not update the eager version. -// -// An important point: We cannot use internal->lrat_chain and -// internal->clause because in most places we can interrupt the -// transformation to learn a new clause representing an equivalence. -// However, we can only have 2 layers so we use this->lrat_chain and -// internal->lrat_chain when we really produce the proof. -struct Internal; - -#define LD_MAX_ARITY 26 -#define MAX_ARITY ((1 << LD_MAX_ARITY) - 1) - -enum class Gate_Type { And_Gate, XOr_Gate, ITE_Gate }; - -// Wrapper when we are looking for implication in if-then-else gates -struct lit_implication { - int first; - int second; - Clause *clause; - lit_implication (int f, int s, Clause *_id) - : first (f), second (s), clause (_id) {} - lit_implication (int f, int s) : first (f), second (s), clause (0) {} - lit_implication () : first (0), second (0), clause (nullptr) {} - void swap () { std::swap (first, second); } -}; - -// Wrapper when we are looking for equivalence for if-then-else-gate. They -// are produced by merging implication -struct lit_equivalence { - int first; - int second; - Clause *first_clause; - Clause *second_clause; - void check_invariant () { - CADICAL_assert (second_clause); - CADICAL_assert (first_clause); - CADICAL_assert (std::find (begin (*first_clause), end (*first_clause), first) != - end (*first_clause)); - CADICAL_assert (std::find (begin (*second_clause), end (*second_clause), - second) != end (*second_clause)); - CADICAL_assert (std::find (begin (*first_clause), end (*first_clause), - -second) != end (*first_clause)); - CADICAL_assert (std::find (begin (*second_clause), end (*second_clause), - -first) != end (*second_clause)); - } - lit_equivalence (int f, Clause *f_id, int s, Clause *s_id) - : first (f), second (s), first_clause (f_id), second_clause (s_id) {} - lit_equivalence (int f, int s) - : first (f), second (s), first_clause (nullptr), - second_clause (nullptr) {} - lit_equivalence () - : first (0), second (0), first_clause (nullptr), - second_clause (nullptr) {} - lit_equivalence swap () { - std::swap (first, second); - std::swap (first_clause, second_clause); - return *this; - } - lit_equivalence negate_both () { - first = -first; - second = -second; - std::swap (first_clause, second_clause); - return *this; - } -}; - -typedef std::vector lit_implications; -typedef std::vector lit_equivalences; - -std::string string_of_gate (Gate_Type t); - -struct LitClausePair { - int current_lit; // current literal from the gate - Clause *clause; - LitClausePair (int lit, Clause *cl) : current_lit (lit), clause (cl) {} - LitClausePair () : current_lit (0), clause (nullptr) {} -}; -struct LitIdPair { - int lit; // current literal from the gate - LRAT_ID id; - LitIdPair (int l, LRAT_ID i) : lit (l), id (i) {} - LitIdPair () : lit (0), id (0) {} -}; - -/*------------------------------------------------------------------------*/ - -// Sorting the scheduled clauses is way faster if we compute and save the -// clause size in the schedule to avoid pointer access to clauses during -// sorting. This slightly increases the schedule size though. - -struct ClauseSize { - size_t size; - Clause *clause; - ClauseSize (int s, Clause *c) : size (s), clause (c) {} - ClauseSize (Clause *c): size (c->size), clause (c) {} - ClauseSize () {} -}; - -struct smaller_clause_size_rank { - typedef size_t Type; - Type operator() (const ClauseSize &a) { return a.size; } -}; - -/*------------------------------------------------------------------------*/ -// There are many special cases for ITE gates and we have to keep track of -// them as it is a gate property (rewriting might not make it obvious -// anymore). -// a = (a ? t : e) results in no -t and no +e gate (a --> a = t == (-a v -a v t) & (-a v a v -t)) -// a = (-a ? t : e) results in no +t and no -e gate -// a = (c ? a : e) results in no t gate (none of them) -// a = (c ? t : a) results in no e gate (none of them) - -enum Special_ITE_GATE { - NORMAL = 0, - NO_PLUS_THEN = (1 << 0), - NO_NEG_THEN = (1 << 1), - NO_THEN = NO_PLUS_THEN + NO_NEG_THEN, - NO_PLUS_ELSE = (1 << 2), - NO_NEG_ELSE = (1 << 3), - NO_ELSE = NO_PLUS_ELSE + NO_NEG_ELSE, - COND_LHS = NO_NEG_THEN + NO_PLUS_ELSE, - UCOND_LHS = NO_PLUS_THEN + NO_NEG_ELSE, -}; - -inline bool ite_flags_no_then_clauses (int8_t flag) { - return (flag & NO_THEN) == NO_THEN; -} - -inline bool ite_flags_no_else_clauses (int8_t flag) { - return (flag & NO_ELSE) == NO_ELSE; -} - -inline bool ite_flags_neg_cond_lhs (int8_t flag) { - return (flag & UCOND_LHS) == UCOND_LHS; -} - -inline bool ite_flags_cond_lhs (int8_t flag) { - return (flag & COND_LHS) == COND_LHS; -} - -/*------------------------------------------------------------------------*/ - -// The core structure of this algorithm: the gate. It is composed of a -// left-hand side and an array of right-hand side. -// -// There are a few tags to help remembering the status of the gate (like -// deleted) -// -// To keep track of the proof we use two extra arrays: -// - `neg_lhs_ids' contains the long clause for AND gates. Otherwise, it is -// empty. TODO: change to std::option as it contains at most one element -// - `pos_lhs_ids' contains all the remaining gates. -// -// We keep the reasons with an index. This index depends on the gates: - -// - AND-Gates and ITE-Gates: the index is the literal from the RHS -// -// - XOR-Gates: if you order the clauses by the order of the literals, -// each literal is either positive (bit '1') or negative (bit '0'). This -// gives a number that we can use. -// -// TODO Florian: I do not think that you have to changed anything, look at -// the 'Look at this first' in the CPP file. -// -// Important for the proofs: the LHS is not updated. -// -// TODO: we currently use a vector for the rhs, but we could also use FMA -// and inline the structure to avoid any indirection. -// -// One warning for degenerated gate: it is a monotone property on the -// defining clauses, but not on the LHS/RHS as the LHS is not rewritten: -// take 4 = AND 3 4 (degenerated with only the clause -4 3) with a rewriting -// 4 -> 1 (unchanged clause) and later 1 -> 3 (unchanged clause) but you do -// not know anymore from the gate that it is degenerated -struct Gate { -#ifdef LOGGING - uint64_t id; -#endif - int lhs; - Gate_Type tag; - bool garbage : 1; - bool indexed : 1; - bool marked : 1; - bool shrunken : 1; - size_t hash; // TODO remove this field (the C++ implementation is caching - // it anyway) - vector pos_lhs_ids; - vector neg_lhs_ids; - bool degenerated_and_neg = false; // LRAT only relevant for AND Gates, neg lhs in RHS - bool degenerated_and_pos = false; // LRAT only relevant for AND Gates, pos lhs in RHS - int8_t degenerated_ite = Special_ITE_GATE::NORMAL; - vector rhs; - - size_t arity () const { return rhs.size (); } - - bool operator== (Gate const &lhs) { - return tag == lhs.tag && hash == lhs.hash && rhs == lhs.rhs; - } -}; - -typedef vector GOccs; - -struct GateEqualTo { - bool operator() (const Gate *const lhs, const Gate *const rhs) const { - return lhs->rhs == rhs->rhs && lhs->tag == rhs->tag; - } -}; - -struct CompactBinary { - Clause *clause; - LRAT_ID id; - int lit1, lit2; - CompactBinary (Clause *c, LRAT_ID i, int l1, int l2) - : clause (c), id (i), lit1 (l1), lit2 (l2) {} - CompactBinary () : clause (nullptr), id (0), lit1 (0), lit2 (0) {} -}; - -struct Hash { - Hash (std::array &ncs) : nonces (ncs) {} - std::array &nonces; - size_t operator() (const Gate *const g) const; -}; - -struct Rewrite { - int src, dst; - LRAT_ID id1; - LRAT_ID id2; - - Rewrite (int _src, int _dst, LRAT_ID _id1, LRAT_ID _id2) - : src (_src), dst (_dst), id1 (_id1), id2 (_id2) {} - Rewrite () : src (0), dst (0), id1 (0), id2 (0) {} -}; - -struct Closure { - - Closure (Internal *i); - - Internal *const internal; - vector extra_clauses; - vector binaries; - std::vector> offsetsize; - bool full_watching = false; - std::array nonces; - typedef unordered_set GatesTable; - - vector scheduled; - vector marks; - vector mu1_ids, mu2_ids, - mu4_ids; // remember the ids and the literal. 2 and 4 are - // only used for lrat proofs, but we need 1 to - // promote binary clauses to irredundant - - vector lits; // result of definitions - vector rhs; // stack for storing RHS - vector unsimplified; // stack for storing unsimplified version (XOR, - // ITEs) for DRAT proof - vector chain; // store clauses to be able to delete them properly - vector clause; // storing partial clauses - vector - glargecounts; // count for large clauses to complement internal->noccs - vector gnew_largecounts; // count for large clauses to - // complement internal->noccs - GatesTable table; - std::array condbin; - std::array condeq; - - std::vector new_unwatched_binary_clauses; - // LRAT proofs - vector resolvent_analyzed; - mutable vector lrat_chain; // storing LRAT chain - -#ifdef LOGGING - uint64_t fresh_id; -#endif - - uint64_t &new_largecounts (int lit); - uint64_t &largecounts (int lit); - - void unmark_all (); - vector representant; // union-find - vector eager_representant; // union-find - vector representant_id; // lrat version of union-find - vector eager_representant_id; // lrat version of union-find - int &representative (int lit); - int representative (int lit) const; - LRAT_ID &representative_id (int lit); - LRAT_ID representative_id (int lit) const; - int &eager_representative (int lit); - int eager_representative (int lit) const; - LRAT_ID &eager_representative_id (int lit); - LRAT_ID eager_representative_id (int lit) const; - std::vector lazy_propagated_idx; - char &lazy_propagated (int lit); - - int find_lrat_representative_with_marks (int lit); - // representative in the union-find structure in the lazy equivalences - int find_representative (int lit); - // find the representative and produce the binary clause representing the - // normalization from the literal to the result. - int find_representative_and_compress (int, bool update_eager = true); - // find the lazy representative for the `lit' and `-lit' - void find_representative_and_compress_both (int); - // find the eager representative - int find_eager_representative (int); - - // compreses the path from lit to the representative with a new clause if - // needed. Save internal->lrat_chain to avoid any issue. - int find_eager_representative_and_compress (int); - // Import the path from the literal and its negation to the representative - // in the lazy graph to the eager part, producing the binary clauses. - void import_lazy_and_find_eager_representative_and_compress_both ( - int); // generates clauses for -lit and lit - - // returns the ID of the LRAT clause for the normalization from the - // literal lit to its argument, assuming that the representative was - // already compressed. - LRAT_ID find_representative_lrat (int lit); - // returns the ID of the LRAT clause for the eager normalization from the - // literal lit to its argument assuming that the representative was - // already compressed. - LRAT_ID find_eager_representative_lrat (int lit); - - // Writes the LRAT chain required for the eager normalization to - // `lrat_chain`. - void produce_eager_representative_lrat (int lit); - // Writes the LRAT chain required for the lazy normalization to - // `lrat_chain`. - void produce_representative_lrat (int lit); - - // learns a binary clause if not unit - Clause *maybe_add_binary_clause (int a, int b); - // add binary clause - Clause *add_binary_clause (int a, int b); - // add tmp clause - Clause *add_tmp_binary_clause (int a, int b); - // add clause taking core of tmp or full - Clause *learn_binary_tmp_or_full_clause (int a, int b); - - // promotes a clause from redundant to irredundant. We do this for all - // clauses involved in gates to make sure that we produce correct result. - void promote_clause (Clause *); - - // Merge functions. We actually need different several versions for LRAT - // in order to simplify the proof production. - // - // When merging binary clauses, we can simply produce the LRAT chain by - // (1) using the two binary clauses and (2) the reason clause from the - // literals to the representatives. - // - // The same approach does not work for merging gates because the - // representative might be also a representative of another literal - // (because of eager rewriting), requiring to resolve more than once on - // the same literal. An example of this are the two gates 4=-2&7 and - // 6=-2&1, the rewriting 7=1 and the equivalence 4=1. The simple road of - // merging 6 and 4 (requires resolving away 1) + adding the rewrite 4 to 1 - // (requires adding 1) does not work. - // - // Therefore, we actually go for the more regular road and produce two - // equivalence: the merge from the LHS, followed by the actual equivalence - // (by combining it with the rewrite). In DRAT this is less important - // because the checker finds a chain and is less restricted than our LRAT - // chain. - bool merge_literals_equivalence (int lit, int other, Clause *c1, - Clause *c2); - bool merge_literals_lrat (Gate *g, Gate *h, int lit, int other, - const std::vector & = {}, - const std::vector & = {}); - bool merge_literals_lrat (int lit, int other, - const std::vector & = {}, - const std::vector & = {}); - - // proof production - vector lrat_chain_and_gate; - void push_lrat_id (const Clause *const c, int lit); - void push_lrat_unit (int lit); - - // pushes the clause with the reasons to rewrite clause - // unless: - // - the rewriting is not necessary (resolvent_marked == 1) - // - it is overwritten by one of the arguments - void push_id_and_rewriting_lrat_unit (Clause *c, Rewrite rewrite1, - std::vector &chain, - bool = true, - Rewrite rewrite2 = Rewrite (), - int execept_lhs = 0, - int except_lhs2 = 0); - void push_id_and_rewriting_lrat_full (Clause *c, Rewrite rewrite1, - std::vector &chain, - bool = true, - Rewrite rewrite2 = Rewrite (), - int execept_lhs = 0, - int except_lhs2 = 0); - // TODO: does nothing except pushing on the stack, remove! - void push_id_on_chain (std::vector &chain, Clause *c); - // TODO: does nothing except pushing on the stack, remove! - void push_id_on_chain (std::vector &chain, - const std::vector &c); - // TODO: does nothing except pushing on the stack, remove! - void push_id_on_chain (std::vector &chain, Rewrite rewrite, int); - void update_and_gate_build_lrat_chain ( - Gate *g, Gate *h, std::vector &extra_reasons_lit, - std::vector &extra_reasons_ulit, bool remove_units = true); - void update_and_gate_unit_build_lrat_chain ( - Gate *g, int src, LRAT_ID id1, LRAT_ID id2, int dst, - std::vector &extra_reasons_lit, - std::vector &extra_reasons_ulit); - // occs - vector gtab; - GOccs &goccs (int lit); - void connect_goccs (Gate *g, int lit); - vector garbage; - void mark_garbage (Gate *); - // remove the gate from the table - bool remove_gate (Gate *); - bool remove_gate (GatesTable::iterator git); - void index_gate (Gate *); - - // second counter for size, complements noccs - uint64_t &largecount (int lit); - - // simplification - bool skip_and_gate (Gate *g); - bool skip_xor_gate (Gate *g); - void update_and_gate (Gate *g, GatesTable::iterator, int src, int dst, - LRAT_ID id1, LRAT_ID id2, int falsified = 0, - int clashing = 0); - void update_xor_gate (Gate *g, GatesTable::iterator); - void shrink_and_gate (Gate *g, int falsified = 0, int clashing = 0); - bool simplify_gate (Gate *g); - void simplify_and_gate (Gate *g); - void simplify_ite_gate (Gate *g); - Clause *simplify_xor_clause (int lhs, Clause *); - void simplify_xor_gate (Gate *g); - bool simplify_gates (int lit); - void simplify_and_sort_xor_lrat_clauses (const vector &, - vector &, int, - int except2 = 0, bool flip = 0); - void simplify_unit_xor_lrat_clauses (const vector &, int); - - // rewriting - bool rewriting_lhs (Gate *g, int dst); - bool rewrite_gates (int dst, int src, LRAT_ID id1, LRAT_ID id2); - bool rewrite_gate (Gate *g, int dst, int src, LRAT_ID id1, LRAT_ID id2); - void rewrite_xor_gate (Gate *g, int dst, int src); - void rewrite_and_gate (Gate *g, int dst, int src, LRAT_ID id1, - LRAT_ID id2); - void rewrite_ite_gate (Gate *g, int dst, int src); - - size_t units; // next trail position to propagate - bool propagate_unit (int lit); - bool propagate_units (); - size_t propagate_units_and_equivalences (); - bool propagate_equivalence (int lit); - - // gates - void init_closure (); - void reset_closure (); - void reset_extraction (); - void reset_and_gate_extraction (); - void extract_and_gates (Closure &); - void extract_gates (); - void extract_and_gates_with_base_clause (Clause *c); - void init_and_gate_extraction (); - Gate *find_first_and_gate (Clause *base_clause, int lhs); - Gate *find_remaining_and_gate (Clause *base_clause, int lhs); - void extract_and_gates (); - - Gate *find_and_lits (const vector &rhs, Gate *except = nullptr); - // rhs is sorted, so passing by copy - Gate *find_gate_lits (const vector &rhs, Gate_Type typ, - Gate *except = nullptr); - Gate *find_xor_lits (const vector &rhs); - // not const to normalize negations, also fixes the order of the LRAT - Gate *find_ite_gate (Gate *, bool &); - Gate *find_xor_gate (Gate *); - - void reset_xor_gate_extraction (); - void init_xor_gate_extraction (std::vector &candidates); - LRAT_ID check_and_add_to_proof_chain (vector &clause); - void add_xor_matching_proof_chain (Gate *g, int lhs1, - const vector &, - int lhs2, vector &, - vector &); - void add_xor_shrinking_proof_chain (Gate *g, int src); - void extract_xor_gates (); - void extract_xor_gates_with_base_clause (Clause *c); - Clause *find_large_xor_side_clause (std::vector &lits); - - void merge_condeq (int cond, lit_equivalences &condeq, - lit_equivalences ¬_condeq); - void find_conditional_equivalences (int lit, lit_implications &condbin, - lit_equivalences &condeq); - void copy_conditional_equivalences (int lit, lit_implications &condbin); - void check_ite_implied (int lhs, int cond, int then_lit, int else_lit); - void check_ite_gate_implied (Gate *g); - void check_and_gate_implied (Gate *g); - void check_ite_lrat_reasons (Gate *g, bool = false); - void check_contained_module_rewriting (Clause *c, int lit, bool, - int except); - void delete_proof_chain (); - - // ite gate extraction - void extract_ite_gates_of_literal (int); - void extract_ite_gates_of_variable (int idx); - void extract_condeq_pairs (int lit, lit_implications &condbin, - lit_equivalences &condeq); - void init_ite_gate_extraction (std::vector &candidates); - lit_implications::const_iterator find_lit_implication_second_literal ( - int lit, lit_implications::const_iterator begin, - lit_implications::const_iterator end); - void search_condeq (int lit, int pos_lit, - lit_implications::const_iterator pos_begin, - lit_implications::const_iterator pos_end, int neg_lit, - lit_implications::const_iterator neg_begin, - lit_implications::const_iterator neg_end, - lit_equivalences &condeq); - void reset_ite_gate_extraction (); - void extract_ite_gates (); - - void forward_subsume_matching_clauses (); - - void extract_congruence (); - - void add_ite_matching_proof_chain (Gate *g, Gate *h, int lhs1, int lhs2, - std::vector &reasons1, - std::vector &reasons2); - void add_ite_turned_and_binary_clauses (Gate *g); - Gate *new_and_gate (Clause *, int); - Gate *new_ite_gate (int lhs, int cond, int then_lit, int else_lit, - std::vector &&clauses); - Gate *new_xor_gate (const vector &, int); - // check - void check_xor_gate_implied (Gate const *const); - void check_ternary (int a, int b, int c); - void check_binary_implied (int a, int b); - void check_implied (); - - // learn units. You can delay units if you want to learn several at once before - // propagation. Otherwise, propagate! If you need propagation even if nothing is set, use the - // second parameter. - // - // The function can also learn the empty clause if the unit is already set. Do not add the unit in - // the chain! - bool learn_congruence_unit (int unit, bool = false, bool = false); - bool fully_propagate (); - void learn_congruence_unit_falsifies_lrat_chain (Gate *g, int src, - int dst, - int clashing, - int falsified, int unit); - void learn_congruence_unit_when_lhs_set (Gate *g, int src, LRAT_ID id1, - LRAT_ID id2, int dst); - - void find_units (); - void find_equivalences (); - void subsume_clause (Clause *subsuming, Clause *subsumed); - bool find_subsuming_clause (Clause *c); - void produce_rewritten_clause_lrat_and_clean (vector &, - int execept_lhs = 0, - bool = true); - // rewrite the clause using eager rewriting and rew1 and rew2, except for - // 2 literals Usage: - // - the except are used to ignore LHS of gates that have not and should - // not be rewritten. - // - TODO: except_lhs2 should never be used actually - // - the Rewrite are for additional rewrite to allow for lazy rewrites - // to be taken into account without being added to the eager rewriting - // (yet) - Clause *produce_rewritten_clause_lrat (Clause *c, int execept_lhs = 0, - bool remove_units = true, bool = true); - void produce_rewritten_clause_lrat (vector &, - int execept_lhs = 0, - bool = true); - void compute_rewritten_clause_lrat_simple (Clause *c, int except); - // variant where we update the indices after removing the tautologies and - // remove the tautological clauses - void produce_rewritten_clause_lrat_and_clean ( - std::vector &litIds, int except_lhs, - size_t &old_position1, size_t &old_position2, - bool remove_units = true); - // binary extraction and ternary strengthening - void extract_binaries (); - bool find_binary (int, int) const; - - Clause *new_tmp_clause (std::vector &clause); - Clause *maybe_promote_tmp_binary_clause (Clause *); - void check_not_tmp_binary_clause (Clause *c); - Clause *new_clause (); - // - void sort_literals_by_var (vector &rhs); - void sort_literals_by_var_except (vector &rhs, int, int except2 = 0); - - // schedule - queue schedule; - void schedule_literal (int lit); - void add_clause_to_chain (std::vector, LRAT_ID); - // proof. If delete_id is non-zero, then delete the clause instead of - // learning it - LRAT_ID simplify_and_add_to_proof_chain (vector &unsimplified, - LRAT_ID delete_id = 0); - - // we define our own wrapper as cadical has otherwise a non-compatible - // marking system - signed char &marked (int lit); - void set_mu1_reason (int lit, Clause *c); - void set_mu2_reason (int lit, Clause *c); - void set_mu4_reason (int lit, Clause *c); - LitClausePair marked_mu1 (int lit); - LitClausePair marked_mu2 (int lit); - LitClausePair marked_mu4 (int lit); - - // XOR - uint32_t number_from_xor_reason_reversed (const std::vector &rhs); - uint32_t number_from_xor_reason (const std::vector &rhs, int, - int except2 = 0, bool flip = 0); - void gate_sort_lrat_reasons (std::vector &, int, - int except2 = 0, bool flip = 0); - void gate_sort_lrat_reasons (LitClausePair &, int, int except2 = 0, - bool flip = 0); - - bool rewrite_ite_gate_to_and (Gate *g, int dst, int src, size_t c, - size_t d, int cond_lit_to_learn_if_degenerated); - void produce_ite_merge_then_else_reasons ( - Gate *g, int dst, int src, std::vector &reasons_implication, - std::vector &reasons_back); - void produce_ite_merge_lhs_then_else_reasons ( - Gate *g, std::vector &reasons_implication, - std::vector &reasons_back, - std::vector &reasons_unit, bool, bool &); - void rewrite_ite_gate_update_lrat_reasons (Gate *g, int src, int dst); - void simplify_ite_gate_produce_unit_lrat (Gate *g, int lit, size_t idx1, - size_t idx2); - void merge_and_gate_lrat_produce_lrat ( - Gate *g, Gate *h, std::vector &reasons_lrat, - std::vector &reasons_lrat_back, bool remove_units = true); - // first index is a binary clause after unit propagation and the second - // has length 3 - bool simplify_ite_gate_to_and (Gate *g, size_t idx1, size_t idx2, - int removed); - void - merge_ite_gate_same_then_else_lrat (std::vector &clauses, - std::vector &reasons_implication, - std::vector &reasons_back); - void simplify_ite_gate_then_else_set ( - Gate *g, std::vector &reasons_implication, - std::vector &reasons_back, size_t idx1, size_t idx2); - - void simplify_ite_gate_condition_set ( - Gate *g, std::vector &reasons_lrat, - std::vector &reasons_back_lrat, size_t idx1, size_t idx2); - bool normalize_ite_lits_gate (Gate *rhs); -}; - -} // namespace CaDiCaL - -ABC_NAMESPACE_CXX_HEADER_END - -#endif diff --git a/src/sat/cadical/contract.hpp b/src/sat/cadical/contract.hpp deleted file mode 100644 index 4a3ba066ab..0000000000 --- a/src/sat/cadical/contract.hpp +++ /dev/null @@ -1,142 +0,0 @@ -#ifndef _contract_hpp_INCLUDED -#define _contract_hpp_INCLUDED - -#include "global.h" - -ABC_NAMESPACE_CXX_HEADER_START - -/*------------------------------------------------------------------------*/ -#ifndef CADICAL_NCONTRACTS -/*------------------------------------------------------------------------*/ - -// If the user violates API contracts while calling functions declared in -// 'cadical.hpp' and implemented in 'solver.cpp' then an error is reported. -// Currently we also force aborting the program. In the future it might be -// better to allow the user to provide a call back function, which then can -// for instance throw a C++ exception or execute a 'longjmp' in 'C' etc. - -#define CONTRACT_VIOLATED(...) \ - do { \ - fatal_message_start (); \ - fprintf (stderr, \ - "invalid API usage of '%s' in '%s': ", __PRETTY_FUNCTION__, \ - __FILE__); \ - fprintf (stderr, __VA_ARGS__); \ - fputc ('\n', stderr); \ - fflush (stderr); \ - abort (); \ - } while (0) - -/*------------------------------------------------------------------------*/ - -namespace CaDiCaL { - -// It would be much easier to just write 'REQUIRE (this, "not initialized")' -// which however produces warnings due to the '-Wnonnull' check. Note, that -// 'this' is always assumed to be non zero in modern C++. Much worse, if we -// use instead 'this != 0' or something similar like 'this != nullptr' then -// optimization silently removes this check ('gcc-7.4.0' at least) even -// though of course a zero pointer might be used as 'this' if the user did -// not initialize it. The only solution I found is to disable optimization -// for this check. It does not seem to be necessary for 'clang++' though -// ('clang++-6.0.0' at least). The alternative is to not check that the -// user forgot to initialize the solver pointer, but as long this works we -// keep this ugly hack. It also forces the function not to be inlined. -// The actual code I is in 'contract.cpp'. -// -void require_solver_pointer_to_be_non_zero (const void *ptr, - const char *function_name, - const char *file_name); -#define REQUIRE_NON_ZERO_THIS() \ - do { \ - require_solver_pointer_to_be_non_zero (this, __PRETTY_FUNCTION__, \ - __FILE__); \ - } while (0) - -} // namespace CaDiCaL - -/*------------------------------------------------------------------------*/ - -// These are common shortcuts for 'Solver' API contracts (requirements). - -#define REQUIRE(COND, ...) \ - do { \ - if ((COND)) \ - break; \ - CONTRACT_VIOLATED (__VA_ARGS__); \ - } while (0) - -#define REQUIRE_INITIALIZED() \ - do { \ - REQUIRE_NON_ZERO_THIS (); \ - REQUIRE (external, "external solver not initialized"); \ - REQUIRE (internal, "internal solver not initialized"); \ - } while (0) - -#define REQUIRE_VALID_STATE() \ - do { \ - REQUIRE_INITIALIZED (); \ - REQUIRE (this->state () & VALID, "solver in invalid state"); \ - } while (0) - -#define REQUIRE_READY_STATE() \ - do { \ - REQUIRE_VALID_STATE (); \ - REQUIRE (state () != ADDING, \ - "clause incomplete (terminating zero not added)"); \ - } while (0) - -#define REQUIRE_VALID_OR_SOLVING_STATE() \ - do { \ - REQUIRE_INITIALIZED (); \ - REQUIRE (this->state () & (VALID | SOLVING), \ - "solver neither in valid nor solving state"); \ - } while (0) - -#define REQUIRE_VALID_LIT(LIT) \ - do { \ - REQUIRE ((int) (LIT) && ((int) (LIT)) != INT_MIN, \ - "invalid literal '%d'", (int) (LIT)); \ - REQUIRE (external->is_valid_input ((int) (LIT)), \ - "extension variable %d defined by the solver", (int) (LIT)); \ - } while (0) - -#define REQUIRE_STEADY_STATE() \ - do { \ - REQUIRE_INITIALIZED (); \ - REQUIRE (this->state () & STEADY, "solver is not in steady state"); \ - } while (0) - -/*------------------------------------------------------------------------*/ -#else // CADICAL_NCONTRACTS -/*------------------------------------------------------------------------*/ - -#define REQUIRE(...) \ - do { \ - } while (0) -#define REQUIRE_INITIALIZED() \ - do { \ - } while (0) -#define REQUIRE_VALID_STATE() \ - do { \ - } while (0) -#define REQUIRE_READY_STATE() \ - do { \ - } while (0) -#define REQUIRE_VALID_OR_SOLVING_STATE() \ - do { \ - } while (0) -#define REQUIRE_VALID_LIT(...) \ - do { \ - } while (0) -#define REQUIRE_STEADY_STATE() \ - do { \ - } while (0) - -/*------------------------------------------------------------------------*/ -#endif -/*------------------------------------------------------------------------*/ - -ABC_NAMESPACE_CXX_HEADER_END - -#endif diff --git a/src/sat/cadical/cover.hpp b/src/sat/cadical/cover.hpp deleted file mode 100644 index 47e6f1b822..0000000000 --- a/src/sat/cadical/cover.hpp +++ /dev/null @@ -1,36 +0,0 @@ -#ifndef _cover_hpp_INCLUDED -#define _cover_hpp_INCLUDED - -#include "global.h" - -/*------------------------------------------------------------------------*/ - -// This header only provides the 'COVER' macro for testing. It is unrelated -// to 'cover.cpp' which implements covered clause elimination (CCE), but we -// wanted to use the name base name in both cases. More explanation on CCE -// is provided in 'cover.cpp'. - -/*------------------------------------------------------------------------*/ - -// Coverage goal, used similar to 'CADICAL_assert' (but with flipped condition) and -// also included even if 'CADICAL_NDEBUG' is defined (in optimizing compilation). -// -// This should in essence not be used in production code. -// -// There seems to be no problem overloading the name 'COVER' of this macro -// with the constant 'COVER' of 'Internal::Mode' (surprisingly). - -#define COVER(COND) \ - do { \ - if (!(COND)) \ - break; \ - fprintf (stderr, \ - "%scadical%s: %s:%d: %s: Coverage goal %s`%s'%s reached.\n", \ - terr.bold_code (), terr.normal_code (), __FUNCTION__, \ - __LINE__, __FILE__, terr.green_code (), #COND, \ - terr.normal_code ()); \ - fflush (stderr); \ - abort (); \ - } while (0) - -#endif diff --git a/src/sat/cadical/decompose.hpp b/src/sat/cadical/decompose.hpp deleted file mode 100644 index 9c93e84486..0000000000 --- a/src/sat/cadical/decompose.hpp +++ /dev/null @@ -1,29 +0,0 @@ -#ifndef _decompose_hpp_INCLUDED -#define _decompose_hpp_INCLUDED - -#include "global.h" - -ABC_NAMESPACE_CXX_HEADER_START - -namespace CaDiCaL { - -// This implements Tarjan's algorithm for decomposing the binary implication -// graph intro strongly connected components (SCCs). Literals in one SCC -// are equivalent and we replace them all by the literal with the smallest -// index in the SCC. These variables are marked 'substituted' and will be -// removed from all clauses. Their value will be fixed during 'extend'. - -#define TRAVERSED UINT_MAX // mark completely traversed - -struct DFS { - unsigned idx; // depth first search index - unsigned min; // minimum reachable index - Clause *parent; // for lrat - DFS () : idx (0), min (0), parent (0) {} -}; - -} // namespace CaDiCaL - -ABC_NAMESPACE_CXX_HEADER_END - -#endif diff --git a/src/sat/cadical/delay.hpp b/src/sat/cadical/delay.hpp deleted file mode 100644 index 9ee9a92348..0000000000 --- a/src/sat/cadical/delay.hpp +++ /dev/null @@ -1,44 +0,0 @@ -#ifndef _delay_hpp_INCLUDED -#define _delay_hpp_INCLUDED - -#include "global.h" - -#include -#include - -ABC_NAMESPACE_CXX_HEADER_START - -namespace CaDiCaL { -struct Delay { - unsigned count; - unsigned current; - - Delay () : count (0), current (0) {} - - bool delay () { - if (count) { - --count; - return true; - } else { - return false; - } - } - - void bump_delay () { - current += current < std::numeric_limits::max (); - count = current; - } - - void reduce_delay () { - if (!current) - return; - current /= 2; - count = current; - } -}; - -} // namespace CaDiCaL - -ABC_NAMESPACE_CXX_HEADER_END - -#endif diff --git a/src/sat/cadical/drattracer.hpp b/src/sat/cadical/drattracer.hpp deleted file mode 100644 index 8c2c023d51..0000000000 --- a/src/sat/cadical/drattracer.hpp +++ /dev/null @@ -1,59 +0,0 @@ -#ifndef _drattracer_h_INCLUDED -#define _drattracer_h_INCLUDED - -#include "global.h" - -#include "tracer.hpp" - -ABC_NAMESPACE_CXX_HEADER_START - -namespace CaDiCaL { - -class DratTracer : public FileTracer { - - Internal *internal; - File *file; - bool binary; -#ifndef CADICAL_QUIET - int64_t added, deleted; -#endif - void put_binary_zero (); - void put_binary_lit (int external_lit); - - // support DRAT - void drat_add_clause (const vector &); - void drat_delete_clause (const vector &); - -public: - // own and delete 'file' - DratTracer (Internal *, File *file, bool binary); - ~DratTracer (); - - void connect_internal (Internal *i) override; - void begin_proof (int64_t) override {} // skip - - void add_original_clause (int64_t, bool, const vector &, - bool = false) override {} // skip - - void add_derived_clause (int64_t, bool, const vector &, - const vector &) override; - - void delete_clause (int64_t, bool, const vector &) override; - - void finalize_clause (int64_t, const vector &) override {} // skip - - void report_status (int, int64_t) override {} // skip - -#ifndef CADICAL_QUIET - void print_statistics (); -#endif - bool closed () override; - void close (bool) override; - void flush (bool) override; -}; - -} // namespace CaDiCaL - -ABC_NAMESPACE_CXX_HEADER_END - -#endif diff --git a/src/sat/cadical/elim.hpp b/src/sat/cadical/elim.hpp deleted file mode 100644 index 73e1bf5479..0000000000 --- a/src/sat/cadical/elim.hpp +++ /dev/null @@ -1,59 +0,0 @@ -#ifndef _elim_hpp_INCLUDED -#define _elim_hpp_INCLUDED - -#include "global.h" - -#include "heap.hpp" // Alphabetically after 'elim.hpp'. - -ABC_NAMESPACE_CXX_HEADER_START - -namespace CaDiCaL { - -struct Internal; - -struct elim_more { - Internal *internal; - elim_more (Internal *i) : internal (i) {} - bool operator() (unsigned a, unsigned b); -}; - -typedef heap ElimSchedule; - -struct proof_clause { - int64_t id; - vector literals; - // for lrat - unsigned cid; // cadical_kitten id - bool learned; - vector chain; -}; - -enum GateType { NO = 0, EQUI = 1, AND = 2, ITE = 3, XOR = 4, DEF = 5 }; - -struct Eliminator { - - Internal *internal; - ElimSchedule schedule; - - Eliminator (Internal *i) - : internal (i), schedule (elim_more (i)), definition_unit (0), - gatetype (NO) {} - ~Eliminator (); - - queue backward; - - Clause *dequeue (); - void enqueue (Clause *); - - vector gates; - unsigned definition_unit; - vector proof_clauses; - vector marked; - GateType gatetype; -}; - -} // namespace CaDiCaL - -ABC_NAMESPACE_CXX_HEADER_END - -#endif diff --git a/src/sat/cadical/ema.hpp b/src/sat/cadical/ema.hpp deleted file mode 100644 index 51eb5f8c16..0000000000 --- a/src/sat/cadical/ema.hpp +++ /dev/null @@ -1,74 +0,0 @@ -#ifndef _ema_hpp_INCLUDED -#define _ema_hpp_INCLUDED - -#include "global.h" - -#include - -ABC_NAMESPACE_CXX_HEADER_START - -namespace CaDiCaL { - -struct Internal; - -// This is a more complex generic exponential moving average class to -// support more robust initialization (see comments in the 'update' -// implementation). - -struct EMA { - -#ifdef LOGGING - uint64_t updated; -#endif - double value; // unbiased (corrected) moving average - double biased; // biased initialized moving average - double alpha; // input scaling with 'alpha = 1 - beta' - double beta; // decay of 'biased' with 'beta = 1 - alpha' - double exp; // 'exp = pow (beta, updated)' - - EMA () - : -#ifdef LOGGING - updated (0), -#endif - value (0), biased (0), alpha (0), beta (0), exp (0) { - } - - EMA (double a) - : -#ifdef LOGGING - updated (0), -#endif - value (0), biased (0), alpha (a), beta (1 - a), exp (!!beta) { - CADICAL_assert (beta >= 0); - } - - operator double () const { return value; } - void update (Internal *, double y, const char *name); -}; - -} // namespace CaDiCaL - -/*------------------------------------------------------------------------*/ - -// Compact average update and initialization macros for better logging. - -#define UPDATE_AVERAGE(A, Y) \ - do { \ - A.update (internal, (Y), #A); \ - } while (0) - -#define INIT_EMA(E, WINDOW) \ - do { \ - CADICAL_assert ((WINDOW) >= 1); \ - double ALPHA = 1.0 / (double) (WINDOW); \ - E = EMA (ALPHA); \ - LOG ("init " #E " EMA target alpha %g window %d", ALPHA, \ - (int) WINDOW); \ - } while (0) - -/*------------------------------------------------------------------------*/ - -ABC_NAMESPACE_CXX_HEADER_END - -#endif diff --git a/src/sat/cadical/external.hpp b/src/sat/cadical/external.hpp deleted file mode 100644 index 7618fb936a..0000000000 --- a/src/sat/cadical/external.hpp +++ /dev/null @@ -1,467 +0,0 @@ -#ifndef _external_hpp_INCLUDED -#define _external_hpp_INCLUDED - -#include "global.h" - -/*------------------------------------------------------------------------*/ - -#include "range.hpp" -#include -#include - -/*------------------------------------------------------------------------*/ - -ABC_NAMESPACE_CXX_HEADER_START - -namespace CaDiCaL { - -using namespace std; - -/*------------------------------------------------------------------------*/ - -// The CaDiCaL code is split into three layers: -// -// Solver: facade object providing the actual API of the solver -// External: communication layer between 'Solver' and 'Internal' -// Internal: the actual solver code -// -// Note, that 'Solver' is defined in 'cadical.hpp' and 'solver.cpp', while -// 'External' and 'Internal' in '{external,internal}.{hpp,cpp}'. -// -// Also note, that any user should access the library only through the -// 'Solver' API. For the library internal 'Parser' code we make an -// exception and allow access to both 'External' and 'Internal'. The former -// to enforce the same external to internal mapping of variables and the -// latter for profiling and messages. The same applies to 'App'. -// -// The 'External' class provided here stores the information needed to map -// external variable indices to internal variables (actually literals). -// This is helpful for shrinking the working size of the internal solver -// after many variables become inactive. It will also help to provide -// support for extended resolution in the future, since it allows to -// introduce only internally visible variables (even though we do not know -// how to support generating incremental proofs in this situation yet). -// -// External literals are usually called 'elit' and internal 'ilit'. - -/*------------------------------------------------------------------------*/ - -struct Clause; -struct Internal; -struct CubesWithStatus; - -/*------------------------------------------------------------------------*/ - -/*------------------------------------------------------------------------*/ - -struct External { - - /*==== start of state ==================================================*/ - - Internal *internal; // The actual internal solver. - - int max_var; // External maximum variable index. - size_t vsize; // Allocated external size. - - vector vals; // Current external (extended) assignment. - vector e2i; // External 'idx' to internal 'lit'. - - vector assumptions; // External assumptions. - vector constraint; // External constraint. Terminated by zero. - - vector - ext_units; // External units. Needed to compute LRAT for eclause - vector ext_flags; // to avoid duplicate units - vector eclause; // External version of original input clause. - // The extension stack for reconstructing complete satisfying assignments - // (models) of the original external formula is kept in this external - // solver object. It keeps track of blocked clauses and clauses containing - // eliminated variable. These irredundant clauses are stored in terms of - // external literals on the 'extension' stack after mapping the - // internal literals given as arguments with 'externalize'. - - bool extended; // Have been extended. - bool concluded; - vector extension; // Solution reconstruction extension stack. - - vector witness; // Literal witness on extension stack. - vector tainted; // Literal tainted in adding literals. - - vector ervars; // Variables added through Extended Resolution. - - vector frozentab; // Reference counts for frozen variables. - - // Regularly checked terminator if non-zero. The terminator is set from - // 'Solver::set (Terminator *)' and checked by 'Internal::terminating ()'. - - Terminator *terminator; - - // If there is a learner export learned clauses. - - Learner *learner; - - void export_learned_empty_clause (); - void export_learned_unit_clause (int ilit); - void export_learned_large_clause (const vector &); - - // If there is a listener for fixed assignments. - - FixedAssignmentListener *fixed_listener; - - // If there is an external propagator. - - ExternalPropagator *propagator; - - vector is_observed; // Quick flag for each external variable - - // Saved 'forgettable' original clauses coming from the external - // propagator. The value of the map starts with a Boolean flag indicating - // if the clause is still present or got already deleted, and then - // followed by the literals of the clause. - unordered_map> forgettable_original; - - void add_observed_var (int elit); - void remove_observed_var (int elit); - void reset_observed_vars (); - - bool observed (int elit); - bool is_witness (int elit); - bool is_decision (int elit); - - void force_backtrack (size_t new_level); - - //----------------------------------------------------------------------// - - signed char *solution; // Given solution checking for debugging. - int solution_size; // Given solution checking for debugging. - vector original; // Saved original formula for checking. - - // If 'opts.checkfrozen' is set make sure that only literals are added - // which were never completely molten before. These molten literals are - // marked at the beginning of the 'solve' call. Note that variables - // larger than 'max_var' are not molten and can thus always be used in the - // future. Only needed to check and debug old style freeze semantics. - // - vector moltentab; - - //----------------------------------------------------------------------// - - const Range vars; // Provides safe variable iterations. - - /*==== end of state ====================================================*/ - - // These two just factor out common sanity (CADICAL_assertion) checking code. - - inline int vidx (int elit) const { - CADICAL_assert (elit); - CADICAL_assert (elit != INT_MIN); - int res = abs (elit); - CADICAL_assert (res <= max_var); - return res; - } - - inline int vlit (int elit) const { - CADICAL_assert (elit); - CADICAL_assert (elit != INT_MIN); - CADICAL_assert (abs (elit) <= max_var); - return elit; - } - - inline bool is_valid_input (int elit) { - CADICAL_assert (elit); - CADICAL_assert (elit != INT_MIN); - int eidx = abs (elit); - return eidx > max_var || !ervars[eidx]; - } - - /*----------------------------------------------------------------------*/ - - // The following five functions push individual literals or clauses on the - // extension stack. They all take internal literals as argument, and map - // them back to external literals first, before pushing them on the stack. - - void push_zero_on_extension_stack (); - - // Our general version of extension stacks always pushes a set of witness - // literals (for variable elimination the literal of the eliminated - // literal and for blocked clauses the blocking literal) followed by all - // the clause literals starting with and separated by zero. - // - void push_clause_literal_on_extension_stack (int ilit); - void push_witness_literal_on_extension_stack (int ilit); - - void push_clause_on_extension_stack (Clause *); - void push_clause_on_extension_stack (Clause *, int witness); - void push_binary_clause_on_extension_stack (int64_t id, int witness, - int other); - - // The main 'extend' function which extends an internal assignment to an - // external assignment using the extension stack (and sets 'extended'). - // - void extend (); - void conclude_sat (); - - /*----------------------------------------------------------------------*/ - - // Marking external literals. - - unsigned elit2ulit (int elit) const { - CADICAL_assert (elit); - CADICAL_assert (elit != INT_MIN); - const int idx = abs (elit) - 1; - CADICAL_assert (idx <= max_var); - return 2u * idx + (elit < 0); - } - - bool marked (const vector &map, int elit) const { - const unsigned ulit = elit2ulit (elit); - return ulit < map.size () ? map[ulit] : false; - } - - void mark (vector &map, int elit) { - const unsigned ulit = elit2ulit (elit); - if (ulit >= map.size ()) - map.resize (ulit + 1, false); - map[ulit] = true; - } - - void unmark (vector &map, int elit) { - const unsigned ulit = elit2ulit (elit); - if (ulit < map.size ()) - map[ulit] = false; - } - - /*----------------------------------------------------------------------*/ - - void push_external_clause_and_witness_on_extension_stack ( - const vector &clause, const vector &witness, int64_t id); - - void push_id_on_extension_stack (int64_t id); - - // Restore a clause, which was pushed on the extension stack. - void restore_clause (const vector::const_iterator &begin, - const vector::const_iterator &end, - const int64_t id); - - void restore_clauses (); - - /*----------------------------------------------------------------------*/ - - // Explicitly freeze and melt literals (instead of just freezing - // internally and implicitly assumed literals). Passes on freezing and - // melting to the internal solver, which has separate frozen counters. - - void freeze (int elit); - void melt (int elit); - - bool frozen (int elit) { - CADICAL_assert (elit); - CADICAL_assert (elit != INT_MIN); - int eidx = abs (elit); - if (eidx > max_var) - return false; - if (eidx >= (int) frozentab.size ()) - return false; - return frozentab[eidx] > 0; - } - - /*----------------------------------------------------------------------*/ - - External (Internal *); - ~External (); - - void enlarge (int new_max_var); // Enlarge allocated 'vsize'. - void init (int new_max_var, - bool extension = false); // Initialize up-to 'new_max_var'. - - int internalize ( - int, - bool extension = false); // Translate external to internal literal. - - /*----------------------------------------------------------------------*/ - - // According to the CaDiCaL API contract (as well as IPASIR) we have to - // forget about the previous assumptions after a 'solve' call. This - // should however be delayed until we transition out of an 'UNSATISFIED' - // state, i.e., after no more 'failed' calls are expected. Note that - // 'failed' requires to know the failing assumptions, and the 'failed' - // status of those should cleared before at start of the next 'solve'. - // As a consequence 'reset_assumptions' is only called from - // 'transition_to_unknown_state' in API calls in 'solver.cpp'. - - void reset_assumptions (); - - // Similarly to 'failed', 'conclude' needs to know about failing - // assumptions and therefore needs to be reset when leaving the - // 'UNSATISFIED' state. - // - void reset_concluded (); - - // Similarly a valid external assignment obtained through 'extend' has to - // be reset at each point it risks to become invalid. This is done - // in the external layer in 'external.cpp' functions.. - - void reset_extended (); - - // Finally, the semantics of incremental solving also require that limits - // are only valid for the next 'solve' call. Since the limits can not - // really be queried, handling them is less complex and they are just - // reset immediately at the end of 'External::solve'. - - void reset_limits (); - - /*----------------------------------------------------------------------*/ - - // Proxies to IPASIR functions. - - void add (int elit); - void assume (int elit); - int solve (bool preprocess_only); - - // We call it 'ival' as abbreviation for 'val' with 'int' return type to - // avoid bugs due to using 'signed char tmp = val (lit)', which might turn - // a negative value into a positive one (happened in 'extend'). - // - inline int ival (int elit) const { - CADICAL_assert (elit != INT_MIN); - int eidx = abs (elit); - bool val = false; - if (eidx <= max_var && (size_t) eidx < vals.size ()) - val = vals[eidx]; - if (elit < 0) - val = !val; - return val ? elit : -elit; - } - - bool flip (int elit); - bool flippable (int elit); - - bool failed (int elit); - - void terminate (); - - // Other important non IPASIR functions. - - /*----------------------------------------------------------------------*/ - - // Add literal to external constraint. - // - void constrain (int elit); - - // Returns true if 'solve' returned 20 because of the constraint. - // - bool failed_constraint (); - - // Deletes the current constraint clause. Called on - // 'transition_to_unknown_state' and if a new constraint is added. Can be - // called directly using the API. - // - void reset_constraint (); - - /*----------------------------------------------------------------------*/ - - int propagate_assumptions (); - void implied (std::vector &entrailed); - void conclude_unknown (); - - /*----------------------------------------------------------------------*/ - int lookahead (); - CaDiCaL::CubesWithStatus generate_cubes (int, int); - - int fixed (int elit) const; // Implemented in 'internal.hpp'. - - /*----------------------------------------------------------------------*/ - - void phase (int elit); - void unphase (int elit); - - /*----------------------------------------------------------------------*/ - - // Traversal functions for the witness stack and units. The explanation - // in 'external.cpp' for why we have to distinguish these cases. - - bool traverse_all_frozen_units_as_clauses (ClauseIterator &); - bool traverse_all_non_frozen_units_as_witnesses (WitnessIterator &); - bool traverse_witnesses_backward (WitnessIterator &); - bool traverse_witnesses_forward (WitnessIterator &); - - /*----------------------------------------------------------------------*/ - - // Copy flags for determining preprocessing state. - - void copy_flags (External &other) const; - - /*----------------------------------------------------------------------*/ - - // Check solver behaves as expected during testing and debugging. - - void check_assumptions_satisfied (); - void check_constraint_satisfied (); - void check_failing (); - - void check_solution_on_learned_clause (); - void check_solution_on_shrunken_clause (Clause *); - void check_solution_on_learned_unit_clause (int unit); - void check_no_solution_after_learning_empty_clause (); - - void check_learned_empty_clause () { - if (solution) - check_no_solution_after_learning_empty_clause (); - } - - void check_learned_unit_clause (int unit) { - if (solution) - check_solution_on_learned_unit_clause (unit); - } - - void check_learned_clause () { - if (solution) - check_solution_on_learned_clause (); - } - - void check_shrunken_clause (Clause *c) { - if (solution) - check_solution_on_shrunken_clause (c); - } - - void check_assignment (int (External::*assignment) (int) const); - - void check_satisfiable (); - void check_unsatisfiable (); - - void check_solve_result (int res); - - void update_molten_literals (); - - /*----------------------------------------------------------------------*/ - - // For debugging and testing only. See 'solution.hpp' for more details. - // TODO: if elit > solution_size, elit is an extension variable. For now - // the clause will count as satisfied regardless. For the future one - // should check that actually there is one consistent extension for the - // solution that satisfies the clauses with this extension variable (by - // setting it to a value once a clause is learned which is not satisfied - // already). - // - inline int sol (int elit) const { - CADICAL_assert (solution); - CADICAL_assert (elit != INT_MIN); - int eidx = abs (elit); - if (eidx > max_var) - return 0; - else if (eidx > solution_size) - return elit; - signed char value = solution[eidx]; - if (!value) - return 0; - if (elit < 0) - value = -value; - return value > 0 ? elit : -elit; - } -}; - -} // namespace CaDiCaL - -ABC_NAMESPACE_CXX_HEADER_END - -#endif diff --git a/src/sat/cadical/factor.hpp b/src/sat/cadical/factor.hpp deleted file mode 100644 index 3aa5ea6c66..0000000000 --- a/src/sat/cadical/factor.hpp +++ /dev/null @@ -1,60 +0,0 @@ -#ifndef _factor_hpp_INCLUDED -#define _factor_hpp_INCLUDED - -#include "global.h" - -#include "clause.hpp" -#include "heap.hpp" - -ABC_NAMESPACE_CXX_HEADER_START - -namespace CaDiCaL { - -struct Internal; - -struct factor_occs_size { - Internal *internal; - factor_occs_size (Internal *i) : internal (i) {} - bool operator() (unsigned a, unsigned b); -}; - -struct Quotient { - Quotient (int f) : factor (f) {} - ~Quotient () {} - int factor; - size_t id; - int64_t bid; // for LRAT - Quotient *prev, *next; - vector qlauses; - vector matches; - size_t matched; -}; - -typedef heap FactorSchedule; - -struct Factoring { - Factoring (Internal *, int64_t); - ~Factoring (); - - // These are initialized by the constructor - Internal *internal; - int64_t limit; - FactorSchedule schedule; - - int initial; - int bound; - vector count; - vector fresh; - vector counted; - vector nounted; - vector flauses; - struct { - Quotient *first, *last; - } quotients; -}; - -} // namespace CaDiCaL - -ABC_NAMESPACE_CXX_HEADER_END - -#endif diff --git a/src/sat/cadical/file.hpp b/src/sat/cadical/file.hpp deleted file mode 100644 index 860e9613a7..0000000000 --- a/src/sat/cadical/file.hpp +++ /dev/null @@ -1,221 +0,0 @@ -#ifndef _file_hpp_INCLUDED -#define _file_hpp_INCLUDED - -#include "global.h" - -#include -#include -#include -#include -#include - -#ifndef CADICAL_NDEBUG -#include -#endif - -/*------------------------------------------------------------------------*/ -#ifdef WIN32 -#define cadical_putc_unlocked putc -#define cadical_getc_unlocked getc -#else -#ifndef NUNLOCKED -#define cadical_putc_unlocked putc_unlocked -#define cadical_getc_unlocked getc_unlocked -#else -#define cadical_putc_unlocked putc -#define cadical_getc_unlocked getc -#endif -#endif -/*------------------------------------------------------------------------*/ - -ABC_NAMESPACE_CXX_HEADER_START - -namespace CaDiCaL { - -// Wraps a 'C' file 'FILE' with name and supports zipped reading and writing -// through 'popen' using external helper tools. Reading has line numbers. -// Compression and decompression relies on external utilities, e.g., 'gzip', -// 'bzip2', 'xz', and '7z', which should be in the 'PATH'. - -struct Internal; - -class File { - - Internal *internal; -#if !defined(CADICAL_QUIET) || !defined(CADICAL_NDEBUG) - bool writing; -#endif - - int close_file; // need to close file (1=fclose, 2=pclose, 3=pipe) - int child_pid; - FILE *file; - char *_name; - uint64_t _lineno; - uint64_t _bytes; - - File (Internal *, bool, int, int, FILE *, const char *); - - static FILE *open_file (Internal *, const char *path, const char *mode); - static FILE *read_file (Internal *, const char *path); - static FILE *write_file (Internal *, const char *path); - - static void split_str (const char *, std::vector &); - static void delete_str_vector (std::vector &); - - static FILE *open_pipe (Internal *, const char *fmt, const char *path, - const char *mode); - static FILE *read_pipe (Internal *, const char *fmt, const int *sig, - const char *path); -#ifndef WIN32 - static FILE *write_pipe (Internal *, const char *fmt, const char *path, - int &child_pid); -#endif - -public: - static char *find_program (const char *prg); // search in 'PATH' - static bool exists (const char *path); // file exists? - static bool writable (const char *path); // can write to that file? - static size_t size (const char *path); // file size in bytes - - bool piping (); // Is opened file a pipe? - - // Does the file match the file type signature. - // - static bool match (Internal *, const char *path, const int *sig); - - // Read from existing file. Assume given name. - // - static File *read (Internal *, FILE *f, const char *name); - - // Open file from path name for reading (possibly through opening a pipe - // to a decompression utility, based on the suffix). - // - static File *read (Internal *, const char *path); - - // Same for writing as for reading above. - // - static File *write (Internal *, FILE *, const char *name); - static File *write (Internal *, const char *path); - - ~File (); - - // Using the 'unlocked' versions here is way faster but - // not thread safe if the same file is used by different - // threads, which on the other hand currently is impossible. - - int get () { - CADICAL_assert (!writing); - int res = cadical_getc_unlocked (file); - if (res == '\n') - _lineno++; - if (res != EOF) - _bytes++; - return res; - } - - bool put (char ch) { - CADICAL_assert (writing); - if (cadical_putc_unlocked (ch, file) == EOF) - return false; - _bytes++; - return true; - } - - bool endl () { return put ('\n'); } - - bool put (unsigned char ch) { - CADICAL_assert (writing); - if (cadical_putc_unlocked (ch, file) == EOF) - return false; - _bytes++; - return true; - } - - bool put (const char *s) { - for (const char *p = s; *p; p++) - if (!put (*p)) - return false; - return true; - } - - bool put (int lit) { - CADICAL_assert (writing); - if (!lit) - return put ('0'); - else if (lit == -2147483648) { - CADICAL_assert (lit == INT_MIN); - return put ("-2147483648"); - } else { - char buffer[11]; - int i = sizeof buffer; - buffer[--i] = 0; - CADICAL_assert (lit != INT_MIN); - unsigned idx = abs (lit); - while (idx) { - CADICAL_assert (i > 0); - buffer[--i] = '0' + idx % 10; - idx /= 10; - } - if (lit < 0 && !put ('-')) - return false; - return put (buffer + i); - } - } - - bool put (int64_t l) { - CADICAL_assert (writing); - if (!l) - return put ('0'); - else if (l == INT64_MIN) { - CADICAL_assert (sizeof l == 8); - return put ("-9223372036854775808"); - } else { - char buffer[21]; - int i = sizeof buffer; - buffer[--i] = 0; - CADICAL_assert (l != INT64_MIN); - uint64_t k = l < 0 ? -l : l; - while (k) { - CADICAL_assert (i > 0); - buffer[--i] = '0' + k % 10; - k /= 10; - } - if (l < 0 && !put ('-')) - return false; - return put (buffer + i); - } - } - - bool put (uint64_t l) { - CADICAL_assert (writing); - if (!l) - return put ('0'); - else { - char buffer[22]; - int i = sizeof buffer; - buffer[--i] = 0; - while (l) { - CADICAL_assert (i > 0); - buffer[--i] = '0' + l % 10; - l /= 10; - } - return put (buffer + i); - } - } - - const char *name () const { return _name; } - uint64_t lineno () const { return _lineno; } - uint64_t bytes () const { return _bytes; } - - void connect_internal (Internal *i) { internal = i; } - bool closed () { return !file; } - - void close (bool print = false); - void flush (); -}; - -} // namespace CaDiCaL - -ABC_NAMESPACE_CXX_HEADER_END - -#endif diff --git a/src/sat/cadical/flags.hpp b/src/sat/cadical/flags.hpp deleted file mode 100644 index 995438c7a8..0000000000 --- a/src/sat/cadical/flags.hpp +++ /dev/null @@ -1,91 +0,0 @@ -#ifndef _flags_hpp_INCLUDED -#define _flags_hpp_INCLUDED - -#include "global.h" - -ABC_NAMESPACE_CXX_HEADER_START - -namespace CaDiCaL { - -struct Flags { // Variable flags. - - // The first set of flags is related to 'analyze' and 'minimize'. - // - bool seen : 1; // seen in generating first UIP clause in 'analyze' - bool keep : 1; // keep in learned clause in 'minimize' - bool poison : 1; // can not be removed in 'minimize' - bool removable : 1; // can be removed in 'minimize' - bool shrinkable : 1; // can be removed in 'shrink' - bool added : 1; // has already been added to lrat_chain (in 'minimize') - - // These three variable flags are used to schedule clauses in subsumption - // ('subsume'), variables in bounded variable elimination ('elim') and in - // hyper ternary resolution ('ternary'). - // - bool elim : 1; // removed since last 'elim' round (*) - bool subsume : 1; // added since last 'subsume' round (*) - bool ternary : 1; // added in ternary clause since last 'ternary' (*) - bool sweep : 1; - bool blockable : 1; - - unsigned char - marked_signed : 2; // generate correct LRAT chains in decompose - unsigned char factor : 2; - - // These literal flags are used by blocked clause elimination ('block'). - // - unsigned char block : 2; // removed since last 'block' round (*) - unsigned char skip : 2; // skip this literal as blocking literal - - // Bits for handling assumptions. - // - unsigned char assumed : 2; - unsigned char failed : 2; // 0 if not part of failure - // 1 if positive lit is in failure - // 2 if negated lit is in failure - - enum { - UNUSED = 0, - ACTIVE = 1, - FIXED = 2, - ELIMINATED = 3, - SUBSTITUTED = 4, - PURE = 5 - }; - - unsigned char status : 3; - - // Initialized explicitly in 'Internal::init' through this function. - // - Flags () { - seen = keep = poison = removable = shrinkable = added = sweep = false; - subsume = elim = ternary = true; - block = 3u; - skip = assumed = failed = marked_signed = factor = 0; - status = UNUSED; - } - - bool unused () const { return status == UNUSED; } - bool active () const { return status == ACTIVE; } - bool fixed () const { return status == FIXED; } - bool eliminated () const { return status == ELIMINATED; } - bool substituted () const { return status == SUBSTITUTED; } - bool pure () const { return status == PURE; } - - // The flags marked with '(*)' are copied during 'External::copy_flags', - // which in essence means they are reset in the copy if they were clear. - // This avoids the effort of fruitless preprocessing the copy. - - void copy (Flags &dst) const { - dst.elim = elim; - dst.subsume = subsume; - dst.ternary = ternary; - dst.block = block; - } -}; - -} // namespace CaDiCaL - -ABC_NAMESPACE_CXX_HEADER_END - -#endif diff --git a/src/sat/cadical/format.hpp b/src/sat/cadical/format.hpp deleted file mode 100644 index cf229e7607..0000000000 --- a/src/sat/cadical/format.hpp +++ /dev/null @@ -1,42 +0,0 @@ -#ifndef _format_hpp_INCLUDED -#define _format_hpp_INCLUDED - -#include "global.h" - -#include -#include - -ABC_NAMESPACE_CXX_HEADER_START - -namespace CaDiCaL { - -// This class provides a 'printf' style formatting utility. -// Only '%c', '%d', '%s' are supported at this point. -// It is used to capture and save an error message. - -class Format { - char *buffer; - int64_t count, size; - void enlarge (); - void push_char (char); - void push_string (const char *); - void push_int (int); - void push_uint64 (uint64_t); - const char *add (const char *fmt, va_list &); - -public: - Format () : buffer (0), count (0), size (0) {} - ~Format () { - if (buffer) - delete[] buffer; - } - const char *init (const char *fmt, ...) CADICAL_ATTRIBUTE_FORMAT (2, 3); - const char *append (const char *fmt, ...) CADICAL_ATTRIBUTE_FORMAT (2, 3); - operator const char * () const { return count ? buffer : 0; } -}; - -} // namespace CaDiCaL - -ABC_NAMESPACE_CXX_HEADER_END - -#endif diff --git a/src/sat/cadical/frattracer.hpp b/src/sat/cadical/frattracer.hpp deleted file mode 100644 index cadfa37217..0000000000 --- a/src/sat/cadical/frattracer.hpp +++ /dev/null @@ -1,68 +0,0 @@ -#ifndef _frattracer_h_INCLUDED -#define _frattracer_h_INCLUDED - -#include "global.h" - -ABC_NAMESPACE_CXX_HEADER_START - -namespace CaDiCaL { - -class FratTracer : public FileTracer { - - Internal *internal; - File *file; - bool binary; - bool with_antecedents; - -#ifndef CADICAL_QUIET - int64_t added, deleted; - int64_t finalized, original; -#endif - - vector delete_ids; - - void put_binary_zero (); - void put_binary_lit (int external_lit); - void put_binary_id (int64_t id, bool = false); - - // support FRAT - void frat_add_original_clause (int64_t, const vector &); - void frat_add_derived_clause (int64_t, const vector &); - void frat_add_derived_clause (int64_t, const vector &, - const vector &); - void frat_delete_clause (int64_t, const vector &); - void frat_finalize_clause (int64_t, const vector &); - -public: - // own and delete 'file' - FratTracer (Internal *, File *file, bool binary, bool antecedents); - ~FratTracer (); - - void connect_internal (Internal *i) override; - void begin_proof (int64_t) override {} // skip - - void add_original_clause (int64_t, bool, const vector &, - bool = false) override; - - void add_derived_clause (int64_t, bool, const vector &, - const vector &) override; - - void delete_clause (int64_t, bool, const vector &) override; - - void finalize_clause (int64_t, const vector &) override; - - void report_status (int, int64_t) override {} // skip - -#ifndef CADICAL_QUIET - void print_statistics (); -#endif - bool closed () override; - void close (bool) override; - void flush (bool) override; -}; - -} // namespace CaDiCaL - -ABC_NAMESPACE_CXX_HEADER_END - -#endif diff --git a/src/sat/cadical/global.h b/src/sat/cadical/global.h deleted file mode 100644 index 916246cab7..0000000000 --- a/src/sat/cadical/global.h +++ /dev/null @@ -1,21 +0,0 @@ -#ifndef ABC_SAT_CADICAL_GLOBAL_HPP_ -#define ABC_SAT_CADICAL_GLOBAL_HPP_ - -// comment out next line to enable cadical debug mode -#define CADICAL_NDEBUG - -#define CADICAL_NBUILD -#define CADICAL_QUIET -#define CADICAL_NCONTRACTS -#define CADICAL_NTRACING -#define CADICAL_NCLOSEFROM - -#ifdef CADICAL_NDEBUG -#define CADICAL_assert(ignore) ((void)0) -#else -#define CADICAL_assert(cond) assert(cond) -#endif - -#include "misc/util/abc_global.h" - -#endif diff --git a/src/sat/cadical/heap.hpp b/src/sat/cadical/heap.hpp deleted file mode 100644 index c429414132..0000000000 --- a/src/sat/cadical/heap.hpp +++ /dev/null @@ -1,218 +0,0 @@ -#ifndef _heap_hpp_INCLUDED -#define _heap_hpp_INCLUDED - -#include "global.h" - -#include "util.hpp" // Alphabetically after 'heap.hpp'. - -#include - -ABC_NAMESPACE_CXX_HEADER_START - -namespace CaDiCaL { - -using namespace std; - -// This is a priority queue with updates for unsigned integers implemented -// as binary heap. We need to map integer elements added (through -// 'push_back') to positions on the binary heap in 'array'. This map is -// stored in the 'pos' array. This approach is really wasteful (at least in -// terms of memory) if only few and a sparse set of integers is added. So -// it should not be used in this situation. A generic priority queue would -// implement the mapping externally provided by another template parameter. -// Since we use 'UINT_MAX' as 'not contained' flag, we can only have -// 'UINT_MAX - 1' elements in the heap. - -const unsigned invalid_heap_position = UINT_MAX; - -template class heap { - - vector array; // actual binary heap - vector pos; // positions of elements in array - C less; // less-than for elements - - // Map an element to its position entry in the 'pos' map. - // - unsigned &index (unsigned e) { - if (e >= pos.size ()) - pos.resize (1 + (size_t) e, invalid_heap_position); - unsigned &res = pos[e]; - CADICAL_assert (res == invalid_heap_position || (size_t) res < array.size ()); - return res; - } - - bool has_parent (unsigned e) { return index (e) > 0; } - bool has_left (unsigned e) { - return (size_t) 2 * index (e) + 1 < size (); - } - bool has_right (unsigned e) { - return (size_t) 2 * index (e) + 2 < size (); - } - - unsigned parent (unsigned e) { - CADICAL_assert (has_parent (e)); - return array[(index (e) - 1) / 2]; - } - - unsigned left (unsigned e) { - CADICAL_assert (has_left (e)); - return array[2 * index (e) + 1]; - } - - unsigned right (unsigned e) { - CADICAL_assert (has_right (e)); - return array[2 * index (e) + 2]; - } - - // Exchange elements 'a' and 'b' in 'array' and fix their positions. - // - void exchange (unsigned a, unsigned b) { - unsigned &i = index (a), &j = index (b); - swap (array[i], array[j]); - swap (i, j); - } - - // Bubble up an element as far as necessary. - // - void up (unsigned e) { - unsigned p; - while (has_parent (e) && less ((p = parent (e)), e)) - exchange (p, e); - } - - // Bubble down an element as far as necessary. - // - void down (unsigned e) { - while (has_left (e)) { - unsigned c = left (e); - if (has_right (e)) { - unsigned r = right (e); - if (less (c, r)) - c = r; - } - if (!less (e, c)) - break; - exchange (e, c); - } - } - - // Very expensive checker for the main 'heap' invariant. Can be enabled - // to find violations of antisymmetry in the client implementation of - // 'less' and as well of course bugs in this heap implementation. It - // should be enabled during testing applications of the heap. - // - void check () { -#if 0 // EXPENSIVE HEAP CHECKING IF ENABLED -#warning "expensive checking in heap enabled" - CADICAL_assert (array.size () <= invalid_heap_position); - for (size_t i = 0; i < array.size (); i++) { - size_t l = 2*i + 1, r = 2*i + 2; - if (l < array.size ()) CADICAL_assert (!less (array[i], array[l])); - if (r < array.size ()) CADICAL_assert (!less (array[i], array[r])); - CADICAL_assert (array[i] >= 0); - { - CADICAL_assert ((size_t) array[i] < pos.size ()); - CADICAL_assert (i == (size_t) pos[array[i]]); - } - } - for (size_t i = 0; i < pos.size (); i++) { - if (pos[i] == invalid_heap_position) continue; - CADICAL_assert (pos[i] < array.size ()); - CADICAL_assert (array[pos[i]] == (unsigned) i); - } -#endif - } - -public: - heap (const C &c) : less (c) {} - - // Number of elements in the heap. - // - size_t size () const { return array.size (); } - - // Check if no more elements are in the heap. - // - bool empty () const { return array.empty (); } - - // Check whether 'e' is already in the heap. - // - bool contains (unsigned e) const { - if ((size_t) e >= pos.size ()) - return false; - return pos[e] != invalid_heap_position; - } - - // Add a new (not contained) element 'e' to the heap. - // - void push_back (unsigned e) { - CADICAL_assert (!contains (e)); - size_t i = array.size (); - CADICAL_assert (i < (size_t) invalid_heap_position); - array.push_back (e); - index (e) = (unsigned) i; - up (e); - down (e); - check (); - } - - // Returns the maximum element in the heap. - // - unsigned front () const { - CADICAL_assert (!empty ()); - return array[0]; - } - - // Removes the maximum element in the heap. - // - unsigned pop_front () { - CADICAL_assert (!empty ()); - unsigned res = array[0], last = array.back (); - if (size () > 1) - exchange (res, last); - index (res) = invalid_heap_position; - array.pop_back (); - if (size () > 1) - down (last); - check (); - return res; - } - - // Notify the heap, that evaluation of 'less' has changed for 'e'. - // - void update (unsigned e) { - CADICAL_assert (contains (e)); - up (e); - down (e); - check (); - } - - void clear () { - array.clear (); - pos.clear (); - } - - void erase () { - erase_vector (array); - erase_vector (pos); - } - - void shrink () { - shrink_vector (array); - shrink_vector (pos); - } - - // Standard iterators 'inherited' from 'vector'. - // - typedef typename vector::iterator iterator; - typedef typename vector::const_iterator const_iterator; - iterator begin () { return array.begin (); } - iterator end () { return array.end (); } - const_iterator begin () const { return array.begin (); } - const_iterator end () const { return array.end (); } -}; - -} // namespace CaDiCaL - -ABC_NAMESPACE_CXX_HEADER_END - -#endif diff --git a/src/sat/cadical/idruptracer.hpp b/src/sat/cadical/idruptracer.hpp deleted file mode 100644 index 4ea478705b..0000000000 --- a/src/sat/cadical/idruptracer.hpp +++ /dev/null @@ -1,116 +0,0 @@ -#ifndef _idruptracer_h_INCLUDED -#define _idruptracer_h_INCLUDED - -#include "global.h" - -ABC_NAMESPACE_CXX_HEADER_START - -class FileTracer; - -namespace CaDiCaL { - -struct IdrupClause { - IdrupClause *next; // collision chain link for hash table - uint64_t hash; // previously computed full 64-bit hash - int64_t id; // id of clause - unsigned size; - int literals[1]; -}; - -class IdrupTracer : public FileTracer { - - Internal *internal; - File *file; - bool binary; - bool piping; // The 'file' is a pipe and needs eagerly flushing. - - // hash table for conclusion - // - uint64_t num_clauses; // number of clauses in hash table - uint64_t size_clauses; // size of clause hash table - IdrupClause **clauses; // hash table of clauses - vector imported_clause; - vector assumptions; - - static const unsigned num_nonces = 4; - - uint64_t nonces[num_nonces]; // random numbers for hashing - uint64_t last_hash; // last computed hash value of clause - int64_t last_id; // id of the last added clause - IdrupClause *last_clause; - uint64_t compute_hash (int64_t); // compute and save hash value of clause - - IdrupClause *new_clause (); - void delete_clause (IdrupClause *); - - static uint64_t reduce_hash (uint64_t hash, uint64_t size); - - void enlarge_clauses (); // enlarge hash table for clauses - void insert (); // insert clause in hash table - bool - find_and_delete (const int64_t); // find clause position in hash table - -#ifndef CADICAL_QUIET - int64_t added, deleted, weakened, restore, original, solved; -#endif - - void flush_if_piping (); - - void put_binary_zero (); - void put_binary_lit (int external_lit); - void put_binary_id (int64_t id, bool = false); - - void idrup_add_derived_clause (const vector &clause); - void idrup_delete_clause (int64_t id, const vector &clause); - void idrup_add_restored_clause (const vector &clause); - void idrup_add_original_clause (const vector &clause); - void idrup_conclude_and_delete (const vector &conclusion); - void idrup_report_status (int status); - void idrup_conclude_sat (const vector &model); - void idrup_conclude_unknown (const vector &trail); - void idrup_solve_query (); - -public: - IdrupTracer (Internal *, File *file, bool); - ~IdrupTracer (); - - // proof section: - void add_derived_clause (int64_t, bool, const vector &, - const vector &) override; - void add_assumption_clause (int64_t, const vector &, - const vector &) override; - void weaken_minus (int64_t, const vector &) override; - void delete_clause (int64_t, bool, const vector &) override; - void add_original_clause (int64_t, bool, const vector &, - bool = false) override; - void report_status (int, int64_t) override; - void conclude_sat (const vector &) override; - void conclude_unsat (ConclusionType, const vector &) override; - void conclude_unknown (const vector &) override; - - void solve_query () override; - void add_assumption (int) override; - void reset_assumptions () override; - - // skip - void begin_proof (int64_t) override {} - void finalize_clause (int64_t, const vector &) override {} - void strengthen (int64_t) override {} - void add_constraint (const vector &) override {} - - // logging and file io - void connect_internal (Internal *i) override; - -#ifndef CADICAL_QUIET - void print_statistics (); -#endif - bool closed () override; - void close (bool) override; - void flush (bool) override; -}; - -} // namespace CaDiCaL - -ABC_NAMESPACE_CXX_HEADER_END - -#endif diff --git a/src/sat/cadical/instantiate.hpp b/src/sat/cadical/instantiate.hpp deleted file mode 100644 index f6c8767dd9..0000000000 --- a/src/sat/cadical/instantiate.hpp +++ /dev/null @@ -1,51 +0,0 @@ -#ifndef _instantiate_hpp_INCLUDED -#define _instantiate_hpp_INCLUDED - -#include "global.h" - -ABC_NAMESPACE_CXX_HEADER_START - -namespace CaDiCaL { - -// We are trying to remove literals in clauses, which occur in few clauses -// and further restrict this removal to variables for which variable -// elimination failed. Thus if for instance we succeed in removing the -// single occurrence of a literal, pure literal elimination can -// eliminate the corresponding variable in the next variable elimination -// round. The set of such literal clause candidate pairs is collected at -// the end of a variable elimination round and tried before returning. The -// name of this technique is inspired by 'variable instantiation' as -// described in [AnderssonBjesseCookHanna-DAC'02] and apparently -// successfully used in the 'Oepir' SAT solver. - -struct Clause; -struct Internal; - -class Instantiator { - - friend struct Internal; - - struct Candidate { - int lit; - int size; - size_t negoccs; - Clause *clause; - Candidate (int l, Clause *c, int s, size_t n) - : lit (l), size (s), negoccs (n), clause (c) {} - }; - - vector candidates; - -public: - void candidate (int l, Clause *c, int s, size_t n) { - candidates.push_back (Candidate (l, c, s, n)); - } - - operator bool () const { return !candidates.empty (); } -}; - -} // namespace CaDiCaL - -ABC_NAMESPACE_CXX_HEADER_END - -#endif diff --git a/src/sat/cadical/internal.hpp b/src/sat/cadical/internal.hpp deleted file mode 100644 index d86479a522..0000000000 --- a/src/sat/cadical/internal.hpp +++ /dev/null @@ -1,1866 +0,0 @@ -#ifndef _internal_hpp_INCLUDED -#define _internal_hpp_INCLUDED - -#include "global.h" - -/*------------------------------------------------------------------------*/ - -// Wrapped build specific headers which should go first. - -#include "inttypes.hpp" - -/*------------------------------------------------------------------------*/ - -// Common 'C' headers. - -#include -#include -#include -#include -#include -#include -#include -#include - -// Less common 'C' header. - -#ifndef WIN32 -extern "C" { -#include -} -#endif - -/*------------------------------------------------------------------------*/ - -// Common 'C++' headers. - -#include -#include -#include -#include -#include - -/*------------------------------------------------------------------------*/ - -// All internal headers are included here. This gives a nice overview on -// what is needed altogether. The 'Internal' class needs almost all the -// headers anyhow (since the idea is to avoid pointer references as much as -// possible). Most implementation files need to see the definition of the -// 'Internal' too. Thus there is no real advantage in trying to reduce the -// number of header files included here. The other benefit of having all -// header files here is that '.cpp' files then only need to include this. - -#include "arena.hpp" -#include "averages.hpp" -#include "bins.hpp" -#include "block.hpp" -#include "cadical.hpp" -#include "checker.hpp" -#include "clause.hpp" -#include "config.hpp" -#include "congruence.hpp" -#include "contract.hpp" -#include "cover.hpp" -#include "decompose.hpp" -#include "drattracer.hpp" -#include "elim.hpp" -#include "ema.hpp" -#include "external.hpp" -#include "factor.hpp" -#include "file.hpp" -#include "flags.hpp" -#include "format.hpp" -#include "frattracer.hpp" -#include "heap.hpp" -#include "idruptracer.hpp" -#include "instantiate.hpp" -#include "internal.hpp" -#include "level.hpp" -#include "lidruptracer.hpp" -#include "limit.hpp" -#include "logging.hpp" -#include "lratchecker.hpp" -#include "lrattracer.hpp" -#include "message.hpp" -#include "occs.hpp" -#include "options.hpp" -#include "parse.hpp" -#include "phases.hpp" -#include "profile.hpp" -#include "proof.hpp" -#include "queue.hpp" -#include "radix.hpp" -#include "random.hpp" -#include "range.hpp" -#include "reap.hpp" -#include "reluctant.hpp" -#include "resources.hpp" -#include "score.hpp" -#include "stats.hpp" -#include "sweep.hpp" -#include "terminal.hpp" -#include "tracer.hpp" -#include "util.hpp" -#include "var.hpp" -#include "veripbtracer.hpp" -#include "version.hpp" -#include "vivify.hpp" -#include "watch.hpp" - -// c headers -//extern "C" { -#include "kitten.h" -//} -/*------------------------------------------------------------------------*/ - -ABC_NAMESPACE_CXX_HEADER_START - -namespace CaDiCaL { - -using namespace std; - -struct Coveror; -struct External; -struct Walker; -class Tracer; -class FileTracer; -class StatTracer; - -struct CubesWithStatus { - int status = 0; - std::vector> cubes; -}; - -/*------------------------------------------------------------------------*/ - -struct Internal { - - /*----------------------------------------------------------------------*/ - - // The actual internal state of the solver is set and maintained in this - // section. This is currently only used for debugging and testing. - - enum Mode { - BLOCK = (1 << 0), - CONDITION = (1 << 1), - CONGRUENCE = (1 << 2), - COVER = (1 << 3), - DECOMP = (1 << 4), - DEDUP = (1 << 5), - ELIM = (1 << 6), - FACTOR = (1 << 7), - LUCKY = (1 << 8), - PROBE = (1 << 9), - SEARCH = (1 << 10), - SIMPLIFY = (1 << 11), - SUBSUME = (1 << 12), - SWEEP = (1 << 13), - TERNARY = (1 << 14), - TRANSRED = (1 << 15), - VIVIFY = (1 << 16), - WALK = (1 << 17), - }; - - bool in_mode (Mode m) const { return (mode & m) != 0; } - void set_mode (Mode m) { - CADICAL_assert (!(mode & m)); - mode |= m; - } - void reset_mode (Mode m) { - CADICAL_assert (mode & m); - mode &= ~m; - } - void require_mode (Mode m) const { CADICAL_assert (mode & m), (void) m; } - - /*----------------------------------------------------------------------*/ - - int mode; // current internal state - int tier1[2] = { - 2, 2}; // tier1 limit for 0=focused, 1=stable; aka tier1[stable] - int tier2[2] = { - 6, 6}; // tier2 limit for 0=focused, 1=stable; aka tier1[stable] - bool unsat; // empty clause found or learned - bool iterating; // report learned unit ('i' line) - bool localsearching; // true during local search - bool lookingahead; // true during look ahead - bool preprocessing; // true during preprocessing - bool protected_reasons; // referenced reasons are protected - bool force_saved_phase; // force saved phase in decision - bool searching_lucky_phases; // during 'lucky_phases' - bool stable; // true during stabilization phase - bool reported; // reported in this solving call - bool external_prop; // true if an external propagator is connected - bool did_external_prop; // true if ext. propagation happened - bool external_prop_is_lazy; // true if the external propagator is lazy - bool forced_backt_allowed; // external propagator can force backtracking - bool private_steps; // no notification of ext. prop during these steps - char rephased; // last type of resetting phases - Reluctant reluctant; // restart counter in stable mode - size_t vsize; // actually allocated variable data size - int max_var; // internal maximum variable index - int64_t clause_id; // last used id for clauses - int64_t original_id; // ids for original clauses to produce LRAT - int64_t reserved_ids; // number of reserved ids for original clauses - int64_t conflict_id; // store conflict id for finalize (frat) - int64_t saved_decisions; // to compute decision rate average - bool concluded; // keeps track of conclude - vector conclusion; // store ids of conclusion clauses - vector - unit_clauses_idx; // keep track of unit_clauses (LRAT/FRAT) - vector lrat_chain; // create LRAT in solver: option lratdirect - vector mini_chain; // used to create LRAT in minimize - vector minimize_chain; // used to create LRAT in minimize - vector unit_chain; // used to avoid duplicate units - vector inst_chain; // for LRAT in instantiate - vector>> - probehbr_chains; // only used if opts.probehbr=false - bool lrat; // generate LRAT internally - bool frat; // finalize non-deleted clauses in proof - int level; // decision level ('control.size () - 1') - Phases phases; // saved, target and best phases - signed char *vals; // assignment [-max_var,max_var] - vector marks; // signed marks [1,max_var] - vector frozentab; // frozen counters [1,max_var] - vector i2e; // maps internal 'idx' to external 'lit' - vector relevanttab; // Reference counts for observed variables. - Queue queue; // variable move to front decision queue - Links links; // table of links for decision queue - double score_inc; // current score increment - ScoreSchedule scores; // score based decision priority queue - vector stab; // table of variable scores [1,max_var] - vector vtab; // variable table [1,max_var] - vector parents; // parent literals during probing - vector ftab; // variable and literal flags - vector btab; // enqueue time stamps for queue - vector gtab; // time stamp table to recompute glue - vector otab; // table of occurrences for all literals - vector rtab; // table of redundant occurrences - vector ptab; // table for caching probing attempts - vector ntab; // number of one-sided occurrences table - vector big; // binary implication graph - vector wtab; // table of watches for all literals - Clause *conflict; // set in 'propagation', reset in 'analyze' - Clause *ignore; // ignored during 'vivify_propagate' - Clause *dummy_binary; // Dummy binary clause for subsumption - Clause *external_reason; // used as reason at external propagations - Clause *newest_clause; // used in external_propagate - bool force_no_backtrack; // for new clauses with external propagator - bool from_propagator; // differentiate new clauses... - bool ext_clause_forgettable; // Is new clause from propagator forgettable - int tainted_literal; // used for ILB - size_t notified; // next trail position to notify external prop - Clause *probe_reason; // set during probing - size_t propagated; // next trail position to propagate - size_t propagated2; // next binary trail position to propagate - size_t propergated; // propagated without blocking literals - size_t best_assigned; // best maximum assigned ever - size_t target_assigned; // maximum assigned without conflict - size_t no_conflict_until; // largest trail prefix without conflict - vector trail; // currently assigned literals - vector clause; // simplified in parsing & learning - vector assumptions; // assumed literals - vector constraint; // literals of the constraint - bool unsat_constraint; // constraint used for unsatisfiability? - bool marked_failed; // are the failed assumptions marked? - vector original; // original added literals - vector levels; // decision levels in learned clause - vector analyzed; // analyzed literals in 'analyze' - vector unit_analyzed; // to avoid duplicate units in lrat_chain - vector sign_marked; // literals skipped in 'decompose' - vector minimized; // removable or poison in 'minimize' - vector shrinkable; // removable or poison in 'shrink' - Reap reap; // radix heap for shrink - - vector sweep_schedule; // remember sweep varibles to reschedule - bool sweep_incomplete; // sweep - - cadical_kitten *citten; - - size_t num_assigned; // check for satisfied - - vector probes; // remaining scheduled probes - vector control; // 'level + 1 == control.size ()' - vector clauses; // ordered collection of all clauses - Averages averages; // glue, size, jump moving averages - Delay delay[2]; // Delay certain functions - Delay congruence_delay; // Delay congruence if not successful recently - Limit lim; // limits for various phases - Last last; // statistics at last occurrence - Inc inc; // increments on limits - - Delay delaying_vivify_irredundant; - Delay delaying_sweep; - - Proof *proof; // abstraction layer between solver and tracers - vector - tracers; // proof tracing objects (ie interpolant calculator) - vector - file_tracers; // file proof tracers (ie DRAT, LRAT...) - vector stat_tracers; // checkers - - Options opts; // run-time options - Stats stats; // statistics -#ifndef CADICAL_QUIET - Profiles profiles; // time profiles for various functions - bool force_phase_messages; // force 'phase (...)' messages -#endif - Arena arena; // memory arena for moving garbage collector - Format error_message; // provide persistent error message - string prefix; // verbose messages prefix - - Internal *internal; // proxy to 'this' in macros - External *external; // proxy to 'external' buddy in 'Solver' - - const unsigned max_used = 255; // must fix into the header of the clause! - /*----------------------------------------------------------------------*/ - - // Asynchronous termination flag written by 'terminate' and read by - // 'terminated_asynchronously' (the latter at the end of this header). - // - volatile bool termination_forced; - - /*----------------------------------------------------------------------*/ - - const Range vars; // Provides safe variable iteration. - const Sange lits; // Provides safe literal iteration. - - /*----------------------------------------------------------------------*/ - - Internal (); - ~Internal (); - - /*----------------------------------------------------------------------*/ - - // Internal delegates and helpers for corresponding functions in - // 'External' and 'Solver'. The 'init_vars' function initializes - // variables up to and including the requested variable index. - // - void init_vars (int new_max_var); - - void init_enqueue (int idx); - void init_queue (int old_max_var, int new_max_var); - - void init_scores (int old_max_var, int new_max_var); - - void add_original_lit (int lit); - - // only able to restore irredundant clause - void finish_added_clause_with_id (int64_t id, bool restore = false); - - // Reserve ids for original clauses to produce lrat - void reserve_ids (int number); - - // Enlarge tables. - // - void enlarge_vals (size_t new_vsize); - void enlarge (int new_max_var); - - // A variable is 'active' if it is not eliminated nor fixed. - // - bool active (int lit) { return flags (lit).active (); } - - int active () const { - int res = stats.active; -#ifndef CADICAL_NDEBUG - int tmp = max_var; - tmp -= stats.unused; - tmp -= stats.now.fixed; - tmp -= stats.now.eliminated; - tmp -= stats.now.substituted; - tmp -= stats.now.pure; - CADICAL_assert (tmp >= 0); - CADICAL_assert (tmp == res); -#endif - return res; - } - - void reactivate (int lit); // During 'restore'. - - // Currently remaining active redundant and irredundant clauses. - - int64_t redundant () const { return stats.current.redundant; } - - int64_t irredundant () const { return stats.current.irredundant; } - - double clause_variable_ratio () const { - return relative (irredundant (), active ()); - } - - // Scale values relative to clause variable ratio. - // - double scale (double v) const; - - // Unsigned literals (abs) with checks. - // - int vidx (int lit) const { - int idx; - CADICAL_assert (lit); - CADICAL_assert (lit != INT_MIN); - idx = abs (lit); - CADICAL_assert (idx <= max_var); - return idx; - } - - // Unsigned version with LSB denoting sign. This is used in indexing - // arrays by literals. The idea is to keep the elements in such an array - // for both the positive and negated version of a literal close together. - // - unsigned vlit (int lit) const { - return (lit < 0) + 2u * (unsigned) vidx (lit); - } - - int u2i (unsigned u) { - CADICAL_assert (u > 1); - int res = u / 2; - CADICAL_assert (res <= max_var); - if (u & 1) - res = -res; - return res; - } - - int citten2lit (unsigned ulit) { - int res = (ulit / 2) + 1; - CADICAL_assert (res <= max_var); - if (ulit & 1) - res = -res; - return res; - } - - unsigned lit2citten (int lit) { - int idx = vidx (lit) - 1; - return (lit < 0) + 2u * (unsigned) idx; - } - - int64_t unit_id (int lit) const { - CADICAL_assert (lrat || frat); - CADICAL_assert (val (lit) > 0); - const unsigned uidx = vlit (lit); - int64_t id = unit_clauses_idx[uidx]; - CADICAL_assert (id); - return id; - } - - inline int64_t &unit_clauses (int uidx) { - CADICAL_assert (lrat || frat); - CADICAL_assert (uidx > 0); - CADICAL_assert ((size_t) uidx < unit_clauses_idx.size ()); - return unit_clauses_idx[uidx]; - } - - // Helper functions to access variable and literal data. - // - Var &var (int lit) { return vtab[vidx (lit)]; } - Link &link (int lit) { return links[vidx (lit)]; } - Flags &flags (int lit) { return ftab[vidx (lit)]; } - int64_t &bumped (int lit) { return btab[vidx (lit)]; } - int &propfixed (int lit) { return ptab[vlit (lit)]; } - double &score (int lit) { return stab[vidx (lit)]; } - - const Flags &flags (int lit) const { return ftab[vidx (lit)]; } - - bool occurring () const { return !otab.empty (); } - bool watching () const { return !wtab.empty (); } - - Bins &bins (int lit) { return big[vlit (lit)]; } - Occs &occs (int lit) { return otab[vlit (lit)]; } - int64_t &noccs (int lit) { return ntab[vlit (lit)]; } - Watches &watches (int lit) { return wtab[vlit (lit)]; } - - // Variable bumping through exponential VSIDS (EVSIDS) as in MiniSAT. - // - bool use_scores () const { return opts.score && stable; } - void bump_variable_score (int lit); - void bump_variable_score_inc (); - void rescale_variable_scores (); - - // Marking variables with a sign (positive or negative). - // - signed char marked (int lit) const { - signed char res = marks[vidx (lit)]; - if (lit < 0) - res = -res; - return res; - } - void mark (int lit) { - CADICAL_assert (!marked (lit)); - marks[vidx (lit)] = sign (lit); - CADICAL_assert (marked (lit) > 0); - CADICAL_assert (marked (-lit) < 0); - } - void unmark (int lit) { - marks[vidx (lit)] = 0; - CADICAL_assert (!marked (lit)); - } - - // Use only bits 6 and 7 to store the sign or zero. The remaining - // bits can be used as additional flags. - // - signed char marked67 (int lit) const { - signed char res = marks[vidx (lit)] >> 6; - if (lit < 0) - res = -res; - return res; - } - void mark67 (int lit) { - signed char &m = marks[vidx (lit)]; - const signed char mask = 0x3f; -#ifndef CADICAL_NDEBUG - const signed char bits = m & mask; -#endif - m = (m & mask) | (sign (lit) << 6); - CADICAL_assert (marked (lit) > 0); - CADICAL_assert (marked (-lit) < 0); - CADICAL_assert ((m & mask) == bits); - CADICAL_assert (marked67 (lit) > 0); - CADICAL_assert (marked67 (-lit) < 0); - } - void unmark67 (int lit) { - signed char &m = marks[vidx (lit)]; - const signed char mask = 0x3f; -#ifndef CADICAL_NDEBUG - const signed bits = m & mask; -#endif - m &= mask; - CADICAL_assert ((m & mask) == bits); - } - - void unmark (vector &lits) { - for (const auto &lit : lits) - unmark (lit); - } - - // The other 6 bits of the 'marks' bytes can be used as additional - // (unsigned) marking bits. Currently we only use the least significant - // bit in 'condition' to mark variables in the conditional part. - // - bool getbit (int lit, int bit) const { - CADICAL_assert (0 <= bit), CADICAL_assert (bit < 6); - return marks[vidx (lit)] & (1 << bit); - } - void setbit (int lit, int bit) { - CADICAL_assert (0 <= bit), CADICAL_assert (bit < 6); - CADICAL_assert (!getbit (lit, bit)); - marks[vidx (lit)] |= (1 << bit); - CADICAL_assert (getbit (lit, bit)); - } - void unsetbit (int lit, int bit) { - CADICAL_assert (0 <= bit), CADICAL_assert (bit < 6); - CADICAL_assert (getbit (lit, bit)); - marks[vidx (lit)] &= ~(1 << bit); - CADICAL_assert (!getbit (lit, bit)); - } - - // Marking individual literals. - // - bool marked2 (int lit) const { - unsigned res = marks[vidx (lit)]; - CADICAL_assert (res <= 3); - unsigned bit = bign (lit); - return (res & bit) != 0; - } - void mark2 (int lit) { - marks[vidx (lit)] |= bign (lit); - CADICAL_assert (marked2 (lit)); - } - - // marks bits 1,2,3 and 4,5,6 depending on fact and sign of lit - // - bool getfact (int lit, int fact) const { - CADICAL_assert (fact == 1 || fact == 2 || fact == 4); - int res = marks[vidx (lit)]; - if (lit < 0) { - res >>= 3; - } else { - res &= 7; - } - // CADICAL_assert (!res || res == 1 || res == 2 || res == 4); - return res & fact; - } - - void markfact (int lit, int fact) { - CADICAL_assert (fact == 1 || fact == 2 || fact == 4); - CADICAL_assert (!getfact (lit, fact)); -#ifndef CADICAL_NDEBUG - int before = getfact (-lit, fact); -#endif - int res = marks[vidx (lit)]; - if (lit < 0) { - res |= fact << 3; - } else { - res |= fact; - } - marks[vidx (lit)] = res; - CADICAL_assert (getfact (lit, fact)); -#ifndef CADICAL_NDEBUG - CADICAL_assert (getfact (-lit, fact) == before); -#endif - } - - void unmarkfact (int lit, int fact) { - CADICAL_assert (fact == 1 || fact == 2 || fact == 4); - CADICAL_assert (getfact (lit, fact)); - int res = marks[vidx (lit)]; - if (lit < 0) { - res &= ~(fact << 3); - } else { - res &= ~fact; - } - marks[vidx (lit)] = res; - CADICAL_assert (!getfact (lit, fact)); - } - - // Marking and unmarking of all literals in a clause. - // - void mark_clause (); // mark 'this->clause' - void mark (Clause *); - void mark2 (Clause *); - void unmark_clause (); // unmark 'this->clause' - void unmark (Clause *); - - // Watch literal 'lit' in clause with blocking literal 'blit'. - // Inlined here, since it occurs in the tight inner loop of 'propagate'. - // - inline void watch_literal (int lit, int blit, Clause *c) { - CADICAL_assert (lit != blit); - Watches &ws = watches (lit); - ws.push_back (Watch (blit, c)); - LOG (c, "watch %d blit %d in", lit, blit); - } - - // Add two watches to a clause. This is used initially during allocation - // of a clause and during connecting back all watches after preprocessing. - // - inline void watch_clause (Clause *c) { - const int l0 = c->literals[0]; - const int l1 = c->literals[1]; - watch_literal (l0, l1, c); - watch_literal (l1, l0, c); - } - - inline void unwatch_clause (Clause *c) { - const int l0 = c->literals[0]; - const int l1 = c->literals[1]; - remove_watch (watches (l0), c); - remove_watch (watches (l1), c); - } - - // Update queue to point to last potentially still unassigned variable. - // All variables after 'queue.unassigned' in bump order are assumed to be - // assigned. Then update the 'queue.bumped' field and log it. This is - // inlined here since it occurs in several inner loops. - // - inline void update_queue_unassigned (int idx) { - CADICAL_assert (0 < idx); - CADICAL_assert (idx <= max_var); - queue.unassigned = idx; - queue.bumped = btab[idx]; - LOG ("queue unassigned now %d bumped %" PRId64 "", idx, btab[idx]); - } - - void bump_queue (int idx); - - // Mark (active) variables as eliminated, substituted, pure or fixed, - // which turns them into inactive variables. - // - void mark_eliminated (int); - void mark_substituted (int); - void mark_active (int); - void mark_fixed (int); - void mark_pure (int); - - // Managing clauses in 'clause.cpp'. Without explicit 'Clause' argument - // these functions work on the global temporary 'clause'. - // - Clause *new_clause (bool red, int glue = 0); - void promote_clause (Clause *, int new_glue); - void promote_clause_glue_only (Clause *, int new_glue); - size_t shrink_clause (Clause *, int new_size); - void minimize_sort_clause (); - void shrink_and_minimize_clause (); - void reset_shrinkable (); - void mark_shrinkable_as_removable (int, std::vector::size_type); - int shrink_literal (int, int, unsigned); - unsigned shrunken_block_uip (int, int, - std::vector::reverse_iterator &, - std::vector::reverse_iterator &, - std::vector::size_type, const int); - void shrunken_block_no_uip (const std::vector::reverse_iterator &, - const std::vector::reverse_iterator &, - unsigned &, const int); - void push_literals_of_block (const std::vector::reverse_iterator &, - const std::vector::reverse_iterator &, - int, unsigned); - unsigned shrink_next (int, unsigned &, unsigned &); - std::vector::reverse_iterator - minimize_and_shrink_block (std::vector::reverse_iterator &, - unsigned int &, unsigned int &, const int); - unsigned shrink_block (std::vector::reverse_iterator &, - std::vector::reverse_iterator &, int, - unsigned &, unsigned &, const int, unsigned); - unsigned shrink_along_reason (int, int, bool, bool &, unsigned); - - void deallocate_clause (Clause *); - void delete_clause (Clause *); - void mark_garbage (Clause *); - void assign_original_unit (int64_t, int); - void add_new_original_clause (int64_t); - Clause *new_learned_redundant_clause (int glue); - Clause *new_hyper_binary_resolved_clause (bool red, int glue); - Clause *new_clause_as (const Clause *orig); - Clause *new_resolved_irredundant_clause (); - - // Forward reasoning through propagation in 'propagate.cpp'. - // - int assignment_level (int lit, Clause *); - void build_chain_for_units (int lit, Clause *reason, bool forced); - void build_chain_for_empty (); - void search_assign (int lit, Clause *); - void search_assign_driving (int lit, Clause *reason); - void search_assign_external (int lit); - void search_assume_decision (int decision); - void assign_unit (int lit); - int64_t cache_lines (size_t bytes) { return (bytes + 127) / 128; } - int64_t cache_lines (size_t n, size_t bytes) { - return cache_lines (n * bytes); - } - bool propagate (); - -#ifdef PROFILE_MODE - bool propagate_wrapper (); - bool propagate_unstable (); - bool propagate_stable (); - void analyze_wrapper (); - void analyze_unstable (); - void analyze_stable (); - int decide_wrapper (); - int decide_stable (); - int decide_unstable (); -#else -#define propagate_wrapper propagate -#define analyze_wrapper analyze -#define decide_wrapper decide -#endif - - void propergate (); // Repropagate without blocking literals. - - // Undo and restart in 'backtrack.cpp'. - // - void unassign (int lit); - void update_target_and_best (); - void backtrack (int target_level = 0); - void backtrack_without_updating_phases (int target_level = 0); - - // Minimized learned clauses in 'minimize.cpp'. - // - bool minimize_literal (int lit, int depth = 0); - void minimize_clause (); - void calculate_minimize_chain (int lit, std::vector &stack); - - // Learning from conflicts in 'analyze.cc'. - // - void learn_empty_clause (); - void learn_unit_clause (int lit); - - void bump_variable (int lit); - void bump_variables (); - int recompute_glue (Clause *); - void bump_clause (Clause *); - void bump_clause2 (Clause *); - void clear_unit_analyzed_literals (); - void clear_analyzed_literals (); - void clear_analyzed_levels (); - void clear_minimized_literals (); - bool bump_also_reason_literal (int lit); - void bump_also_reason_literals (int lit, int depth_limit, - size_t size_limit); - void bump_also_all_reason_literals (); - void analyze_literal (int lit, int &open, int &resolvent_size, - int &antecedent_size); - void analyze_reason (int lit, Clause *, int &open, int &resolvent_size, - int &antecedent_size); - Clause *new_driving_clause (const int glue, int &jump); - int find_conflict_level (int &forced); - int determine_actual_backtrack_level (int jump); - void otfs_strengthen_clause (Clause *, int, int, - const std::vector &); - void otfs_subsume_clause (Clause *subsuming, Clause *subsumed); - int otfs_find_backtrack_level (int &forced); - Clause *on_the_fly_strengthen (Clause *conflict, int lit); - void update_decision_rate_average (); - void analyze (); - void iterate (); // report learned unit clause - - // Learning from external propagator in 'external_propagate.cpp' - // - bool external_propagate (); - bool external_check_solution (); - void add_external_clause (int propagated_lit = 0, - bool no_backtrack = false); - Clause *learn_external_reason_clause (int lit, int falsified_elit = 0, - bool no_backtrack = false); - Clause *wrapped_learn_external_reason_clause (int lit); - void explain_external_propagations (); - void explain_reason (int lit, Clause *, int &open); - void move_literals_to_watch (); - void handle_external_clause (Clause *); - void notify_assignments (); - void notify_decision (); - void notify_backtrack (size_t new_level); - void force_backtrack (size_t new_level); - int ask_decision (); - bool ask_external_clause (); - void add_observed_var (int ilit); - void remove_observed_var (int ilit); - bool observed (int ilit) const; - bool is_decision (int ilit); - void check_watched_literal_invariants (); - void set_tainted_literal (); - void renotify_trail_after_ilb (); - void renotify_trail_after_local_search (); - void renotify_full_trail (); - void connect_propagator (); - void mark_garbage_external_forgettable (int64_t id); - bool is_external_forgettable (int64_t id); -#ifndef CADICAL_NDEBUG - bool get_merged_literals (std::vector &); - void get_all_fixed_literals (std::vector &); -#endif - - void recompute_tier (); - // Use last learned clause to subsume some more. - // - void eagerly_subsume_recently_learned_clauses (Clause *); - - // Restarting policy in 'restart.cc'. - // - bool stabilizing (); - bool restarting (); - int reuse_trail (); - void restart (); - - // Functions to set and reset certain 'phases'. - // - void clear_phases (vector &); // reset argument to zero - void copy_phases (vector &); // copy 'saved' to argument - - // Resetting the saved phased in 'rephase.cpp'. - // - bool rephasing (); - char rephase_best (); - char rephase_flipping (); - char rephase_inverted (); - char rephase_original (); - char rephase_random (); - char rephase_walk (); - void shuffle_scores (); - void shuffle_queue (); - void rephase (); - - // Lucky feasible case checking. - // - int unlucky (int res); - bool lucky_propagate_discrepency (int); - int trivially_false_satisfiable (); - int trivially_true_satisfiable (); - int forward_false_satisfiable (); - int forward_true_satisfiable (); - int backward_false_satisfiable (); - int backward_true_satisfiable (); - int positive_horn_satisfiable (); - int negative_horn_satisfiable (); - - // Asynchronous terminating check. - // - bool terminated_asynchronously (int factor = 1); - - bool search_limits_hit (); - - void terminate () { - LOG ("forcing asynchronous termination"); - termination_forced = true; - } - - // Reducing means determining useless clauses with 'reduce' in - // 'reduce.cpp' as well as root level satisfied clause and then removing - // those which are not used as reason anymore with garbage collection. - // - bool flushing (); - bool reducing (); - void protect_reasons (); - void mark_clauses_to_be_flushed (); - void mark_useless_redundant_clauses_as_garbage (); - bool propagate_out_of_order_units (); - void unprotect_reasons (); - void reduce (); - - // Garbage collection in 'collect.cpp' called from 'reduce' and during - // inprocessing and preprocessing. - // - int clause_contains_fixed_literal (Clause *); - void remove_falsified_literals (Clause *); - void mark_satisfied_clauses_as_garbage (); - void copy_clause (Clause *); - void flush_watches (int lit, Watches &); - size_t flush_occs (int lit); - void flush_all_occs_and_watches (); - void update_reason_references (); - void copy_non_garbage_clauses (); - void delete_garbage_clauses (); - void check_clause_stats (); - void check_var_stats (); - bool arenaing (); - void garbage_collection (); - - // only remove binary clauses from the watches - void remove_garbage_binaries (); - - // Set-up occurrence list counters and containers. - // - void init_occs (); - void init_bins (); - void init_noccs (); - void clear_noccs (); - void clear_occs (); - void reset_occs (); - void reset_bins (); - void reset_noccs (); - - // Operators on watches. - // - void init_watches (); - void connect_watches (bool irredundant_only = false); - void connect_binary_watches (); - void sort_watches (); - void clear_watches (); - void reset_watches (); - - // Regular forward subsumption checking in 'subsume.cpp'. - // - void strengthen_clause (Clause *, int); - void subsume_clause (Clause *subsuming, Clause *subsumed); - int subsume_check (Clause *subsuming, Clause *subsumed); - int try_to_subsume_clause (Clause *, vector &shrunken); - void reset_subsume_bits (); - bool subsume_round (); - void subsume (); - - // Covered clause elimination of large clauses. - // - void covered_literal_addition (int lit, Coveror &); - void asymmetric_literal_addition (int lit, Coveror &); - void cover_push_extension (int lit, Coveror &); - bool cover_propagate_asymmetric (int lit, Clause *ignore, Coveror &); - bool cover_propagate_covered (int lit, Coveror &); - bool cover_clause (Clause *c, Coveror &); - int64_t cover_round (); - bool cover (); - - // Strengthening through vivification in 'vivify.cpp'. - // - void demote_clause (Clause *); - void flush_vivification_schedule (std::vector &, int64_t &); - void vivify_increment_stats (const Vivifier &vivifier); - void vivify_subsume_clause (Clause *subsuming, Clause *subsumed); - void compute_tier_limits (Vivifier &); - void vivify_initialize (Vivifier &vivifier, int64_t &ticks); - inline void vivify_prioritize_leftovers (char, size_t prioritized, - std::vector &schedule); - bool consider_to_vivify_clause (Clause *candidate); - void vivify_sort_watched (Clause *c); - bool vivify_instantiate ( - const std::vector &, Clause *, - std::vector> &lrat_stack, - int64_t &ticks); - void vivify_analyze_redundant (Vivifier &, Clause *start, bool &); - void vivify_build_lrat (int, Clause *, - std::vector> &); - void vivify_chain_for_units (int lit, Clause *reason); - void vivify_strengthen (Clause *candidate); - void vivify_assign (int lit, Clause *); - void vivify_assume (int lit); - bool vivify_propagate (int64_t &); - void vivify_deduce (Clause *candidate, Clause *conflct, int implied, - Clause **, bool &); - bool vivify_clause (Vivifier &, Clause *candidate); - void vivify_analyze (Clause *start, bool &, Clause **, - const Clause *const, int implied, bool &); - bool vivify_shrinkable (const std::vector &sorted, Clause *c); - void vivify_round (Vivifier &, int64_t delta); - bool vivify (); - - // Compacting (shrinking internal variable tables) in 'compact.cpp' - // - bool compacting (); - void compact (); - - // Transitive reduction of binary implication graph in 'transred.cpp' - // - void transred (); - - // We monitor the maximum size and glue of clauses during 'reduce' and - // thus can predict if a redundant extended clause is likely to be kept in - // the next 'reduce' phase. These clauses are target of subsumption and - // vivification checks, in addition to irredundant clauses. Their - // variables are also marked as being 'added'. - // - bool likely_to_be_kept_clause (Clause *c) { - if (!c->redundant) - return true; - if (c->glue <= tier2[false]) - return true; - if (c->glue > lim.keptglue) - return false; - if (c->size > lim.keptsize) - return false; - return true; - } - - // We mark variables in added or shrunken clauses as 'subsume' candidates - // if the clause is likely to be kept in the next 'reduce' phase (see last - // function above). This gives a persistent (across consecutive - // interleaved search and inprocessing phases) set of variables which have - // to be reconsidered in subsumption checks, i.e., only clauses with - // 'subsume' marked variables are checked to be forward subsumed. - // A similar technique is used to reduce the effort in hyper ternary - // resolution to focus on variables in new ternary clauses. - // - void mark_subsume (int lit) { - Flags &f = flags (lit); - if (f.subsume) - return; - LOG ("marking %d as subsuming literal candidate", abs (lit)); - stats.mark.subsume++; - f.subsume = true; - } - void mark_ternary (int lit) { - Flags &f = flags (lit); - if (f.ternary) - return; - LOG ("marking %d as ternary resolution literal candidate", abs (lit)); - stats.mark.ternary++; - f.ternary = true; - } - void mark_factor (int lit) { - Flags &f = flags (lit); - const unsigned bit = bign (lit); - if (f.factor & bit) - return; - LOG ("marking %d as factor literal candidate", lit); - stats.mark.factor++; - f.factor |= bit; - } - void mark_added (int lit, int size, bool redundant); - void mark_added (Clause *); - - bool marked_subsume (int lit) const { return flags (lit).subsume; } - - // If irredundant clauses are removed or literals in clauses are removed, - // then variables in such clauses should be reconsidered to be eliminated - // through bounded variable elimination. In contrast to 'subsume' the - // 'elim' flag is restricted to 'irredundant' clauses only. For blocked - // clause elimination it is better to have a more precise signed version, - // which allows to independently mark positive and negative literals. - // - void mark_elim (int lit) { - Flags &f = flags (lit); - if (f.elim) - return; - LOG ("marking %d as elimination literal candidate", lit); - stats.mark.elim++; - f.elim = true; - } - void mark_block (int lit) { - Flags &f = flags (lit); - const unsigned bit = bign (lit); - if (f.block & bit) - return; - LOG ("marking %d as blocking literal candidate", lit); - stats.mark.block++; - f.block |= bit; - } - void mark_removed (int lit) { - mark_elim (lit); - mark_block (-lit); - } - void mark_removed (Clause *, int except = 0); - - // The following two functions are only used for testing & debugging. - - bool marked_block (int lit) const { - const Flags &f = flags (lit); - const unsigned bit = bign (lit); - return (f.block & bit) != 0; - } - void unmark_block (int lit) { - Flags &f = flags (lit); - const unsigned bit = bign (lit); - f.block &= ~bit; - } - - // During scheduling literals for blocked clause elimination we skip those - // literals which occur negated in a too large clause. - // - void mark_skip (int lit) { - Flags &f = flags (lit); - const unsigned bit = bign (lit); - if (f.skip & bit) - return; - LOG ("marking %d to be skipped as blocking literal", lit); - f.skip |= bit; - } - bool marked_skip (int lit) { - const Flags &f = flags (lit); - const unsigned bit = bign (lit); - return (f.skip & bit) != 0; - } - - // During decompose ignore literals where we already built LRAT chains - // - void mark_decomposed (int lit) { - Flags &f = flags (lit); - const unsigned bit = bign (lit); - CADICAL_assert ((f.marked_signed & bit) == 0); - sign_marked.push_back (lit); - f.marked_signed |= bit; - } - void unmark_decomposed (int lit) { - Flags &f = flags (lit); - const unsigned bit = bign (lit); - f.marked_signed &= ~bit; - } - bool marked_decomposed (int lit) { - const Flags &f = flags (lit); - const unsigned bit = bign (lit); - return (f.marked_signed & bit) != 0; - } - void clear_sign_marked_literals (); - - // Blocked Clause elimination in 'block.cpp'. - // - bool is_blocked_clause (Clause *c, int pivot); - void block_schedule (Blocker &); - size_t block_candidates (Blocker &, int lit); - Clause *block_impossible (Blocker &, int lit); - void block_literal_with_at_least_two_negative_occs (Blocker &, int lit); - void block_literal_with_one_negative_occ (Blocker &, int lit); - void block_pure_literal (Blocker &, int lit); - void block_reschedule_clause (Blocker &, int lit, Clause *); - void block_reschedule (Blocker &, int lit); - void block_literal (Blocker &, int lit); - bool block (); - - // Find gates in 'gates.cpp' for bounded variable substitution. - // - int second_literal_in_binary_clause_lrat (Clause *, int first); - int second_literal_in_binary_clause (Eliminator &, Clause *, int first); - void mark_binary_literals (Eliminator &, int pivot); - void find_and_gate (Eliminator &, int pivot); - void find_equivalence (Eliminator &, int pivot); - - bool get_ternary_clause (Clause *, int &, int &, int &); - bool match_ternary_clause (Clause *, int, int, int); - Clause *find_ternary_clause (int, int, int); - - bool get_clause (Clause *, vector &); - bool is_clause (Clause *, const vector &); - Clause *find_clause (const vector &); - void find_xor_gate (Eliminator &, int pivot); - - void find_if_then_else (Eliminator &, int pivot); - - Clause *find_binary_clause (int, int); - void find_gate_clauses (Eliminator &, int pivot); - void unmark_gate_clauses (Eliminator &); - - // mine definitions for cadical_kitten in 'definition.cpp' - // - void find_definition (Eliminator &, int); - void init_citten (); - void reset_citten (); - void citten_clear_track_log_terminate (); - - // Bounded variable elimination in 'elim.cpp'. - // - bool ineliminating (); - double compute_elim_score (unsigned lit); - void mark_redundant_clauses_with_eliminated_variables_as_garbage (); - void unmark_binary_literals (Eliminator &); - bool resolve_clauses (Eliminator &, Clause *, int pivot, Clause *, bool); - void mark_eliminated_clauses_as_garbage (Eliminator &, int pivot, bool &); - bool elim_resolvents_are_bounded (Eliminator &, int pivot); - void elim_update_removed_lit (Eliminator &, int lit); - void elim_update_removed_clause (Eliminator &, Clause *, int except = 0); - void elim_update_added_clause (Eliminator &, Clause *); - void elim_add_resolvents (Eliminator &, int pivot); - void elim_backward_clause (Eliminator &, Clause *); - void elim_backward_clauses (Eliminator &); - void elim_propagate (Eliminator &, int unit); - void elim_on_the_fly_self_subsumption (Eliminator &, Clause *, int); - void try_to_eliminate_variable (Eliminator &, int pivot, bool &); - void increase_elimination_bound (); - int elim_round (bool &completed, bool &); - void elim (bool update_limits = true); - - int64_t flush_elimfast_occs (int lit); - void elimfast_add_resolvents (Eliminator &, int pivot); - bool elimfast_resolvents_are_bounded (Eliminator &, int pivot); - void try_to_fasteliminate_variable (Eliminator &, int pivot, bool &); - int elimfast_round (bool &completed, bool &); - void elimfast (); - - // sweeping in 'sweep.cpp' - int sweep_solve (); - void sweep_set_cadical_kitten_ticks_limit (Sweeper &sweeper); - bool cadical_kitten_ticks_limit_hit (Sweeper &sweeper, const char *when); - void init_sweeper (Sweeper &sweeper); - void release_sweeper (Sweeper &sweeper); - void clear_sweeper (Sweeper &sweeper); - int sweep_repr (Sweeper &sweeper, int lit); - void add_literal_to_environment (Sweeper &sweeper, unsigned depth, int); - void sweep_clause (Sweeper &sweeper, unsigned depth, Clause *); - void sweep_add_clause (Sweeper &sweeper, unsigned depth); - void add_core (Sweeper &sweeper, unsigned core_idx); - void save_core (Sweeper &sweeper, unsigned core); - void clear_core (Sweeper &sweeper, unsigned core_idx); - void save_add_clear_core (Sweeper &sweeper); - void init_backbone_and_partition (Sweeper &sweeper); - void sweep_empty_clause (Sweeper &sweeper); - void sweep_refine_partition (Sweeper &sweeper); - void sweep_refine_backbone (Sweeper &sweeper); - void sweep_refine (Sweeper &sweeper); - void flip_backbone_literals (struct Sweeper &sweeper); - bool sweep_backbone_candidate (Sweeper &sweeper, int lit); - int64_t add_sweep_binary (sweep_proof_clause, int lit, int other); - bool scheduled_variable (Sweeper &sweeper, int idx); - void schedule_inner (Sweeper &sweeper, int idx); - void schedule_outer (Sweeper &sweeper, int idx); - int next_scheduled (Sweeper &sweeper); - void substitute_connected_clauses (Sweeper &sweeper, int lit, int other, - int64_t id); - void sweep_remove (Sweeper &sweeper, int lit); - void flip_partition_literals (struct Sweeper &sweeper); - const char *sweep_variable (Sweeper &sweeper, int idx); - bool scheduable_variable (Sweeper &sweeper, int idx, size_t *occ_ptr); - unsigned schedule_all_other_not_scheduled_yet (Sweeper &sweeper); - bool sweep_equivalence_candidates (Sweeper &sweeper, int lit, int other); - unsigned reschedule_previously_remaining (Sweeper &sweeper); - unsigned incomplete_variables (); - void mark_incomplete (Sweeper &sweeper); - unsigned schedule_sweeping (Sweeper &sweeper); - void unschedule_sweeping (Sweeper &sweeper, unsigned swept, - unsigned scheduled); - bool sweep (); - void sweep_dense_propagate (Sweeper &sweeper); - void sweep_sparse_mode (); - void sweep_dense_mode_and_watch_irredundant (); - void sweep_substitute_lrat (Clause *c, int64_t id); - void sweep_substitute_new_equivalences (Sweeper &sweeper); - void sweep_update_noccs (Clause *c); - void delete_sweep_binary (const sweep_binary &sb); - bool can_sweep_clause (Clause *c); - bool sweep_flip (int); - int sweep_flip_and_implicant (int); - bool sweep_extract_fixed (Sweeper &sweeper, int lit); - - // factor - void factor_mode (); - void reset_factor_mode (); - double tied_next_factor_score (int); - Quotient *new_quotient (Factoring &, int); - void release_quotients (Factoring &); - size_t first_factor (Factoring &, int); - void clear_nounted (vector &); - void clear_flauses (vector &); - Quotient *best_quotient (Factoring &, size_t *); - int next_factor (Factoring &, unsigned *); - void factorize_next (Factoring &, int, unsigned); - void resize_factoring (Factoring &factoring, int lit); - void flush_unmatched_clauses (Quotient *); - void add_self_subsuming_factor (Quotient *, Quotient *); - bool self_subsuming_factor (Quotient *); - void add_factored_divider (Quotient *, int); - void blocked_clause (Quotient *q, int); - void add_factored_quotient (Quotient *, int not_fresh); - void eagerly_remove_from_occurences (Clause *c); - void delete_unfactored (Quotient *q); - void update_factored (Factoring &factoring, Quotient *q); - bool apply_factoring (Factoring &factoring, Quotient *q); - void update_factor_candidate (Factoring &, int); - void schedule_factorization (Factoring &); - bool run_factorization (int64_t limit); - bool factor (); - int get_new_extension_variable (); - Clause *new_factor_clause (); - - // instantiate - // - void inst_assign (int lit); - bool inst_propagate (); - void collect_instantiation_candidates (Instantiator &); - bool instantiate_candidate (int lit, Clause *); - void instantiate (Instantiator &); - - void new_trail_level (int lit); - - // Hyper ternary resolution. - // - bool ternary_find_binary_clause (int, int); - bool ternary_find_ternary_clause (int, int, int); - Clause *new_hyper_ternary_resolved_clause (bool red); - Clause *new_hyper_ternary_resolved_clause_and_watch (bool red, bool); - bool hyper_ternary_resolve (Clause *, int, Clause *); - void ternary_lit (int pivot, int64_t &steps, int64_t &htrs); - void ternary_idx (int idx, int64_t &steps, int64_t &htrs); - bool ternary_round (int64_t &steps, int64_t &htrs); - bool ternary (); - - // Probing in 'probe.cpp'. - // - bool inprobing (); - void failed_literal (int lit); - void probe_lrat_for_units (int lit); - void probe_assign_unit (int lit); - void probe_assign_decision (int lit); - void probe_assign (int lit, int parent); - void mark_duplicated_binary_clauses_as_garbage (); - int get_parent_reason_literal (int lit); - void set_parent_reason_literal (int lit, int reason); - void clean_probehbr_lrat (); - void init_probehbr_lrat (); - void get_probehbr_lrat (int lit, int uip); - void set_probehbr_lrat (int lit, int uip); - void probe_post_dominator_lrat (vector &, int, int); - void probe_dominator_lrat (int dom, Clause *reason); - int probe_dominator (int a, int b); - int hyper_binary_resolve (Clause *); - void probe_propagate2 (); - bool probe_propagate (); - bool is_binary_clause (Clause *c, int &, int &); - void generate_probes (); - void flush_probes (); - int next_probe (); - bool probe (); - void inprobe (bool update_limits = true); - - // ProbSAT/WalkSAT implementation called initially or from 'rephase'. - // - void walk_save_minimum (Walker &); - Clause *walk_pick_clause (Walker &); - unsigned walk_break_value (int lit); - int walk_pick_lit (Walker &, Clause *); - void walk_flip_lit (Walker &, int lit); - int walk_round (int64_t limit, bool prev); - void walk (); - - // Detect strongly connected components in the binary implication graph - // (BIG) and equivalent literal substitution (ELS) in 'decompose.cpp'. - // - void decompose_conflicting_scc_lrat (DFS *dfs, vector &); - void build_lrat_for_clause (const vector> &dfs_chains, - bool invert = false); - vector decompose_analyze_binary_clauses (DFS *dfs, int from); - void decompose_analyze_binary_chain (DFS *dfs, int); - bool decompose_round (); - void decompose (); - - void reset_limits (); // Reset after 'solve' call. - - // Try flipping a literal while not falsifying a model. - - bool flip (int lit); - bool flippable (int lit); - - // Assumption handling. - // - void assume_analyze_literal (int lit); - void assume_analyze_reason (int lit, Clause *reason); - void assume (int); // New assumption literal. - bool failed (int lit); // Literal failed assumption? - void reset_assumptions (); // Reset after 'solve' call. - void sort_and_reuse_assumptions (); // reorder the assumptions in order to - // reuse parts of the trail - void failing (); // Prepare failed assumptions. - - bool assumed (int lit) { // Marked as assumption. - Flags &f = flags (lit); - const unsigned bit = bign (lit); - return (f.assumed & bit) != 0; - } - - // Add temporary clause as constraint. - // - void constrain (int); // Add literal to constraint. - bool - failed_constraint (); // Was constraint used to proof unsatisfiablity? - void reset_constraint (); // Reset after 'solve' call. - - // Propagate the current set of assumptions and return the - // non-witness assigned literals - int propagate_assumptions (); - void implied (std::vector &entrailed); - - // Forcing decision variables to a certain phase. - // - void phase (int lit); - void unphase (int lit); - - // Globally blocked clause elimination. - // - bool is_autarky_literal (int lit) const; - bool is_conditional_literal (int lit) const; - void mark_as_conditional_literal (int lit); - void unmark_as_conditional_literal (int lit); - // - bool is_in_candidate_clause (int lit) const; - void mark_in_candidate_clause (int lit); - void unmark_in_candidate_clause (int lit); - // - void condition_assign (int lit); - void condition_unassign (int lit); - // - bool conditioning (); - long condition_round (long unassigned_literal_propagation_limit); - void condition (bool update_limits = true); - - // Part on picking the next decision in 'decide.cpp'. - // - bool satisfied (); - int next_decision_variable_on_queue (); - int next_decision_variable_with_best_score (); - int next_decision_variable (); - int decide_phase (int idx, bool target); - int likely_phase (int idx); - bool better_decision (int lit, int other); - int decide (); // 0=decision, 20=failed - - // Internal functions to enable explicit search limits. - // - void limit_terminate (int); - void limit_decisions (int); // Force decision limit. - void limit_conflicts (int); // Force conflict limit. - void limit_preprocessing (int); // Enable 'n' preprocessing rounds. - void limit_local_search (int); // Enable 'n' local search rounds. - - // External versions can access limits by 'name'. - // - static bool is_valid_limit (const char *name); - bool limit (const char *name, int); // 'true' if 'name' valid - - // Set all the CDCL search limits and increments for scheduling - // inprocessing, restarts, clause database reductions, etc. - // - void init_report_limits (); - void init_preprocessing_limits (); - void init_search_limits (); - - // The computed averages are local to the 'stable' and 'unstable' phase. - // Their main use is to be reported in 'report', except for the 'glue' - // averages, which are used to schedule (prohibit actually) restarts - // during 'unstable' phases ('stable' phases use reluctant doubling). - // - void init_averages (); - void swap_averages (); - - int try_to_satisfy_formula_by_saved_phases (); - void produce_failed_assumptions (); - - // Main solve & search functions in 'internal.cpp'. - // - // We have three pre-solving techniques. These consist of preprocessing, - // local search and searching for lucky phases, which in full solving - // mode except for the last are usually optional and then followed by - // the main CDCL search loop with inprocessing. If only preprocessing - // is requested from 'External::simplify' only preprocessing is called - // though. This is all orchestrated by the 'solve' function. - // - int already_solved (); - int restore_clauses (); - bool preprocess_round (int round); - void preprocess_quickly (); - int preprocess (); - int local_search_round (int round); - int local_search (); - int lucky_phases (); - int cdcl_loop_with_inprocessing (); - void reset_solving (); - int solve (bool preprocess_only = false); - void finalize (int); - - // - int lookahead (); - CubesWithStatus generate_cubes (int, int); - int most_occurring_literal (); - int lookahead_probing (); - int lookahead_next_probe (); - void lookahead_flush_probes (); - void lookahead_generate_probes (); - std::vector lookahead_populate_locc (); - int lookahead_locc (const std::vector &); - - bool terminating_asked (); - -#ifndef CADICAL_QUIET - // Built in profiling in 'profile.cpp' (see also 'profile.hpp'). - // - void start_profiling (Profile &p, double); - void stop_profiling (Profile &p, double); - - double update_profiles (); // Returns 'time ()'. - void print_profile (); -#endif - - // Get the value of an internal literal: -1=false, 0=unassigned, 1=true. - // We use a redundant table for both negative and positive literals. This - // allows a branch-less check for the value of literal and is considered - // substantially faster than negating the result if the argument is - // negative. We also avoid taking the absolute value. - // - signed char val (int lit) const { - CADICAL_assert (-max_var <= lit); - CADICAL_assert (lit); - CADICAL_assert (lit <= max_var); - return vals[lit]; - } - - // As suggested by Matt Ginsberg it might be useful to factor-out a common - // setter function for setting and resetting the value of a literal. - // - void set_val (int lit, signed char val) { - CADICAL_assert (-1 <= val); - CADICAL_assert (val <= 1); - CADICAL_assert (-max_var <= lit); - CADICAL_assert (lit); - CADICAL_assert (lit <= max_var); - vals[lit] = val; - vals[-lit] = -val; - } - - // As 'val' but restricted to the root-level value of a literal. - // It is not that time critical and also needs to check the decision level - // of the variable anyhow. - // - int fixed (int lit) { - CADICAL_assert (-max_var <= lit); - CADICAL_assert (lit); - CADICAL_assert (lit <= max_var); - const int idx = vidx (lit); - int res = vals[idx]; - if (res && vtab[idx].level) - res = 0; - if (lit < 0) - res = -res; - return res; - } - - // Map back an internal literal to an external. - // - int externalize (int lit) { - CADICAL_assert (lit != INT_MIN); - const int idx = abs (lit); - CADICAL_assert (idx); - CADICAL_assert (idx <= max_var); - int res = i2e[idx]; - if (lit < 0) - res = -res; - return res; - } - - // Explicit freezing and melting of variables. - // - void freeze (int lit) { - int idx = vidx (lit); - if ((size_t) idx >= frozentab.size ()) { - size_t new_vsize = vsize ? 2 * vsize : 1 + (size_t) max_var; - while (new_vsize <= (size_t) max_var) - new_vsize *= 2; - frozentab.resize (new_vsize); - } - unsigned &ref = frozentab[idx]; - if (ref < UINT_MAX) { - ref++; - LOG ("variable %d frozen %u times", idx, ref); - } else - LOG ("variable %d remains frozen forever", idx); - } - void melt (int lit) { - int idx = vidx (lit); - unsigned &ref = frozentab[idx]; - if (ref < UINT_MAX) { - if (!--ref) { - if (relevanttab[idx]) { - LOG ("variable %d is observed, can not be completely molten", - idx); - ref++; - } else - LOG ("variable %d completely molten", idx); - } else - LOG ("variable %d melted once but remains frozen %u times", lit, - ref); - } else - LOG ("variable %d remains frozen forever", idx); - } - bool frozen (int lit) { - return (size_t) vidx (lit) < frozentab.size () && - frozentab[vidx (lit)] > 0; - } - - // Congruence closure - bool extract_gates (); - - // Parsing functions in 'parse.cpp'. - // - const char *parse_dimacs (FILE *); - const char *parse_dimacs (const char *); - const char *parse_solution (const char *); - - // Enable and disable proof logging and checking. - // - void new_proof_on_demand (); - void force_lrat (); // sets lrat=true - void resize_unit_clauses_idx (); // resizes unit_clauses_idx - void close_trace (bool stats = false); // Stop proof tracing. - void flush_trace (bool stats = false); // Flush proof trace file. - void trace (File *); // Start write proof file. - void check (); // Enable online proof checking. - - void connect_proof_tracer (Tracer *tracer, bool antecedents, - bool finalize_clauses = false); - void connect_proof_tracer (InternalTracer *tracer, bool antecedents, - bool finalize_clauses = false); - void connect_proof_tracer (StatTracer *tracer, bool antecedents, - bool finalize_clauses = false); - void connect_proof_tracer (FileTracer *tracer, bool antecedents, - bool finalize_clauses = false); - bool disconnect_proof_tracer (Tracer *tracer); - bool disconnect_proof_tracer (StatTracer *tracer); - bool disconnect_proof_tracer (FileTracer *tracer); - void conclude_unsat (); - void reset_concluded (); - - // Dump to '' as DIMACS for debugging. - // - void dump (Clause *); - void dump (); - - // Export and traverse all irredundant (non-unit) clauses. - // - bool traverse_clauses (ClauseIterator &); - - // Export and traverse all irredundant (non-unit) clauses. - // - bool traverse_constraint (ClauseIterator &); - - /*----------------------------------------------------------------------*/ - - double solve_time (); // accumulated time spent in 'solve ()' - - double process_time () const; // since solver was initialized - double real_time () const; // since solver was initialized - - double time () { return opts.realtime ? real_time () : process_time (); } - - // Regularly reports what is going on in 'report.cpp'. - // - void report (char type, int verbose_level = 0); - void report_solving (int); - - void print_statistics (); - void print_resource_usage (); - - /*----------------------------------------------------------------------*/ - -#ifndef CADICAL_QUIET - - void print_prefix (); - - // Non-verbose messages and warnings, i.e., always printed unless 'quiet' - // is set, which disables messages at run-time, or even 'CADICAL_QUIET' is defined - // through the configuration option './configure --quiet', which disables - // such messages completely at compile-time. - // - void vmessage (const char *, va_list &); - void message (const char *, ...) CADICAL_ATTRIBUTE_FORMAT (2, 3); - void message (); // empty line - - // Verbose messages with explicit verbose 'level' controlled by - // 'opts.verbose' (verbose level '0' gives the same as 'message'). - // - void vverbose (int level, const char *fmt, va_list &); - void verbose (int level, const char *fmt, ...) - CADICAL_ATTRIBUTE_FORMAT (3, 4); - void verbose (int level); - - // This is for printing section headers in the form - // - // c ---- [ ] --------------------- - // - // nicely aligned (and of course is ignored if 'quiet' is set). - // - void section (const char *title); - - // Print verbose message about phases if 'opts.verbose > 1' (but not if - // 'quiet' is set). Note that setting 'log' or '-l' forces all verbose - // output (and also ignores 'quiet' set to true'). The 'phase' argument - // is used to print a 'phase' prefix for the message as follows: - // - // c [<phase>] ... - // - void phase (const char *phase, const char *, ...) - CADICAL_ATTRIBUTE_FORMAT (3, 4); - - // Same as the last 'phase' above except that the prefix gets a count: - // - // c [<phase>-<count>] ... - // - void phase (const char *phase, int64_t count, const char *, ...) - CADICAL_ATTRIBUTE_FORMAT (4, 5); -#endif - - // Print error messages which are really always printed (even if 'quiet' - // is set). This leads to exit the current process with exit status '1'. - // - // TODO add possibility to use a call back instead of calling exit. - // - void error_message_end (); - void verror (const char *, va_list &); - void error (const char *, ...) CADICAL_ATTRIBUTE_FORMAT (2, 3); - void error_message_start (); - - // Warning messages. - // - void warning (const char *, ...) CADICAL_ATTRIBUTE_FORMAT (2, 3); -}; - -// Fatal internal error which leads to abort. -// -void fatal_message_start (); -void fatal_message_end (); -void fatal (const char *, ...) CADICAL_ATTRIBUTE_FORMAT (1, 2); - -/*------------------------------------------------------------------------*/ - -// Has to be put here, i.e., not into 'score.hpp', since we need the -// definition of 'Internal::score' above (after '#include "score.hpp"'). - -inline bool score_smaller::operator() (unsigned a, unsigned b) { - - // Avoid computing twice 'abs' in 'score ()'. - // - CADICAL_assert (1 <= a); - CADICAL_assert (a <= (unsigned) internal->max_var); - CADICAL_assert (1 <= b); - CADICAL_assert (b <= (unsigned) internal->max_var); - double s = internal->stab[a]; - double t = internal->stab[b]; - - if (s < t) - return true; - if (s > t) - return false; - - return a > b; -} - -/*------------------------------------------------------------------------*/ - -// Implemented here for keeping it all inline (requires Internal::fixed). - -inline int External::fixed (int elit) const { - CADICAL_assert (elit); - CADICAL_assert (elit != INT_MIN); - int eidx = abs (elit); - if (eidx > max_var) - return 0; - int ilit = e2i[eidx]; - if (!ilit) - return 0; - if (elit < 0) - ilit = -ilit; - return internal->fixed (ilit); -} - -/*------------------------------------------------------------------------*/ - -// We want to have termination checks inlined, particularly the first -// function which appears in preprocessor loops. Even though this first -// 'termination_forced' is set asynchronously, this should not lead to a -// data race issue (it also has been declared 'volatile'). - -inline bool Internal::terminated_asynchronously (int factor) { - // First way of asynchronous termination is through 'terminate' which sets - // the 'termination_forced' flag directly. The second way is through a - // call back to a 'terminator' if it is non-zero, which however is costly. - // - if (termination_forced) { - LOG ("termination asynchronously forced"); - return true; - } - - // This is only for testing and debugging asynchronous termination calls. - // In production code this could be removed but then should not be costly - // and keeping it will allow to test correctness of asynchronous - // termination on the production platform too. After this triggers we - // have to set the 'termination_forced' flag, such that subsequent calls - // to this function do not check this again. - // - if (lim.terminate.forced) { - CADICAL_assert (lim.terminate.forced > 0); - if (lim.terminate.forced-- == 1) { - LOG ("internally forcing termination"); - termination_forced = true; - return true; - } - LOG ("decremented internal forced termination limit to %d", - lim.terminate.forced); - } - - // The second way of asynchronous termination is through registering and - // calling an external 'Terminator' object. This is of course more costly - // than just checking a (volatile though) boolean flag, particularly in - // tight loops. To avoid this cost we only call the terminator in - // intervals of 'opts.terminateint', which in addition can be scaled up by - // the argument 'factor'. If the terminator returns 'true' we set the - // 'termination_forced' flag to 'true' in order to remember the - // termination status and to avoid the terminator again. Setting this - // flag leads to the first test above to succeed in subsequent calls. - // - if (external->terminator && !lim.terminate.check--) { - CADICAL_assert (factor > 0); - CADICAL_assert (INT_MAX / factor > opts.terminateint); - lim.terminate.check = factor * opts.terminateint; - if (external->terminator->terminate ()) { - termination_forced = true; // Cache it. - LOG ("connected terminator forces termination"); - return true; - } - } - - return false; -} - -/*------------------------------------------------------------------------*/ - -inline bool Internal::search_limits_hit () { - CADICAL_assert (!preprocessing); - CADICAL_assert (!localsearching); - - if (lim.conflicts >= 0 && stats.conflicts >= lim.conflicts) { - LOG ("conflict limit %" PRId64 " reached", lim.conflicts); - return true; - } - - if (lim.decisions >= 0 && stats.decisions >= lim.decisions) { - LOG ("decision limit %" PRId64 " reached", lim.decisions); - return true; - } - - return false; -} - -/*------------------------------------------------------------------------*/ - -} // namespace CaDiCaL - -ABC_NAMESPACE_CXX_HEADER_END - -#endif diff --git a/src/sat/cadical/inttypes.hpp b/src/sat/cadical/inttypes.hpp deleted file mode 100644 index 2f7cb74a4d..0000000000 --- a/src/sat/cadical/inttypes.hpp +++ /dev/null @@ -1,30 +0,0 @@ -#ifndef _inttypes_h_INCLUDED -#define _inttypes_h_INCLUDED - -#include "global.h" - -// This is an essence a wrapper around '<cinttypes>' respectively -// 'inttypes.h' in order to please the 'MinGW' cross-compiler (we are using -// 'i686-w64-mingw32-gcc') to produce correct 'printf' style formatting for -// 64-bit numbers as this does not work out-of-the-box (which is also very -// annoying). This also produces lots of warnings (through '-Wformat' and -// the corresponding 'attribute' declaration for 'printf' style functions). -// Again 'MinGW' is not fully standard compliant here and we have to cover -// up for that manually. - -// We repeat the code on making this work which is also contained in -// 'cadical.hpp' as we do not want to require users of the library to -// include another header file (like this one) beside 'cadical.hpp'. - -#ifndef PRINTF_FORMAT -#ifdef __MINGW32__ -#define __USE_MINGW_ANSI_STDIO 1 -#define PRINTF_FORMAT __MINGW_PRINTF_FORMAT -#else -#define PRINTF_FORMAT printf -#endif -#endif - -#include <cinttypes> - -#endif diff --git a/src/sat/cadical/ipasir.h b/src/sat/cadical/ipasir.h deleted file mode 100644 index ccb6101308..0000000000 --- a/src/sat/cadical/ipasir.h +++ /dev/null @@ -1,35 +0,0 @@ -#ifndef _ipasir_h_INCLUDED -#define _ipasir_h_INCLUDED - -#include "global.h" - -/*------------------------------------------------------------------------*/ -ABC_NAMESPACE_HEADER_START -/*------------------------------------------------------------------------*/ - -// Here are the declarations for the actual IPASIR functions, which is the -// generic incremental reentrant SAT solver API used for instance in the SAT -// competition. The other 'C' API in 'ccadical.h' is (more) type safe and -// has additional functions only supported by the CaDiCaL library. Please -// also refer to our SAT Race 2015 article in the Journal of AI from 2016. - -const char *ipasir_signature (void); -void *ipasir_init (void); -void ipasir_release (void *solver); -void ipasir_add (void *solver, int lit); -void ipasir_assume (void *solver, int lit); -int ipasir_solve (void *solver); -int ipasir_val (void *solver, int lit); -int ipasir_failed (void *solver, int lit); - -void ipasir_set_terminate (void *solver, void *state, - int (*terminate) (void *state)); - -void ipasir_set_learn (void *solver, void *state, int max_length, - void (*learn) (void *state, int *clause)); - -/*------------------------------------------------------------------------*/ -ABC_NAMESPACE_HEADER_END -/*------------------------------------------------------------------------*/ - -#endif diff --git a/src/sat/cadical/kitten.h b/src/sat/cadical/kitten.h deleted file mode 100644 index c70d47b0d5..0000000000 --- a/src/sat/cadical/kitten.h +++ /dev/null @@ -1,95 +0,0 @@ -#ifndef _cadical_kitten_h_INCLUDED -#define _cadical_kitten_h_INCLUDED - -#include "global.h" - -#include <stdbool.h> -#include <stdint.h> -#include <stdio.h> -#include <stdlib.h> - -ABC_NAMESPACE_HEADER_START - -typedef struct cadical_kitten cadical_kitten; - -cadical_kitten *cadical_kitten_init (void); -void cadical_kitten_clear (cadical_kitten *); -void cadical_kitten_release (cadical_kitten *); - -#ifdef LOGGING -void cadical_kitten_set_logging (cadical_kitten *cadical_kitten); -#endif - -void cadical_kitten_track_antecedents (cadical_kitten *); - -void cadical_kitten_shuffle_clauses (cadical_kitten *); -void cadical_kitten_flip_phases (cadical_kitten *); -void cadical_kitten_randomize_phases (cadical_kitten *); - -void cadical_kitten_assume (cadical_kitten *, unsigned lit); -void cadical_kitten_assume_signed (cadical_kitten *, int lit); - -void cadical_kitten_clause (cadical_kitten *, size_t size, unsigned *); -void citten_clause_with_id (cadical_kitten *, unsigned id, size_t size, int *); -void cadical_kitten_unit (cadical_kitten *, unsigned); -void cadical_kitten_binary (cadical_kitten *, unsigned, unsigned); - -void cadical_kitten_clause_with_id_and_exception (cadical_kitten *, unsigned id, - size_t size, const unsigned *, - unsigned except); - -void citten_clause_with_id_and_exception (cadical_kitten *, unsigned id, - size_t size, const int *, - unsigned except); -void citten_clause_with_id_and_equivalence (cadical_kitten *, unsigned id, - size_t size, const int *, - unsigned, unsigned); -void cadical_kitten_no_ticks_limit (cadical_kitten *); -void cadical_kitten_set_ticks_limit (cadical_kitten *, uint64_t); -uint64_t cadical_kitten_current_ticks (cadical_kitten *); - -void cadical_kitten_no_terminator (cadical_kitten *); -void cadical_kitten_set_terminator (cadical_kitten *, void *, int (*) (void *)); - -int cadical_kitten_solve (cadical_kitten *); -int cadical_kitten_status (cadical_kitten *); - -signed char cadical_kitten_value (cadical_kitten *, unsigned); -signed char cadical_kitten_signed_value (cadical_kitten *, int); // converts second argument -signed char cadical_kitten_fixed (cadical_kitten *, unsigned); -signed char cadical_kitten_fixed_signed (cadical_kitten *, int); // converts -bool cadical_kitten_failed (cadical_kitten *, unsigned); -bool cadical_kitten_flip_literal (cadical_kitten *, unsigned); -bool cadical_kitten_flip_signed_literal (cadical_kitten *, int); - -unsigned cadical_kitten_compute_clausal_core (cadical_kitten *, uint64_t *learned); -void cadical_kitten_shrink_to_clausal_core (cadical_kitten *); - -void cadical_kitten_traverse_core_ids (cadical_kitten *, void *state, - void (*traverse) (void *state, unsigned id)); - -void cadical_kitten_traverse_core_clauses (cadical_kitten *, void *state, - void (*traverse) (void *state, - bool learned, size_t, - const unsigned *)); -void cadical_kitten_traverse_core_clauses_with_id ( - cadical_kitten *, void *state, - void (*traverse) (void *state, unsigned, bool learned, size_t, - const unsigned *)); -void cadical_kitten_trace_core (cadical_kitten *, void *state, - void (*trace) (void *, unsigned, unsigned, bool, - size_t, const unsigned *, size_t, - const unsigned *)); - -int cadical_kitten_compute_prime_implicant (cadical_kitten *cadical_kitten, void *state, - bool (*ignore) (void *, unsigned)); - -void cadical_kitten_add_prime_implicant (cadical_kitten *cadical_kitten, void *state, int side, - void (*add_implicant) (void *, int, size_t, - const unsigned *)); - -int cadical_kitten_flip_and_implicant_for_signed_literal (cadical_kitten *cadical_kitten, int elit); - -ABC_NAMESPACE_HEADER_END - -#endif diff --git a/src/sat/cadical/level.hpp b/src/sat/cadical/level.hpp deleted file mode 100644 index bbfe774ff0..0000000000 --- a/src/sat/cadical/level.hpp +++ /dev/null @@ -1,39 +0,0 @@ -#ifndef _level_hpp_INCLUDED -#define _level_hpp_INCLUDED - -#include "global.h" - -#include <climits> - -ABC_NAMESPACE_CXX_HEADER_START - -namespace CaDiCaL { - -// For each new decision we increase the decision level and push a 'Level' -// on the 'control' stack. The information gathered here is used in -// 'reuse_trail' and for early aborts in clause minimization. - -struct Level { - - int decision; // decision literal of this level - int trail; // trail start of this level - - struct { - int count; // how many variables seen during 'analyze' - int trail; // smallest trail position seen on this level - } seen; - - void reset () { - seen.count = 0; - seen.trail = INT_MAX; - } - - Level (int d, int t) : decision (d), trail (t) { reset (); } - Level () {} -}; - -} // namespace CaDiCaL - -ABC_NAMESPACE_CXX_HEADER_END - -#endif diff --git a/src/sat/cadical/lidruptracer.hpp b/src/sat/cadical/lidruptracer.hpp deleted file mode 100644 index 02cd33fcd2..0000000000 --- a/src/sat/cadical/lidruptracer.hpp +++ /dev/null @@ -1,123 +0,0 @@ -#ifndef _lidruptracer_h_INCLUDED -#define _lidruptracer_h_INCLUDED - -#include "global.h" - -ABC_NAMESPACE_CXX_HEADER_START - -class FileTracer; - -namespace CaDiCaL { - -struct LidrupClause { - LidrupClause *next; // collision chain link for hash table - uint64_t hash; // previously computed full 64-bit hash - int64_t id; // id of clause - std::vector<int64_t> chain; - std::vector<int> literals; -}; - -class LidrupTracer : public FileTracer { - - Internal *internal; - File *file; - bool binary; - bool piping; // The 'file' is a pipe and needs eagerly flushing. - - // hash table for conclusion - // - uint64_t num_clauses; // number of clauses in hash table - uint64_t size_clauses; // size of clause hash table - LidrupClause **clauses; // hash table of clauses - vector<int> imported_clause; - vector<int> assumptions; - vector<int64_t> imported_chain; - vector<int64_t> batch_weaken; - vector<int64_t> batch_delete; - vector<int64_t> batch_restore; - - static const unsigned num_nonces = 4; - - uint64_t nonces[num_nonces]; // random numbers for hashing - uint64_t last_hash; // last computed hash value of clause - int64_t last_id; // id of the last added clause - LidrupClause *last_clause; - uint64_t compute_hash (int64_t); // compute and save hash value of clause - - LidrupClause *new_clause (); - void delete_clause (LidrupClause *); - - static uint64_t reduce_hash (uint64_t hash, uint64_t size); - - void enlarge_clauses (); // enlarge hash table for clauses - void insert (); // insert clause in hash table - bool - find_and_delete (const int64_t); // find clause position in hash table - -#ifndef CADICAL_QUIET - int64_t added, deleted, weakened, restore, original, solved, batched; -#endif - - void flush_if_piping (); - - void put_binary_zero (); - void put_binary_lit (int external_lit); - void put_binary_id (int64_t id, bool = true); - - void lidrup_add_derived_clause (int64_t id, const vector<int> &clause, - const vector<int64_t> &chain); - void lidrup_delete_clause (int64_t id); //, const vector<int> &clause); - void - lidrup_add_restored_clause (int64_t id); //, const vector<int> &clause); - void lidrup_add_original_clause (int64_t id, const vector<int> &clause); - void lidrup_conclude_and_delete (const vector<int64_t> &conclusion); - void lidrup_report_status (int status); - void lidrup_conclude_sat (const vector<int> &model); - void lidrup_conclude_unknown (const vector<int> &trail); - void lidrup_solve_query (); - void lidrup_batch_weaken_restore_and_delete (); - -public: - LidrupTracer (Internal *, File *file, bool); - ~LidrupTracer (); - - // proof section: - void add_derived_clause (int64_t, bool, const vector<int> &, - const vector<int64_t> &) override; - void add_assumption_clause (int64_t, const vector<int> &, - const vector<int64_t> &) override; - void weaken_minus (int64_t, const vector<int> &) override; - void delete_clause (int64_t, bool, const vector<int> &) override; - void add_original_clause (int64_t, bool, const vector<int> &, - bool = false) override; - void report_status (int, int64_t) override; - void conclude_sat (const vector<int> &) override; - void conclude_unsat (ConclusionType, const vector<int64_t> &) override; - void conclude_unknown (const vector<int> &) override; - - void solve_query () override; - void add_assumption (int) override; - void reset_assumptions () override; - - // skip - void begin_proof (int64_t) override {} - void finalize_clause (int64_t, const vector<int> &) override {} - void strengthen (int64_t) override {} - void add_constraint (const vector<int> &) override {} - - // logging and file io - void connect_internal (Internal *i) override; - -#ifndef CADICAL_QUIET - void print_statistics (); -#endif - bool closed () override; - void close (bool) override; - void flush (bool) override; -}; - -} // namespace CaDiCaL - -ABC_NAMESPACE_CXX_HEADER_END - -#endif diff --git a/src/sat/cadical/limit.hpp b/src/sat/cadical/limit.hpp deleted file mode 100644 index 0f6923214f..0000000000 --- a/src/sat/cadical/limit.hpp +++ /dev/null @@ -1,162 +0,0 @@ -#ifndef _limit_hpp_INCLUDED -#define _limit_hpp_INCLUDED - -#include "global.h" - -#include <cstdint> -#include <limits> - -ABC_NAMESPACE_CXX_HEADER_START - -namespace CaDiCaL { - -struct Internal; - -struct Limit { - - bool initialized; - - int64_t conflicts; // conflict limit if non-negative - int64_t decisions; // decision limit if non-negative - int64_t preprocessing; // limit on preprocessing rounds - int64_t localsearch; // limit on local search rounds - - int64_t compact; // conflict limit for next 'compact' - int64_t condition; // conflict limit for next 'condition' - int64_t elim; // conflict limit for next 'elim' - int64_t flush; // conflict limit for next 'flush' - int64_t inprobe; // conflict limit for next 'inprobe' - int64_t reduce; // conflict limit for next 'reduce' - int64_t rephase; // conflict limit for next 'rephase' - int64_t report; // report limit for header - int64_t restart; // conflict limit for next 'restart' - int64_t stabilize; // conflict/ticks limit for next 'stabilize' - - int keptsize; // maximum kept size in 'reduce' - int keptglue; // maximum kept glue in 'reduce' - int64_t recompute_tier; // conflict limit for next tier recomputation - - // How often rephased during (1) or out (0) of stabilization. - // - int64_t rephased[2]; - - // Current elimination bound per eliminated variable. - // - int64_t elimbound; - - struct { - int check; // countdown to next terminator call - int forced; // forced termination for testing - } terminate; - - Limit (); -}; - -struct Delay { - struct { - int64_t interval = 0, limit = 0; - bool bypass = 0; - - bool delay () { - if (bypass) - return true; - if (limit) { - --limit; - return true; - } else { - return false; - } - } - - void bump_delay () { - interval += interval < INT64_MAX; - limit = interval; - } - - void reduce_delay () { - if (!interval) - return; - interval /= 2; - limit = interval; - } - - void bypass_delay () { bypass = 1; } - void unbypass_delay () { bypass = 0; } - } bumpreasons; -}; - -struct Last { - struct { - int64_t propagations; - } transred; - struct { - int64_t ticks; - } sweep, vivify, probe; - struct { - int64_t fixed, subsumephases, marked; - } elim; - struct { - int64_t reductions; - } inprobe; - struct { - int64_t conflicts; - } reduce, rephase; - struct { - int64_t ticks; - int64_t marked; - } ternary; - struct { - int64_t fixed; - } collect; - struct { - int64_t marked, ticks; - } factor; - struct { - int64_t conflicts; - int64_t ticks; - } stabilize; - Last (); -}; - -struct Inc { - int64_t flush; // flushing interval in terms of conflicts - int64_t stabilize; // base ticks limit after first mode switch - int64_t conflicts; // next conflict limit if non-negative - int64_t decisions; // next decision limit if non-negative - int64_t preprocessing; // next preprocessing limit if non-negative - int64_t localsearch; // next local search limit if non-negative - Inc (); -}; - -#define SET_EFFORT_LIMIT(LIMIT, NAME, THRESHHOLD) \ - int64_t LIMIT; \ - do { \ - const int64_t OLD_LIMIT = stats.ticks.NAME; \ - const int64_t TICKS = stats.ticks.search[0] + stats.ticks.search[1]; \ - const int64_t LAST = last.NAME.ticks; \ - int64_t REFERENCE = TICKS - LAST; \ - if (!REFERENCE || !stats.conflicts) { \ - VERBOSE (2, "last %" PRId64 " current %" PRId64 " delta %" PRId64, \ - LAST, TICKS, REFERENCE); \ - REFERENCE = opts.preprocessinit; \ - } \ - const double EFFORT = (double) opts.NAME##effort * 1e-3; \ - const int64_t DELTA = EFFORT * REFERENCE; \ - const int64_t THRESH = opts.NAME##thresh * clauses.size (); \ - if (THRESHHOLD && DELTA < THRESH) { \ - VERBOSE (2, \ - "delaying %s with ticklimit %" PRId64 \ - " and threshhold %" PRId64, \ - #NAME, DELTA, THRESH); \ - return false; \ - } \ - last.NAME.ticks = TICKS; \ - const int64_t NEW_LIMIT = OLD_LIMIT + DELTA; \ - LIMIT = NEW_LIMIT; \ - } while (0) - -} // namespace CaDiCaL - -ABC_NAMESPACE_CXX_HEADER_END - -#endif diff --git a/src/sat/cadical/logging.hpp b/src/sat/cadical/logging.hpp deleted file mode 100644 index edbd60f8c5..0000000000 --- a/src/sat/cadical/logging.hpp +++ /dev/null @@ -1,104 +0,0 @@ -#ifndef _logging_hpp_INCLUDED -#define _logging_hpp_INCLUDED - -#include "global.h" - -/*------------------------------------------------------------------------*/ -#ifdef LOGGING -/*------------------------------------------------------------------------*/ - -#include <cstdint> -#include <vector> - -ABC_NAMESPACE_CXX_HEADER_START - -namespace CaDiCaL { - -// For debugging purposes and to help understanding what the solver is doing -// there is a logging facility which is compiled in by './configure -l'. It -// still has to be enabled at run-time though (again using the '-l' option -// in the stand-alone solver). It produces quite a bit of information. - -using namespace std; - -struct Clause; -struct Gate; -struct Internal; - -struct Logger { - - static void print_log_prefix (Internal *); - - // Simple logging of a C-style format string. - // - static void log (Internal *, const char *fmt, ...) - CADICAL_ATTRIBUTE_FORMAT (2, 3); - - // Prints the format string (with its argument) and then the clause. The - // clause can also be a zero pointer and then is interpreted as a decision - // (current decision level > 0) or unit clause (zero decision level) and - // printed accordingly. - // - static void log (Internal *, const Clause *, const char *fmt, ...) - CADICAL_ATTRIBUTE_FORMAT (3, 4); - - // Same as before, except that this is meant for the global 'clause' stack - // used for new clauses (and not for reasons). - // - static void log (Internal *, const vector<int> &, const char *fmt, ...) - CADICAL_ATTRIBUTE_FORMAT (3, 4); - - // Another variant, to avoid copying (without logging). - // - static void log (Internal *, const vector<int>::const_iterator &begin, - const vector<int>::const_iterator &end, const char *fmt, - ...) CADICAL_ATTRIBUTE_FORMAT (4, 5); - - // used for logging LRAT proof chains - // - static void log (Internal *, const vector<int64_t> &, const char *fmt, - ...) CADICAL_ATTRIBUTE_FORMAT (3, 4); - - static void log (Internal *, const int *, const unsigned, const char *fmt, - ...) CADICAL_ATTRIBUTE_FORMAT (4, 5); - - static void log_empty_line (Internal *); - - static void log (Internal *, const Gate *, const char *fmt, ...) - CADICAL_ATTRIBUTE_FORMAT (3, 4); - - static string loglit (Internal *, int lit); -}; - -} // namespace CaDiCaL - -/*------------------------------------------------------------------------*/ - -// Make sure that 'logging' code is really not included (second case of the -// '#ifdef') if logging code is not included. - -#define LOG(...) \ - do { \ - if (!internal->opts.log) \ - break; \ - Logger::log (internal, __VA_ARGS__); \ - } while (0) - -#define LOGLIT(lit) Logger::loglit (internal, lit).c_str () - -ABC_NAMESPACE_CXX_HEADER_END - -/*------------------------------------------------------------------------*/ -#else // end of 'then' part of 'ifdef LOGGING' -/*------------------------------------------------------------------------*/ - -#define LOG(...) \ - do { \ - } while (0) - -#define LOGLIT(...) - -/*------------------------------------------------------------------------*/ -#endif // end of 'else' part of 'ifdef LOGGING' -/*------------------------------------------------------------------------*/ -#endif diff --git a/src/sat/cadical/lratchecker.hpp b/src/sat/cadical/lratchecker.hpp deleted file mode 100644 index 0e791e9f4b..0000000000 --- a/src/sat/cadical/lratchecker.hpp +++ /dev/null @@ -1,170 +0,0 @@ -#ifndef _lratchecker_hpp_INCLUDED -#define _lratchecker_hpp_INCLUDED - -#include "global.h" - -/*------------------------------------------------------------------------*/ -#include <unordered_map> - -ABC_NAMESPACE_CXX_HEADER_START - -namespace CaDiCaL { - -/*------------------------------------------------------------------------*/ - -// This checker implements an LRUP checker. -// It requires LRAT-style proof chains for each learned clause -// -// Most of the infrastructure is taken from checker, but without the -// propagation - -/*------------------------------------------------------------------------*/ - -struct LratCheckerClause { - LratCheckerClause *next; // collision chain link for hash table - uint64_t hash; // previously computed full 64-bit hash - int64_t id; // id of clause - bool garbage; // for garbage clauses - unsigned size; - bool used; - bool tautological; - int literals[1]; // 'literals' of length 'size' -}; - -/*------------------------------------------------------------------------*/ - -class LratChecker : public StatTracer { - - Internal *internal; - - // Capacity of variable values. - // - int64_t size_vars; - - // The 'watchers' and 'marks' data structures are not that time critical - // and thus we access them by first mapping a literal to 'unsigned'. - // - static unsigned l2u (int lit); - - signed char &checked_lit (int lit); - signed char &mark (int lit); - - vector<signed char> checked_lits; - vector<signed char> marks; // mark bits of literals - unordered_map<int64_t, vector<int>> clauses_to_reconstruct; - vector<int> assumptions; - vector<int> constraint; - bool concluded; - - uint64_t num_clauses; // number of clauses in hash table - uint64_t num_finalized; - uint64_t num_garbage; // number of garbage clauses - uint64_t size_clauses; // size of clause hash table - LratCheckerClause **clauses; // hash table of clauses - LratCheckerClause *garbage; // linked list of garbage clauses - - vector<int> imported_clause; // original clause for reporting - vector<int64_t> assumption_clauses; - - void enlarge_vars (int64_t idx); - void import_literal (int lit); - void import_clause (const vector<int> &); - - static const unsigned num_nonces = 4; - - uint64_t nonces[num_nonces]; // random numbers for hashing - uint64_t last_hash; // last computed hash value of clause - int64_t last_id; // id of the last added/deleted clause - int64_t current_id; // id of the last added clause - uint64_t compute_hash (int64_t); // compute and save hash value of clause - - // Reduce hash value to the actual size. - // - static uint64_t reduce_hash (uint64_t hash, uint64_t size); - - void enlarge_clauses (); // enlarge hash table for clauses - void insert (); // insert clause in hash table - LratCheckerClause ** - find (const int64_t); // find clause position in hash table - - void add_clause (const char *type); - - void collect_garbage_clauses (); - - LratCheckerClause *new_clause (); - void delete_clause (LratCheckerClause *); - - bool check (vector<int64_t>); // check RUP - bool check_resolution (vector<int64_t>); // check resolution - bool check_blocked (vector<int64_t>); // check ER - - struct { - - int64_t added; // number of added clauses - int64_t original; // number of added original clauses - int64_t derived; // number of added derived clauses - - int64_t deleted; // number of deleted clauses - int64_t finalized; // number of finalized clauses - - int64_t insertions; // number of clauses added to hash table - int64_t collisions; // number of hash collisions in 'find' - int64_t searches; // number of searched clauses in 'find' - - int64_t checks; // number of implication checks - - int64_t collections; // garbage collections - - } stats; - -public: - LratChecker (Internal *); - virtual ~LratChecker (); - - void connect_internal (Internal *i) override; - void begin_proof (int64_t) override; - - void add_original_clause (int64_t, bool, const vector<int> &, - bool restore) override; - void restore_clause (int64_t, const vector<int> &); - - // check the proof chain for the new clause and add it to the checker - void add_derived_clause (int64_t, bool, const vector<int> &, - const vector<int64_t> &) override; - - // check if the clause is present and delete it from the checker - void delete_clause (int64_t, bool, const vector<int> &) override; - // check if the clause is present and delete it from the checker - void weaken_minus (int64_t, const vector<int> &) override; - - // check if the clause is present and delete it from the checker - void finalize_clause (int64_t, const vector<int> &) override; - - // check the proof chain of the assumption clause and delete it - // immediately also check that they contain only assumptions and - // constraints - void add_assumption_clause (int64_t, const vector<int> &, - const vector<int64_t> &) override; - - // mark lit as assumption - void add_assumption (int) override; - - // mark lits as constraint - void add_constraint (const vector<int> &) override; - - void reset_assumptions () override; - - // check if all clauses have been deleted - void report_status (int, int64_t) override; - - void conclude_unsat (ConclusionType, const vector<int64_t> &) override; - - void print_stats () override; - void dump (); // for debugging purposes only -}; - -} // namespace CaDiCaL - -ABC_NAMESPACE_CXX_HEADER_END - -#endif diff --git a/src/sat/cadical/lrattracer.hpp b/src/sat/cadical/lrattracer.hpp deleted file mode 100644 index 9d8e92b6a8..0000000000 --- a/src/sat/cadical/lrattracer.hpp +++ /dev/null @@ -1,63 +0,0 @@ -#ifndef _lrattracer_h_INCLUDED -#define _lrattracer_h_INCLUDED - -#include "global.h" - -ABC_NAMESPACE_CXX_HEADER_START - -namespace CaDiCaL { - -class LratTracer : public FileTracer { - - Internal *internal; - File *file; - bool binary; - -#ifndef CADICAL_QUIET - int64_t added, deleted; -#endif - int64_t latest_id; - vector<int64_t> delete_ids; - - void put_binary_zero (); - void put_binary_lit (int external_lit); - void put_binary_id (int64_t id); - - // support LRAT - void lrat_add_clause (int64_t, const vector<int> &, - const vector<int64_t> &); - void lrat_delete_clause (int64_t); - -public: - // own and delete 'file' - LratTracer (Internal *, File *file, bool binary); - ~LratTracer (); - - void connect_internal (Internal *i) override; - void begin_proof (int64_t) override; - - void add_original_clause (int64_t, bool, const vector<int> &, - bool = false) override {} // skip - - void add_derived_clause (int64_t, bool, const vector<int> &, - const vector<int64_t> &) override; - - void delete_clause (int64_t, bool, const vector<int> &) override; - - void finalize_clause (int64_t, const vector<int> &) override {} // skip - - void report_status (int, int64_t) override {} // skip - -#ifndef CADICAL_QUIET - void print_statistics (); -#endif - bool closed () override; - void close (bool) override; - void flush (bool) override; -}; - -} // namespace CaDiCaL - -ABC_NAMESPACE_CXX_HEADER_END - -#endif diff --git a/src/sat/cadical/message.hpp b/src/sat/cadical/message.hpp deleted file mode 100644 index e0209fed89..0000000000 --- a/src/sat/cadical/message.hpp +++ /dev/null @@ -1,71 +0,0 @@ -#ifndef _message_h_INCLUDED -#define _message_h_INCLUDED - -#include "global.h" - -ABC_NAMESPACE_CXX_HEADER_START - -/*------------------------------------------------------------------------*/ - -// Macros for compact message code. - -#ifndef CADICAL_QUIET - -#define LINE() \ - do { \ - if (internal) \ - internal->message (); \ - } while (0) - -#define MSG(...) \ - do { \ - if (internal) \ - internal->message (__VA_ARGS__); \ - } while (0) - -#define PHASE(...) \ - do { \ - if (internal) \ - internal->phase (__VA_ARGS__); \ - } while (0) - -#define SECTION(...) \ - do { \ - if (internal) \ - internal->section (__VA_ARGS__); \ - } while (0) - -#define VERBOSE(...) \ - do { \ - if (internal) \ - internal->verbose (__VA_ARGS__); \ - } while (0) - -#else - -#define LINE() \ - do { \ - } while (0) -#define MSG(...) \ - do { \ - } while (0) -#define PHASE(...) \ - do { \ - } while (0) -#define SECTION(...) \ - do { \ - } while (0) -#define VERBOSE(...) \ - do { \ - } while (0) - -#endif - -#define FATAL fatal -#define WARNING(...) internal->warning (__VA_ARGS__) - -/*------------------------------------------------------------------------*/ - -ABC_NAMESPACE_CXX_HEADER_END - -#endif // ifndef _message_h_INCLUDED diff --git a/src/sat/cadical/module.make b/src/sat/cadical/module.make deleted file mode 100644 index 74f5d23cb0..0000000000 --- a/src/sat/cadical/module.make +++ /dev/null @@ -1,91 +0,0 @@ -SRC +=src/sat/cadical/cadicalSolver.c \ -src/sat/cadical/cadicalTest.c \ -src/sat/cadical/cadical_analyze.cpp \ -src/sat/cadical/cadical_arena.cpp \ -src/sat/cadical/cadical_assume.cpp \ -src/sat/cadical/cadical_averages.cpp \ -src/sat/cadical/cadical_backtrack.cpp \ -src/sat/cadical/cadical_backward.cpp \ -src/sat/cadical/cadical_bins.cpp \ -src/sat/cadical/cadical_block.cpp \ -src/sat/cadical/cadical_ccadical.cpp \ -src/sat/cadical/cadical_checker.cpp \ -src/sat/cadical/cadical_clause.cpp \ -src/sat/cadical/cadical_collect.cpp \ -src/sat/cadical/cadical_compact.cpp \ -src/sat/cadical/cadical_condition.cpp \ -src/sat/cadical/cadical_config.cpp \ -src/sat/cadical/cadical_congruence.cpp \ -src/sat/cadical/cadical_constrain.cpp \ -src/sat/cadical/cadical_contract.cpp \ -src/sat/cadical/cadical_cover.cpp \ -src/sat/cadical/cadical_decide.cpp \ -src/sat/cadical/cadical_decompose.cpp \ -src/sat/cadical/cadical_deduplicate.cpp \ -src/sat/cadical/cadical_definition.cpp \ -src/sat/cadical/cadical_drattracer.cpp \ -src/sat/cadical/cadical_elim.cpp \ -src/sat/cadical/cadical_elimfast.cpp \ -src/sat/cadical/cadical_ema.cpp \ -src/sat/cadical/cadical_extend.cpp \ -src/sat/cadical/cadical_external.cpp \ -src/sat/cadical/cadical_external_propagate.cpp \ -src/sat/cadical/cadical_factor.cpp \ -src/sat/cadical/cadical_file.cpp \ -src/sat/cadical/cadical_flags.cpp \ -src/sat/cadical/cadical_flip.cpp \ -src/sat/cadical/cadical_format.cpp \ -src/sat/cadical/cadical_frattracer.cpp \ -src/sat/cadical/cadical_gates.cpp \ -src/sat/cadical/cadical_idruptracer.cpp \ -src/sat/cadical/cadical_instantiate.cpp \ -src/sat/cadical/cadical_internal.cpp \ -src/sat/cadical/cadical_ipasir.cpp \ -src/sat/cadical/cadical_lidruptracer.cpp \ -src/sat/cadical/cadical_limit.cpp \ -src/sat/cadical/cadical_logging.cpp \ -src/sat/cadical/cadical_lookahead.cpp \ -src/sat/cadical/cadical_lratchecker.cpp \ -src/sat/cadical/cadical_lrattracer.cpp \ -src/sat/cadical/cadical_lucky.cpp \ -src/sat/cadical/cadical_message.cpp \ -src/sat/cadical/cadical_minimize.cpp \ -src/sat/cadical/cadical_occs.cpp \ -src/sat/cadical/cadical_options.cpp \ -src/sat/cadical/cadical_parse.cpp \ -src/sat/cadical/cadical_phases.cpp \ -src/sat/cadical/cadical_probe.cpp \ -src/sat/cadical/cadical_profile.cpp \ -src/sat/cadical/cadical_proof.cpp \ -src/sat/cadical/cadical_propagate.cpp \ -src/sat/cadical/cadical_queue.cpp \ -src/sat/cadical/cadical_random.cpp \ -src/sat/cadical/cadical_reap.cpp \ -src/sat/cadical/cadical_reduce.cpp \ -src/sat/cadical/cadical_rephase.cpp \ -src/sat/cadical/cadical_report.cpp \ -src/sat/cadical/cadical_resources.cpp \ -src/sat/cadical/cadical_restart.cpp \ -src/sat/cadical/cadical_restore.cpp \ -src/sat/cadical/cadical_score.cpp \ -src/sat/cadical/cadical_shrink.cpp \ -src/sat/cadical/cadical_signal.cpp \ -src/sat/cadical/cadical_solution.cpp \ -src/sat/cadical/cadical_solver.cpp \ -src/sat/cadical/cadical_stable.cpp \ -src/sat/cadical/cadical_stats.cpp \ -src/sat/cadical/cadical_subsume.cpp \ -src/sat/cadical/cadical_sweep.cpp \ -src/sat/cadical/cadical_terminal.cpp \ -src/sat/cadical/cadical_ternary.cpp \ -src/sat/cadical/cadical_tier.cpp \ -src/sat/cadical/cadical_transred.cpp \ -src/sat/cadical/cadical_unstable.cpp \ -src/sat/cadical/cadical_util.cpp \ -src/sat/cadical/cadical_var.cpp \ -src/sat/cadical/cadical_veripbtracer.cpp \ -src/sat/cadical/cadical_version.cpp \ -src/sat/cadical/cadical_vivify.cpp \ -src/sat/cadical/cadical_walk.cpp \ -src/sat/cadical/cadical_watch.cpp \ -src/sat/cadical/cadical_kitten.c diff --git a/src/sat/cadical/occs.hpp b/src/sat/cadical/occs.hpp deleted file mode 100644 index 28a9246bcf..0000000000 --- a/src/sat/cadical/occs.hpp +++ /dev/null @@ -1,42 +0,0 @@ -#ifndef _occs_h_INCLUDED -#define _occs_h_INCLUDED - -#include "global.h" - -#include <vector> - -ABC_NAMESPACE_CXX_HEADER_START - -namespace CaDiCaL { - -// Full occurrence lists used in a one-watch scheme for all clauses in -// subsumption checking and for irredundant clauses in variable elimination. - -struct Clause; -using namespace std; - -typedef vector<Clause *> Occs; - -inline void shrink_occs (Occs &os) { shrink_vector (os); } -inline void erase_occs (Occs &os) { erase_vector (os); } - -inline void remove_occs (Occs &os, Clause *c) { - const auto end = os.end (); - auto i = os.begin (); - for (auto j = i; j != end; j++) { - const Clause *d = *i++ = *j; - if (c == d) - i--; - } - CADICAL_assert (i + 1 == end); - os.resize (i - os.begin ()); -} - -typedef Occs::iterator occs_iterator; -typedef Occs::const_iterator const_occs_iterator; - -} // namespace CaDiCaL - -ABC_NAMESPACE_CXX_HEADER_END - -#endif diff --git a/src/sat/cadical/options.hpp b/src/sat/cadical/options.hpp deleted file mode 100644 index 1527da2e1a..0000000000 --- a/src/sat/cadical/options.hpp +++ /dev/null @@ -1,422 +0,0 @@ -#ifndef _options_hpp_INCLUDED -#define _options_hpp_INCLUDED - -#include "global.h" - -ABC_NAMESPACE_CXX_HEADER_START - -/*------------------------------------------------------------------------*/ - -// In order to add a new option, simply add a new line below. Make sure that -// options are sorted correctly (with '!}sort -k 2' in 'vi'). Otherwise -// initializing the options will trigger an internal error. For the model -// based tester 'mobical' the policy is that options which become redundant -// because another one is disabled (set to zero) should have the name of the -// latter as prefix. The 'O' column determines the options which are -// target to 'optimize' them ('-O[1-3]'). A zero value in the 'O' column -// means that this option is not optimized. A value of '1' results in -// optimizing its value exponentially with exponent base '2', and a value -// of '2' uses base '10'. The 'P' column determines simplification -// options (disabled with '--plain') and 'R' which values can be reset. - -// clang-format off - -#define OPTIONS \ -\ -/* NAME DEFAULT, LO, HI,O,P,R, USAGE */ \ -\ -OPTION( arena, 1, 0, 1,0,0,1, "allocate clauses in arena") \ -OPTION( arenacompact, 1, 0, 1,0,0,1, "keep clauses compact") \ -OPTION( arenasort, 1, 0, 1,0,0,1, "sort clauses in arena") \ -OPTION( arenatype, 3, 1, 3,0,0,1, "1=clause, 2=var, 3=queue") \ -OPTION( binary, 1, 0, 1,0,0,1, "use binary proof format") \ -OPTION( block, 0, 0, 1,0,1,1, "blocked clause elimination") \ -OPTION( blockmaxclslim, 1e5, 1,2e9,2,0,1, "maximum clause size") \ -OPTION( blockminclslim, 2, 2,2e9,0,0,1, "minimum clause size") \ -OPTION( blockocclim, 1e2, 1,2e9,2,0,1, "occurrence limit") \ -OPTION( bump, 1, 0, 1,0,0,1, "bump variables") \ -OPTION( bumpreason, 1, 0, 1,0,0,1, "bump reason literals too") \ -OPTION( bumpreasondepth, 1, 1, 3,0,0,1, "bump reason depth") \ -OPTION( bumpreasonlimit, 10, 1,2e9,0,0,1, "bump reason limit") \ -OPTION( bumpreasonrate, 100, 1,2e9,0,0,1, "bump reason decision rate") \ -OPTION( check, 0, 0, 1,0,0,0, "enable internal checking") \ -OPTION( checkassumptions, 1, 0, 1,0,0,0, "check assumptions satisfied") \ -OPTION( checkconstraint, 1, 0, 1,0,0,0, "check constraint satisfied") \ -OPTION( checkfailed, 1, 0, 1,0,0,0, "check failed literals form core") \ -OPTION( checkfrozen, 0, 0, 1,0,0,0, "check all frozen semantics") \ -OPTION( checkproof, 3, 0, 3,0,0,0, "1=drat, 2=lrat, 3=both") \ -OPTION( checkwitness, 1, 0, 1,0,0,0, "check witness internally") \ -OPTION( chrono, 1, 0, 2,0,0,1, "chronological backtracking") \ -OPTION( chronoalways, 0, 0, 1,0,0,1, "force always chronological") \ -OPTION( chronolevelim, 1e2, 0,2e9,0,0,1, "chronological level limit") \ -OPTION( chronoreusetrail, 1, 0, 1,0,0,1, "reuse trail chronologically") \ -OPTION( compact, 1, 0, 1,0,1,1, "compact internal variables") \ -OPTION( compactint, 2e3, 1,2e9,0,0,1, "compacting interval") \ -OPTION( compactlim, 1e2, 0,1e3,0,0,1, "inactive limit per mille") \ -OPTION( compactmin, 1e2, 1,2e9,0,0,1, "minimum inactive limit") \ -OPTION( condition, 0, 0, 1,0,1,1, "globally blocked clause elim") \ -OPTION( conditioneffort, 100, 1,1e5,0,0,1, "relative efficiency per mille") \ -OPTION( conditionint, 1e4, 1,2e9,0,0,1, "initial conflict interval") \ -OPTION( conditionmaxeff, 1e7, 0,2e9,1,0,1, "maximum condition efficiency") \ -OPTION( conditionmaxrat, 100, 1,2e9,1,0,1, "maximum clause variable ratio") \ -OPTION( conditionmineff, 0, 0,2e9,1,0,1, "minimum condition efficiency") \ -OPTION( congruence, 1, 0, 1,0,0,1, "congruence closure") \ -OPTION( congruenceand, 1, 0, 1,0,0,1, "extract AND gates") \ -OPTION( congruenceandarity,1e6,2,5e7,0,0,1, "AND gate arity limit") \ -OPTION( congruencebinaries,1, 0, 1,0,0,1, "extract binary and strengthen ternary clauses") \ -OPTION( congruenceite, 1, 0, 1,0,0,1, "extract ITE gates") \ -OPTION( congruencexor, 1, 0, 1,0,0,1, "extract XOR gates") \ -OPTION( congruencexorarity,4, 2, 31,0,0,1, "XOR gate arity limit") \ -OPTION( congruencexorcounts,1, 1,5e6,0,0,1, "XOR gate round") \ -OPTION( cover, 0, 0, 1,0,1,1, "covered clause elimination") \ -OPTION( covereffort, 4, 1,1e5,1,0,1, "relative efficiency per mille") \ -OPTION( covermaxclslim, 1e5, 1,2e9,2,0,1, "maximum clause size") \ -OPTION( covermaxeff, 1e8, 0,2e9,1,0,1, "maximum cover efficiency") \ -OPTION( coverminclslim, 2, 2,2e9,0,0,1, "minimum clause size") \ -OPTION( covermineff, 0, 0,2e9,1,0,1, "minimum cover efficiency") \ -OPTION( decompose, 1, 0, 1,0,1,1, "decompose BIG in SCCs and ELS") \ -OPTION( decomposerounds, 2, 1, 16,1,0,1, "number of decompose rounds") \ -OPTION( deduplicate, 1, 0, 1,0,1,1, "remove duplicated binaries") \ -OPTION( eagersubsume, 1, 0, 1,0,1,1, "subsume recently learned") \ -OPTION( eagersubsumelim, 20, 1,1e3,0,0,1, "limit on subsumed candidates") \ -OPTION( elim, 1, 0, 1,0,1,1, "bounded variable elimination") \ -OPTION( elimands, 1, 0, 1,0,0,1, "find AND gates") \ -OPTION( elimbackward, 1, 0, 1,0,0,1, "eager backward subsumption") \ -OPTION( elimboundmax, 16, -1,2e6,1,0,1, "maximum elimination bound") \ -OPTION( elimboundmin, 0, -1,2e6,0,0,1, "minimum elimination bound") \ -OPTION( elimclslim, 1e2, 2,2e9,2,0,1, "resolvent size limit") \ -OPTION( elimdef, 0, 0, 1,0,0,1, "mine definitions with cadical_kitten") \ -OPTION( elimdefcores, 1, 1,100,0,0,1, "number of unsat cores") \ -OPTION( elimdefticks, 2e5, 0,2e9,1,0,1, "cadical_kitten ticks limit") \ -OPTION( elimeffort, 1e3, 1,1e5,1,0,1, "relative efficiency per mille") \ -OPTION( elimequivs, 1, 0, 1,0,0,1, "find equivalence gates") \ -OPTION( elimint, 2e3, 1,2e9,0,0,1, "elimination interval") \ -OPTION( elimites, 1, 0, 1,0,0,1, "find if-then-else gates") \ -OPTION( elimlimited, 1, 0, 1,0,0,1, "limit resolutions") \ -OPTION( elimmaxeff, 2e9, 0,2e9,1,0,1, "maximum elimination efficiency") \ -OPTION( elimmineff, 1e7, 0,2e9,1,0,1, "minimum elimination efficiency") \ -OPTION( elimocclim, 1e2, 0,2e9,2,0,1, "occurrence limit") \ -OPTION( elimprod, 1, 0,1e4,0,0,1, "elim score product weight") \ -OPTION( elimrounds, 2, 1,512,1,0,1, "usual number of rounds") \ -OPTION( elimsubst, 1, 0, 1,0,0,1, "elimination by substitution") \ -OPTION( elimsum, 1, 0,1e4,0,0,1, "elimination score sum weight") \ -OPTION( elimxorlim, 5, 2, 27,1,0,1, "maximum XOR size") \ -OPTION( elimxors, 1, 0, 1,0,0,1, "find XOR gates") \ -OPTION( emadecisions, 1e5, 1,2e9,0,0,1, "window decision rate") \ -OPTION( emagluefast, 33, 1,2e9,0,0,1, "window fast glue") \ -OPTION( emaglueslow, 1e5, 1,2e9,0,0,1, "window slow glue") \ -OPTION( emajump, 1e5, 1,2e9,0,0,1, "window back-jump level") \ -OPTION( emalevel, 1e5, 1,2e9,0,0,1, "window back-track level") \ -OPTION( emasize, 1e5, 1,2e9,0,0,1, "window learned clause size") \ -OPTION( ematrailfast, 1e2, 1,2e9,0,0,1, "window fast trail") \ -OPTION( ematrailslow, 1e5, 1,2e9,0,0,1, "window slow trail") \ -OPTION( exteagerreasons, 1, 0, 1,0,0,1, "eagerly ask for all reasons (0: only when needed)") \ -OPTION( exteagerrecalc, 1, 0, 1,0,0,1, "after eagerly asking for reasons recalculate all levels (0: trust the external tool)") \ -OPTION( externallrat, 0, 0, 1,0,0,1, "external lrat") \ -OPTION( factor, 1, 0, 1,0,1,1, "bounded variable addition") \ -OPTION( factorcandrounds, 2, 0,2e9,0,0,1, "candidates reduction rounds") \ -OPTION( factoreffort, 50, 0,1e6,0,0,1, "relative effort per mille") \ -OPTION( factoriniticks, 300, 1,1e6,0,0,1, "initial effort in millions") \ -OPTION( factorsize, 5, 2,2e9,0,0,1, "clause size limit") \ -OPTION( factorthresh, 7, 0,100,1,0,1, "delay if ticks smaller thresh*clauses") \ -OPTION( fastelim, 1, 0, 1,0,1,1, "fast BVE during preprocessing") \ -OPTION( fastelimbound, 8, 1,1e3,1,0,1, "fast BVE bound during preprocessing") \ -OPTION( fastelimclslim, 1e2, 2,2e9,2,0,1, "fast BVE resolvent size limit") \ -OPTION( fastelimocclim, 100, 1,2e9,2,0,1, "fast BVE occurence limit during preprocessing") \ -OPTION( fastelimrounds, 4, 1,512,1,0,1, "number of fastelim rounds") \ -OPTION( flush, 0, 0, 1,0,1,1, "flush redundant clauses") \ -OPTION( flushfactor, 3, 1,1e3,0,0,1, "interval increase") \ -OPTION( flushint, 1e5, 1,2e9,0,0,1, "initial limit") \ -OPTION( forcephase, 0, 0, 1,0,0,1, "always use initial phase") \ -OPTION( frat, 0, 0, 2,0,0,1, "1=frat(lrat), 2=frat(drat)") \ -OPTION( idrup, 0, 0, 1,0,0,1, "incremental proof format") \ -OPTION( ilb, 0, 0, 1,0,0,1, "ILB (incremental lazy backtrack)") \ -OPTION( ilbassumptions, 0, 0, 1,0,0,1, "trail reuse for assumptions (ILB-like)") \ -OPTION( inprobeint, 100, 1,2e9,0,0,1, "inprobing interval" ) \ -OPTION( inprobing, 1, 0, 1,0,1,1, "enable probe inprocessing") \ -OPTION( inprocessing, 1, 0, 1,0,1,1, "enable general inprocessing") \ -OPTION( instantiate, 0, 0, 1,0,1,1, "variable instantiation") \ -OPTION( instantiateclslim, 3, 2,2e9,0,0,1, "minimum clause size") \ -OPTION( instantiateocclim, 1, 1,2e9,2,0,1, "maximum occurrence limit") \ -OPTION( instantiateonce, 1, 0, 1,0,0,1, "instantiate each clause once") \ -OPTION( lidrup, 0, 0, 1,0,0,1, "linear incremental proof format") \ -LOGOPT( log, 0, 0, 1,0,0,0, "enable logging") \ -LOGOPT( logsort, 0, 0, 1,0,0,0, "sort logged clauses") \ -OPTION( lrat, 0, 0, 1,0,0,1, "use LRAT proof format") \ -OPTION( lucky, 1, 0, 1,0,0,1, "search for lucky phases") \ -OPTION( minimize, 1, 0, 1,0,0,1, "minimize learned clauses") \ -OPTION( minimizedepth, 1e3, 0,1e3,0,0,1, "minimization depth") \ -OPTION( minimizeticks, 1, 0, 1,0,0,1, "increment ticks in minimization") \ -OPTION( otfs, 1, 0, 1,0,0,1, "on-the-fly self subsumption") \ -OPTION( phase, 1, 0, 1,0,0,1, "initial phase") \ -OPTION( preprocessinit, 2e6, 0,2e9,2,0,1, "initial preprocessing base limit" ) \ -OPTION( preprocesslight, 1, 0, 1,0,1,1, "lightweight preprocessing" ) \ -OPTION( probe, 1, 0, 1,0,1,1, "failed literal probing" ) \ -OPTION( probeeffort, 8, 1,1e5,1,0,1, "relative efficiency per mille") \ -OPTION( probehbr, 1, 0, 1,0,0,1, "learn hyper binary clauses") \ -OPTION( probethresh, 0, 0,100,1,0,1, "delay if ticks smaller thresh*clauses") \ -OPTION( profile, 2, 0, 4,0,0,0, "profiling level") \ -QUTOPT( quiet, 0, 0, 1,0,0,0, "disable all messages") \ -OPTION( radixsortlim, 32, 0,2e9,0,0,1, "radix sort limit") \ -OPTION( realtime, 0, 0, 1,0,0,0, "real instead of process time") \ -OPTION( recomputetier, 1, 0, 1,0,0,1, "recompute tiers") \ -OPTION( reduce, 1, 0, 1,0,0,1, "reduce useless clauses") \ -OPTION( reduceinit, 300, 1,1e6,0,0,1, "initial interval") \ -OPTION( reduceint, 25, 2,1e6,0,0,1, "reduce interval") \ -OPTION( reduceopt, 1, 0, 2,0,0,1, "0=prct,1=sqrt,2=max") \ -OPTION( reducetarget, 75, 10,1e2,0,0,1, "reduce fraction in percent") \ -OPTION( reducetier1glue, 2, 1,2e9,0,0,1, "glue of kept learned clauses") \ -OPTION( reducetier2glue, 6, 1,2e9,0,0,1, "glue of tier two clauses") \ -OPTION( reluctant, 1024, 0,2e9,0,0,1, "reluctant doubling period") \ -OPTION( reluctantmax,1048576, 0,2e9,0,0,1, "reluctant doubling period") \ -OPTION( rephase, 1, 0, 1,0,0,1, "enable resetting phase") \ -OPTION( rephaseint, 1e3, 1,2e9,0,0,1, "rephase interval") \ -OPTION( report,reportdefault, 0, 1,0,0,1, "enable reporting") \ -OPTION( reportall, 0, 0, 1,0,0,1, "report even if not successful") \ -OPTION( reportsolve, 0, 0, 1,0,0,1, "use solving not process time") \ -OPTION( restart, 1, 0, 1,0,0,1, "enable restarts") \ -OPTION( restartint, 2, 1,2e9,0,0,1, "restart interval") \ -OPTION( restartmargin, 10, 0,1e2,0,0,1, "slow fast margin in percent") \ -OPTION( restartreusetrail, 1, 0, 1,0,0,1, "enable trail reuse") \ -OPTION( restoreall, 0, 0, 2,0,0,1, "restore all clauses (2=really)") \ -OPTION( restoreflush, 0, 0, 1,0,0,1, "remove satisfied clauses") \ -OPTION( reverse, 0, 0, 1,0,0,1, "reverse variable ordering") \ -OPTION( score, 1, 0, 1,0,0,1, "use EVSIDS scores") \ -OPTION( scorefactor, 950,500,1e3,0,0,1, "score factor per mille") \ -OPTION( seed, 0, 0,2e9,0,0,1, "random seed") \ -OPTION( shrink, 3, 0, 3,0,0,1, "shrink conflict clause (1=only with binary, 2=minimize when pulling, 3=full)") \ -OPTION( shrinkreap, 1, 0, 1,0,0,1, "use a reap for shrinking") \ -OPTION( shuffle, 0, 0, 1,0,0,1, "shuffle variables") \ -OPTION( shufflequeue, 1, 0, 1,0,0,1, "shuffle variable queue") \ -OPTION( shufflerandom, 0, 0, 1,0,0,1, "not reverse but random") \ -OPTION( shufflescores, 1, 0, 1,0,0,1, "shuffle variable scores") \ -OPTION( stabilize, 1, 0, 1,0,0,1, "enable stabilizing phases") \ -OPTION( stabilizeinit, 1e3, 1,2e9,0,0,1, "stabilizing interval") \ -OPTION( stabilizeonly, 0, 0, 1,0,0,1, "only stabilizing phases") \ -OPTION( stats, 0, 0, 1,0,0,1, "print all statistics at the end of the run") \ -OPTION( subsume, 1, 0, 1,0,1,1, "enable clause subsumption") \ -OPTION( subsumebinlim, 1e4, 0,2e9,1,0,1, "watch list length limit") \ -OPTION( subsumeclslim, 1e2, 0,2e9,2,0,1, "clause length limit") \ -OPTION( subsumeeffort, 1e3, 1,1e5,1,0,1, "relative efficiency per mille") \ -OPTION( subsumelimited, 1, 0, 1,0,0,1, "limit subsumption checks") \ -OPTION( subsumemaxeff, 1e8, 0,2e9,1,0,1, "maximum subsuming efficiency") \ -OPTION( subsumemineff, 0, 0,2e9,1,0,1, "minimum subsuming efficiency") \ -OPTION( subsumeocclim, 1e2, 0,2e9,1,0,1, "watch list length limit") \ -OPTION( subsumestr, 1, 0, 1,0,0,1, "subsume strenghten") \ -OPTION( sweep, 1, 0, 1,0,1,1, "enable SAT sweeping") \ -OPTION( sweepclauses, 1024, 0,2e9,1,0,1, "environment clauses") \ -OPTION( sweepcomplete, 0, 0, 1,0,0,1, "run SAT sweeping to completion") \ -OPTION( sweepcountbinary, 1, 0, 1,0,0,1, "count binaries to environment") \ -OPTION( sweepdepth, 2, 0,2e9,1,0,1, "environment depth") \ -OPTION( sweepeffort, 1e2, 0,1e4,0,0,1, "relative effort in ticks per mille") \ -OPTION( sweepfliprounds, 1, 0,2e9,1,0,1, "flipping rounds") \ -OPTION( sweepmaxclauses, 3e5, 2,2e9,1,0,1, "maximum environment clauses") \ -OPTION( sweepmaxdepth, 3, 1,2e9,1,0,1, "maximum environment depth") \ -OPTION( sweepmaxvars, 8192, 2,2e9,1,0,1, "maximum environment variables") \ -OPTION( sweeprand, 0, 0, 1,0,0,1, "randomize sweeping environment") \ -OPTION( sweepthresh, 5, 0,100,1,0,1, "delay if ticks smaller thresh*clauses") \ -OPTION( sweepvars, 256, 0,2e9,1,0,1, "environment variables") \ -OPTION( target, 1, 0, 2,0,0,1, "target phases (1=stable only)") \ -OPTION( terminateint, 10, 0,1e4,0,0,1, "termination check interval") \ -OPTION( ternary, 1, 0, 1,0,1,1, "hyper ternary resolution") \ -OPTION( ternaryeffort, 8, 1,1e5,1,0,1, "relative efficiency per mille") \ -OPTION( ternarymaxadd, 1e3, 0,1e4,1,0,1, "max clauses added in percent") \ -OPTION( ternaryocclim, 1e2, 1,2e9,2,0,1, "ternary occurrence limit") \ -OPTION( ternaryrounds, 2, 1, 16,1,0,1, "maximum ternary rounds") \ -OPTION( ternarythresh, 6, 0,100,1,0,1, "delay if ticks smaller thresh*clauses") \ -OPTION( tier1limit, 50, 0,100,0,0,1, "limit of tier1 usage in percentage") \ -OPTION( tier2limit, 90, 0,100,0,0,1, "limit of tier2 usage in percentage") \ -OPTION( transred, 1, 0, 1,0,1,1, "transitive reduction of BIG") \ -OPTION( transredeffort, 1e2, 1,1e5,1,0,1, "relative efficiency per mille") \ -OPTION( transredmaxeff, 1e8, 0,2e9,1,0,1, "maximum efficiency") \ -OPTION( transredmineff, 0, 0,2e9,1,0,1, "minimum efficiency") \ -QUTOPT( verbose, 0, 0, 3,0,0,0, "more verbose messages") \ -OPTION( veripb, 0, 0, 4,0,0,1, "odd=checkdeletions, > 2=drat") \ -OPTION( vivify, 1, 0, 1,0,1,1, "vivification") \ -OPTION( vivifycalctier, 0, 0, 1,0,0,1, "recalculate tier limits") \ -OPTION( vivifydemote, 0, 0, 1,0,1,1, "demote irredundant or delete directly") \ -OPTION( vivifyeffort, 50, 0,1e5,1,0,1, "overall efficiency per mille") \ -OPTION( vivifyflush, 1, 0, 1,1,0,1, "flush subsumed before vivification rounds") \ -OPTION( vivifyinst, 1, 0, 1,0,0,1, "instantiate last literal when vivify") \ -OPTION( vivifyirred, 1, 0, 1,0,1,1, "vivification irred") \ -OPTION( vivifyirredeff, 3, 1,100,1,0,1, "irredundant efficiency per mille") \ -OPTION( vivifyonce, 0, 0, 2,0,0,1, "vivify once: 1=red, 2=red+irr") \ -OPTION( vivifyretry, 0, 0, 5,0,0,1, "re-vivify clause if vivify was successful") \ -OPTION( vivifyschedmax, 5e3, 10,2e9,0,0,1, "maximum schedule size") \ -OPTION( vivifythresh, 20, 0,100,1,0,1, "delay if ticks smaller thresh*clauses") \ -OPTION( vivifytier1, 1, 0, 1,0,1,1, "vivification tier1") \ -OPTION( vivifytier1eff, 4, 0,100,1,0,1, "relative tier1 effort") \ -OPTION( vivifytier2, 1, 0, 1,0,1,1, "vivification tier2") \ -OPTION( vivifytier2eff, 2, 1,100,1,0,1, "relative tier2 effort") \ -OPTION( vivifytier3, 1, 0, 1,0,1,1, "vivification tier3") \ -OPTION( vivifytier3eff, 1, 1,100,1,0,1, "relative tier3 effort") \ -OPTION( walk, 1, 0, 1,0,0,1, "enable random walks") \ -OPTION( walkeffort, 20, 1,1e5,1,0,1, "relative efficiency per mille") \ -OPTION( walkmaxeff, 1e7, 0,2e9,1,0,1, "maximum efficiency") \ -OPTION( walkmineff, 0, 0,1e7,1,0,1, "minimum efficiency") \ -OPTION( walknonstable, 1, 0, 1,0,0,1, "walk in non-stabilizing phase") \ -OPTION( walkredundant, 0, 0, 1,0,0,1, "walk redundant clauses too") \ - -// Note, keep an empty line right before this line because of the last '\'! -// Also keep those single spaces after 'OPTION(' for proper sorting. - -// clang-format on - -/*------------------------------------------------------------------------*/ - -// Some of the 'OPTION' macros above should only be included if certain -// compile time options are enabled. This has the effect, that for instance -// if 'LOGGING' is defined, and thus logging code is included, then also the -// 'log' option is defined. Otherwise the 'log' option is not included. - -#ifdef LOGGING -#define LOGOPT OPTION -#else -#define LOGOPT(...) /**/ -#endif - -#ifdef CADICAL_QUIET -#define QUTOPT(...) /**/ -#else -#define QUTOPT OPTION -#endif - -/*------------------------------------------------------------------------*/ - -namespace CaDiCaL { - -struct Internal; - -/*------------------------------------------------------------------------*/ - -class Options; - -struct Option { - const char *name; - int def, lo, hi; - int optimizable; - bool preprocessing; - const char *description; - int &val (Options *); -}; - -/*------------------------------------------------------------------------*/ - -// Produce a compile time constant for the number of options. - -static const size_t number_of_options = -#define OPTION(N, V, L, H, O, P, R, D) 1 + - OPTIONS -#undef OPTION - + 0; - -/*------------------------------------------------------------------------*/ - -class Options { - - Internal *internal; - - void set (Option *, int val); // Force to [lo,hi] interval. - - friend struct Option; - static Option table[]; - - static void initialize_from_environment (int &val, const char *name, - const int L, const int H); - - friend Config; - - void reset_default_values (); - void disable_preprocessing (); - -public: - // For library usage we disable reporting by default while for the stand - // alone SAT solver we enable it by default. This default value has to - // be set before the constructor of 'Options' is called (which in turn is - // called from the constructor of 'Solver'). If we would simply overwrite - // its initial value while initializing the stand alone solver, we will - // get that change of the default value (from 'false' to 'true') shown - // during calls to 'print ()', which is confusing to the user. - // - static int reportdefault; - - Options (Internal *); - - // Makes options directly accessible, e.g., for instance declares the - // member 'int restart' here. This will give fast access to option values - // internally in the solver and thus can also be used in tight loops. - // -private: - int __start_of_options__; // Used by 'val' below. -public: -#define OPTION(N, V, L, H, O, P, R, D) \ - int N; // Access option values by name. - OPTIONS -#undef OPTION - - // It would be more elegant to use an anonymous 'struct' of the actual - // option values overlayed with an 'int values[number_of_options]' array - // but that is not proper ISO C++ and produces a warning. Instead we use - // the following construction which relies on '__start_of_options__' and - // that the following options are really allocated directly after it. - // - inline int &val (size_t idx) { - CADICAL_assert (idx < number_of_options); - return (&__start_of_options__ + 1)[idx]; - } - - // With the following function we can get rather fast access to the option - // limits, the default value and the description. The code uses binary - // search over the sorted option 'table'. This static data is shared - // among different instances of the solver. The actual current option - // values are here in the 'Options' class. They can be accessed by the - // offset of the static options using 'Option::val' if you have an - // 'Option' or to have even faster access directly by the member function - // (the 'N' above, e.g., 'restart'). - // - static Option *has (const char *name); - - bool set (const char *name, int); // Explicit version. - int get (const char *name); // Get current value. - - void print (); // Print current values in command line form - static void usage (); // Print usage message for all options. - - void optimize (int val); // increase some limits (val=0..31) - - static bool is_preprocessing_option (const char *name); - - // Parse long option argument - // - // --<name> - // --<name>=<val> - // --no-<name> - // - // where '<val>' is as in 'parse_option_value'. If parsing succeeds, - // 'true' is returned and the string will be set to the name of the - // option. Additionally the parsed value is set (last argument). - // - static bool parse_long_option (const char *, string &, int &); - - // Iterating options. - - typedef Option *iterator; - typedef const Option *const_iterator; - - static iterator begin () { return table; } - static iterator end () { return table + number_of_options; } - - void copy (Options &other) const; // Copy 'this' into 'other'. -}; - -inline int &Option::val (Options *opts) { - CADICAL_assert (Options::table <= this && - this < Options::table + number_of_options); - return opts->val (this - Options::table); -} - -} // namespace CaDiCaL - -ABC_NAMESPACE_CXX_HEADER_END - -#endif diff --git a/src/sat/cadical/parse.hpp b/src/sat/cadical/parse.hpp deleted file mode 100644 index a37117f438..0000000000 --- a/src/sat/cadical/parse.hpp +++ /dev/null @@ -1,78 +0,0 @@ -#ifndef _parse_hpp_INCLUDED -#define _parse_hpp_INCLUDED - -#include "global.h" - -#include <cassert> -#include <vector> - -ABC_NAMESPACE_CXX_HEADER_START - -namespace CaDiCaL { - -// Factors out common functions for parsing of DIMACS and solution files. - -class File; -struct External; -struct Internal; - -class Parser { - - Solver *solver; - Internal *internal; - External *external; - File *file; - - void perr (const char *fmt, ...) CADICAL_ATTRIBUTE_FORMAT (2, 3); - int parse_char (); - - enum { - FORCED = 0, // Force reading even if header is broken. - RELAXED = 1, // Relaxed white space treatment in header. - STRICT = 2, // Strict white space and header compliance. - }; - - const char *parse_string (const char *str, char prev); - const char *parse_positive_int (int &ch, int &res, const char *name); - const char *parse_lit (int &ch, int &lit, int &vars, int strict); - const char *parse_dimacs_non_profiled (int &vars, int strict); - const char *parse_solution_non_profiled (); - - bool *parse_inccnf_too; - vector<int> *cubes; - -public: - // Parse a DIMACS CNF or ICNF file. - // - // Return zero if successful. Otherwise parse error. - Parser (Solver *s, File *f, bool *i, vector<int> *c) - : solver (s), internal (s->internal), external (s->external), - file (f), parse_inccnf_too (i), cubes (c) {} - - // Parse a DIMACS file. Return zero if successful. Otherwise a parse - // error is return. The parsed clauses are added to the solver and the - // maximum variable index found is returned in the 'vars' argument. The - // 'strict' argument can be '0' in which case the numbers in the header - // can be arbitrary, e.g., 'p cnf 0 0' all the time, without producing a - // parse error. Only for this setting the parsed literals are not checked - // to overflow the maximum variable index of the header. The strictest - // form of parsing is enforced for the value '2' of 'strict', in which - // case the header can not have additional white space, while a value of - // '1' exactly relaxes this, e.g., 'p cnf \t 1 3 \r\n' becomes legal. - // - const char *parse_dimacs (int &vars, int strict); - - // Parse a solution file as used in the SAT competition, e.g., with - // comment lines 'c ...', a status line 's ...' and value lines 'v ...'. - // Returns zero if successful. Otherwise a string is returned describing - // the parse error. The parsed solution is saved in 'solution' and can be - // accessed with 'sol (int lit)'. We use it for checking learned clauses. - // - const char *parse_solution (); -}; - -} // namespace CaDiCaL - -ABC_NAMESPACE_CXX_HEADER_END - -#endif diff --git a/src/sat/cadical/phases.hpp b/src/sat/cadical/phases.hpp deleted file mode 100644 index 98e54d4395..0000000000 --- a/src/sat/cadical/phases.hpp +++ /dev/null @@ -1,24 +0,0 @@ -#ifndef _phases_hpp_INCLUDED -#define _phases_hpp_INCLUDED - -#include "global.h" - -ABC_NAMESPACE_CXX_HEADER_START - -namespace CaDiCaL { - -struct Phases { - - vector<signed char> best; // The current largest trail phase. - vector<signed char> forced; // Forced through 'phase'. - vector<signed char> min; // The current minimum unsatisfied phase. - vector<signed char> prev; // Previous during local search. - vector<signed char> saved; // The actual saved phase. - vector<signed char> target; // The current target phase. -}; - -} // namespace CaDiCaL - -ABC_NAMESPACE_CXX_HEADER_END - -#endif diff --git a/src/sat/cadical/profile.hpp b/src/sat/cadical/profile.hpp deleted file mode 100644 index f3799389b4..0000000000 --- a/src/sat/cadical/profile.hpp +++ /dev/null @@ -1,280 +0,0 @@ -#ifndef _profiles_h_INCLUDED -#define _profiles_h_INCLUDED - -#include "global.h" - -ABC_NAMESPACE_CXX_HEADER_START - -/*------------------------------------------------------------------------*/ -#ifndef CADICAL_QUIET -/*------------------------------------------------------------------------*/ - -namespace CaDiCaL { - -struct Internal; - -/*------------------------------------------------------------------------*/ - -// The solver contains some built in profiling (even for optimized code). -// The idea is that even without using external tools it is possible to get -// an overview of where time is spent. This is enabled with the option -// 'profile', e.g., you might want to use '--profile=3', or even higher -// values for more detailed profiling information. Currently the default is -// '--profile=2', which should only induce a tiny profiling overhead. -// -// Profiling has a Heisenberg effect, since we rely on calling 'getrusage' -// instead of using profile counters and sampling. For functions which are -// executed many times, this overhead is substantial (say 10%-20%). For -// functions which are not executed many times there is in essence no -// overhead in measuring time spent in them. These get a smaller profiling -// level, which is the second argument in the 'PROFILE' macro below. Thus -// using '--profile=1' for instance should not add any penalty to the -// run-time, while '--profile=3' and higher levels slow down the solver. -// -// To profile say 'foo', just add another line 'PROFILE(foo,LEVEL)' and wrap -// the code to be profiled within a 'START (foo)' / 'STOP (foo)' block. - -/*------------------------------------------------------------------------*/ - -// Profile counters for functions which are not compiled in should be -// removed. This is achieved by adding a wrapper macro for them here. - -/*------------------------------------------------------------------------*/ - -#ifdef PROFILE_MODE -#define MROFILE PROFILE -#else -#define MROFILE(...) /**/ -#endif - -#define PROFILES \ - PROFILE (analyze, 3) \ - MROFILE (analyzestable, 4) \ - MROFILE (analyzeunstable, 4) \ - PROFILE (backward, 3) \ - PROFILE (block, 2) \ - PROFILE (bump, 4) \ - PROFILE (checking, 2) \ - PROFILE (cdcl, 1) \ - PROFILE (collect, 3) \ - PROFILE (compact, 3) \ - PROFILE (condition, 2) \ - PROFILE (congruence, 2) \ - PROFILE (congruencemerge, 4) \ - PROFILE (congruencematching, 4) \ - PROFILE (connect, 3) \ - PROFILE (copy, 4) \ - PROFILE (cover, 2) \ - PROFILE (decide, 3) \ - PROFILE (decompose, 3) \ - PROFILE (definition, 2) \ - PROFILE (elim, 2) \ - PROFILE (factor, 2) \ - PROFILE (fastelim, 2) \ - PROFILE (extend, 3) \ - PROFILE (extract, 3) \ - PROFILE (extractands, 4) \ - PROFILE (extractbinaries, 4) \ - PROFILE (extractites, 4) \ - PROFILE (extractxors, 4) \ - PROFILE (instantiate, 2) \ - PROFILE (lucky, 2) \ - PROFILE (lookahead, 2) \ - PROFILE (minimize, 4) \ - PROFILE (shrink, 4) \ - PROFILE (parse, 0) /* As 'opts.profile' might change in parsing*/ \ - PROFILE (probe, 2) \ - PROFILE (deduplicate, 3) \ - PROFILE (propagate, 4) \ - MROFILE (propstable, 4) \ - MROFILE (propunstable, 4) \ - PROFILE (reduce, 3) \ - PROFILE (restart, 3) \ - PROFILE (restore, 2) \ - PROFILE (search, 1) \ - PROFILE (solve, 0) \ - PROFILE (stable, 2) \ - PROFILE (sweep, 2) \ - PROFILE (sweepbackbone, 3) \ - PROFILE (sweepequivalences, 3) \ - PROFILE (sweepflip, 4) \ - PROFILE (sweepimplicant, 4) \ - PROFILE (sweepsolve, 4) \ - PROFILE (preprocess, 2) \ - PROFILE (simplify, 1) \ - PROFILE (subsume, 2) \ - PROFILE (ternary, 2) \ - PROFILE (transred, 3) \ - PROFILE (unstable, 2) \ - PROFILE (vivify, 2) \ - PROFILE (walk, 2) - -/*------------------------------------------------------------------------*/ - -// See 'START' and 'STOP' in 'macros.hpp' too. - -struct Profile { - - bool active; - double value; // accumulated time - double started; // started time if active - const char *name; // name of the profiled function (or 'phase') - const int level; // allows to cheaply test if profiling is enabled - - Profile (const char *n, int l) - : active (false), value (0), name (n), level (l) {} -}; - -struct Profiles { - Internal *internal; -#define PROFILE(NAME, LEVEL) Profile NAME; - PROFILES -#undef PROFILE - Profiles (Internal *); -}; - -} // namespace CaDiCaL - -#define NON_CADICAL_QUIET_PROFILE_CODE(CODE) CODE - -#else // !defined(CADICAL_QUIET) - -#define NON_CADICAL_QUIET_PROFILE_CODE(CODE) /**/ - -#endif - -/*------------------------------------------------------------------------*/ - -// Macros for Profiling support and checking and changing the mode. - -#define START(P) \ - do { \ - NON_CADICAL_QUIET_PROFILE_CODE ( \ - if (internal->profiles.P.level <= internal->opts.profile) \ - internal->start_profiling (internal->profiles.P, \ - internal->time ());) \ - } while (0) - -#define STOP(P) \ - do { \ - NON_CADICAL_QUIET_PROFILE_CODE ( \ - if (internal->profiles.P.level <= internal->opts.profile) \ - internal->stop_profiling (internal->profiles.P, \ - internal->time ());) \ - } while (0) - -#define PROFILE_ACTIVE(P) \ - ((internal->profiles.P.level <= internal->opts.profile) && \ - (internal->profiles.P.active)) - -/*------------------------------------------------------------------------*/ - -#define START_SIMPLIFIER(S, M) \ - do { \ - NON_CADICAL_QUIET_PROFILE_CODE (const double N = time (); \ - const int L = internal->opts.profile;) \ - if (!preprocessing && !lookingahead) { \ - NON_CADICAL_QUIET_PROFILE_CODE ( \ - if (stable && internal->profiles.stable.level <= L) \ - internal->stop_profiling (internal->profiles.stable, N); \ - if (!stable && internal->profiles.unstable.level <= L) \ - internal->stop_profiling (internal->profiles.unstable, N); \ - if (internal->profiles.search.level <= L) \ - internal->stop_profiling (internal->profiles.search, N);) \ - reset_mode (SEARCH); \ - } \ - NON_CADICAL_QUIET_PROFILE_CODE ( \ - if (internal->profiles.simplify.level <= L) \ - internal->start_profiling (internal->profiles.simplify, N); \ - if (internal->profiles.S.level <= L) \ - internal->start_profiling (internal->profiles.S, N);) \ - set_mode (SIMPLIFY); \ - set_mode (M); \ - } while (0) - -/*------------------------------------------------------------------------*/ - -#define STOP_SIMPLIFIER(S, M) \ - do { \ - NON_CADICAL_QUIET_PROFILE_CODE ( \ - const double N = time (); const int L = internal->opts.profile; \ - if (internal->profiles.S.level <= L) \ - internal->stop_profiling (internal->profiles.S, N); \ - if (internal->profiles.simplify.level <= L) \ - internal->stop_profiling (internal->profiles.simplify, N);) \ - reset_mode (M); \ - reset_mode (SIMPLIFY); \ - if (!preprocessing && !lookingahead) { \ - NON_CADICAL_QUIET_PROFILE_CODE ( \ - if (internal->profiles.search.level <= L) \ - internal->start_profiling (internal->profiles.search, N); \ - if (stable && internal->profiles.stable.level <= L) \ - internal->start_profiling (internal->profiles.stable, N); \ - if (!stable && internal->profiles.unstable.level <= L) \ - internal->start_profiling (internal->profiles.unstable, N);) \ - set_mode (SEARCH); \ - } \ - } while (0) - -/*------------------------------------------------------------------------*/ -// Used in 'walk' before calling 'walk_round' within the CDCL loop. - -#define START_INNER_WALK() \ - do { \ - require_mode (SEARCH); \ - CADICAL_assert (!preprocessing); \ - NON_CADICAL_QUIET_PROFILE_CODE ( \ - const double N = time (); const int L = internal->opts.profile; \ - if (stable && internal->profiles.stable.level <= L) \ - internal->stop_profiling (internal->profiles.stable, N); \ - if (!stable && internal->profiles.unstable.level <= L) \ - internal->stop_profiling (internal->profiles.unstable, N); \ - if (internal->profiles.walk.level <= L) \ - internal->start_profiling (internal->profiles.walk, N);) \ - set_mode (WALK); \ - } while (0) - -/*------------------------------------------------------------------------*/ -// Used in 'walk' after calling 'walk_round' within the CDCL loop. - -#define STOP_INNER_WALK() \ - do { \ - require_mode (SEARCH); \ - CADICAL_assert (!preprocessing); \ - reset_mode (WALK); \ - NON_CADICAL_QUIET_PROFILE_CODE ( \ - const double N = time (); const int L = internal->opts.profile; \ - if (internal->profiles.walk.level <= L) \ - internal->stop_profiling (internal->profiles.walk, N); \ - if (stable && internal->profiles.stable.level <= L) \ - internal->start_profiling (internal->profiles.stable, N); \ - if (!stable && internal->profiles.unstable.level <= L) \ - internal->start_profiling (internal->profiles.unstable, N); \ - internal->profiles.walk.started = (N);) \ - } while (0) - -/*------------------------------------------------------------------------*/ -// Used in 'local_search' before calling 'walk_round'. - -#define START_OUTER_WALK() \ - do { \ - require_mode (SEARCH); \ - CADICAL_assert (!preprocessing); \ - NON_CADICAL_QUIET_PROFILE_CODE (START (walk);) \ - set_mode (WALK); \ - } while (0) - -/*------------------------------------------------------------------------*/ -// Used in 'local_search' after calling 'walk_round'. - -#define STOP_OUTER_WALK() \ - do { \ - require_mode (SEARCH); \ - CADICAL_assert (!preprocessing); \ - reset_mode (WALK); \ - NON_CADICAL_QUIET_PROFILE_CODE (STOP (walk);) \ - } while (0) - -ABC_NAMESPACE_CXX_HEADER_END - -#endif // ifndef _profiles_h_INCLUDED diff --git a/src/sat/cadical/proof.hpp b/src/sat/cadical/proof.hpp deleted file mode 100644 index 69be6b0807..0000000000 --- a/src/sat/cadical/proof.hpp +++ /dev/null @@ -1,120 +0,0 @@ -#ifndef _proof_h_INCLUDED -#define _proof_h_INCLUDED - -#include "global.h" - -ABC_NAMESPACE_CXX_HEADER_START - -namespace CaDiCaL { - -/*------------------------------------------------------------------------*/ - -class File; -struct Clause; -struct Internal; -class Tracer; -class FileTracer; - -/*------------------------------------------------------------------------*/ - -// Provides proof checking and writing. - -class Proof { - - Internal *internal; - - vector<int> clause; // of external literals - vector<int64_t> proof_chain; // LRAT style proof chain of clause - int64_t clause_id; // id of added clause - bool redundant; - - // the 'tracers' - vector<Tracer *> tracers; // tracers (ie checker) - vector<FileTracer *> file_tracers; // file tracers (ie LRAT tracer) - - void add_literal (int internal_lit); // add to 'clause' - void add_literals (Clause *); // add to 'clause' - - void add_literals (const vector<int> &); // ditto - - void add_original_clause ( - bool restore = false); // notify observers of original clauses - void add_derived_clause (); - void add_assumption_clause (); - void delete_clause (); - void demote_clause (); - void weaken_minus (); - void strengthen (); - void finalize_clause (); - void add_assumption (); - void add_constraint (); - -public: - Proof (Internal *); - ~Proof (); - - void connect (Tracer *t) { tracers.push_back (t); } - void disconnect (Tracer *t); - // Add original clauses to the proof (for online proof checking). - // - void add_original_clause (int64_t, bool, const vector<int> &); - - void add_assumption_clause (int64_t, const vector<int> &, - const vector<int64_t> &); - void add_assumption_clause (int64_t, int, const vector<int64_t> &); - void add_assumption (int); - void add_constraint (const vector<int> &); - void reset_assumptions (); - - // Add/delete original clauses to/from the proof using their original - // external literals (from external->eclause) - // - void add_external_original_clause (int64_t, bool, const vector<int> &, - bool restore = false); - void delete_external_original_clause (int64_t, bool, const vector<int> &); - - // Add derived (such as learned) clauses to the proof. - // - void add_derived_empty_clause (int64_t, const vector<int64_t> &); - void add_derived_unit_clause (int64_t, int unit, const vector<int64_t> &); - void add_derived_clause (Clause *c, const vector<int64_t> &); - void add_derived_clause (int64_t, bool, const vector<int> &, - const vector<int64_t> &); - - // deletion of clauses. It comes in several variants, depending if the - // clause should be restored or not - void delete_clause (int64_t, bool, const vector<int> &); - void weaken_minus (int64_t, const vector<int> &); - void weaken_plus (int64_t, const vector<int> &); - void delete_unit_clause (int64_t id, const int lit); - void delete_clause (Clause *); - void weaken_minus (Clause *); - void weaken_plus (Clause *); - void strengthen (int64_t); - - void finalize_unit (int64_t, int); - void finalize_external_unit (int64_t, int); - void finalize_clause (int64_t, const vector<int> &c); - void finalize_clause (Clause *); - - void report_status (int, int64_t); - void begin_proof (int64_t); - void conclude_unsat (ConclusionType, const vector<int64_t> &); - void conclude_sat (const vector<int> &model); - void conclude_unknown (const vector<int> &trace); - void solve_query (); - // These two actually pretend to add and remove a clause. - // - void flush_clause (Clause *); // remove falsified literals - void strengthen_clause (Clause *, int, const vector<int64_t> &); - void otfs_strengthen_clause (Clause *, const vector<int> &, - const vector<int64_t> &); - - void flush (); -}; - -} // namespace CaDiCaL - -ABC_NAMESPACE_CXX_HEADER_END - -#endif diff --git a/src/sat/cadical/queue.hpp b/src/sat/cadical/queue.hpp deleted file mode 100644 index 14e1f2da7b..0000000000 --- a/src/sat/cadical/queue.hpp +++ /dev/null @@ -1,70 +0,0 @@ -#ifndef _queue_hpp_INCLUDED -#define _queue_hpp_INCLUDED - -#include "global.h" - -#include <vector> - -ABC_NAMESPACE_CXX_HEADER_START - -namespace CaDiCaL { - -// Links for double linked decision queue. - -struct Link { - - int prev, next; // variable indices - - // initialized explicitly in 'init_queue' -}; - -typedef std::vector<Link> Links; - -// Variable move to front (VMTF) decision queue ordered by 'bumped'. See -// our SAT'15 paper for an explanation on how this works. - -struct Queue { - - // We use integers instead of variable pointers. This is more compact and - // also avoids issues due to moving the variable table during 'resize'. - - int first, last; // anchors (head/tail) for doubly linked list - int unassigned; // all variables after this one are assigned - int64_t bumped; // see 'Internal.update_queue_unassigned' - - Queue () : first (0), last (0), unassigned (0), bumped (0) {} - - // We explicitly provide the mapping of integer indices to links to the - // following two (inlined) functions. This avoids including - // 'internal.hpp' and breaks a cyclic dependency, so we can keep their - // code here in this header file. Otherwise they are just ordinary doubly - // linked list 'dequeue' and 'enqueue' operations. - - inline void dequeue (Links &links, int idx) { - Link &l = links[idx]; - if (l.prev) - links[l.prev].next = l.next; - else - first = l.next; - if (l.next) - links[l.next].prev = l.prev; - else - last = l.prev; - } - - inline void enqueue (Links &links, int idx) { - Link &l = links[idx]; - if ((l.prev = last)) - links[last].next = idx; - else - first = idx; - last = idx; - l.next = 0; - } -}; - -} // namespace CaDiCaL - -ABC_NAMESPACE_CXX_HEADER_END - -#endif diff --git a/src/sat/cadical/radix.hpp b/src/sat/cadical/radix.hpp deleted file mode 100644 index 4c4d7b574e..0000000000 --- a/src/sat/cadical/radix.hpp +++ /dev/null @@ -1,186 +0,0 @@ -#ifndef _radix_hpp_INCLUDED -#define _radix_hpp_INCLUDED - -#include "global.h" - -#include <cassert> -#include <cstring> -#include <iterator> -#include <vector> - -ABC_NAMESPACE_CXX_HEADER_START - -namespace CaDiCaL { - -using namespace std; - -// This provides an implementation of a generic radix sort algorithm. The -// reason for having it is that for certain benchmarks and certain parts of -// CaDiCaL where sorting is used, the standard sorting algorithm 'sort' -// turned out to be a hot-spot. Up to 30% of the total running time was for -// instance used for some benchmarks in sorting variables during bumping -// to make sure to bump them in 'enqueued' order. -// -// Further, in most cases, where we need to sort something, sorting is -// actually performed on positive numbers (such as the 'enqueued' time stamp -// during bumping), which allows to use radix sort or variants. At least -// starting with medium sized arrays to be sorted (say above 1000 elements, -// but see discussion on 'MSORT' below), radix sort can be way faster. -// -// Finally it is stable, which is actually preferred most of the time too. -// -// This template algorithm 'rsort' takes as first template parameter the -// iterator class similar to the standard 'sort' algorithm template, but -// then as second parameter a function class (similar to the second 'less -// than' parameter of 'sort') which can obtain a 'rank' from each element, -// on which they are compared. The 'rank' should be able to turn an element -// into a number. The type of these ranks is determined automatically but -// should be 'unsigned'. - -struct pointer_rank { - typedef size_t Type; - Type operator() (void *ptr) { return (size_t) ptr; } -}; - -template <class I, class Rank> void rsort (I first, I last, Rank rank) { - typedef typename iterator_traits<I>::value_type T; - typedef typename Rank::Type R; - - CADICAL_assert (first <= last); - const size_t n = last - first; - if (n <= 1) - return; - - const size_t l = 8; // Radix 8, thus byte-wise. - const size_t w = (1 << l); // So many buckets. - - const unsigned mask = w - 1; // Fast mod 'w'. - -// Uncomment the following define for large values of 'w' in order to keep -// the large bucket array 'count' on the heap instead of the stack. -// -// #define CADICAL_RADIX_BUCKETS_ON_THE_HEAP -// -#ifdef CADICAL_RADIX_BUCKETS_ON_THE_HEAP - size_t *count = new size_t[w]; // Put buckets on the heap. -#else - size_t count[w]; // Put buckets on the stack. -#endif - - I a = first, b = last, c = a; - bool initialized = false; - std::vector<T> v; - - R upper = 0, lower = ~upper; - R shifted = mask; - bool bounded = false; - - R masked_lower = 0, masked_upper = mask; - - for (size_t i = 0; i < 8 * sizeof (rank (*first)); - i += l, shifted <<= l) { - - if (bounded && (lower & shifted) == (upper & shifted)) - continue; - - memset (count + masked_lower, 0, - (masked_upper - masked_lower + 1) * sizeof *count); - - const I end = c + n; - bool sorted = true; - R last = 0; - - for (I p = c; p != end; p++) { - const auto r = rank (*p); - if (!bounded) - lower &= r, upper |= r; - const auto s = r >> i; - const auto m = s & mask; - if (sorted && last > m) - sorted = false; - else - last = m; - count[m]++; - } - - masked_lower = (lower >> i) & mask; - masked_upper = (upper >> i) & mask; - - if (!bounded) { - bounded = true; - if ((lower & shifted) == (upper & shifted)) - continue; - } - - if (sorted) - continue; - - size_t pos = 0; - for (R j = masked_lower; j <= masked_upper; j++) { - const size_t delta = count[j]; - count[j] = pos; - pos += delta; - } - - if (!initialized) { - CADICAL_assert (&*c == &*a); // MS VC++ - v.resize (n); - b = v.begin (); - initialized = true; - } - - I d = (&*c == &*a) ? b : a; // MS VC++ - - for (I p = c; p != end; p++) { - const auto r = rank (*p); - const auto s = r >> i; - const auto m = s & mask; - d[count[m]++] = *p; - } - c = d; - } - - if (c == b) { - for (size_t i = 0; i < n; i++) - a[i] = b[i]; - } - -#ifdef CADICAL_RADIX_BUCKETS_ON_THE_HEAP - delete[] count; -#endif - -#ifndef CADICAL_NDEBUG - for (I p = first; p + 1 != last; p++) - CADICAL_assert (rank (p[0]) <= rank (p[1])); -#endif -} - -// It turns out that for small number of elements (like '100') and in -// particular for large value ranges the standard sorting function is -// considerably faster than our radix sort (like 2.5x). This negative effect -// vanishes at around 800 elements (sorting integers) and thus we provide a -// function 'MSORT' which selects between standard sort and radix sort based -// on the number of elements. However we failed to put this into proper C++ -// style template code and thus have to use a macro instead. We also do not -// use it everywhere instead of 'rsort' since it requires a fourth -// parameter, which is awkward, particular in those situation where we -// expect large arrays to be sorted anyhow (such as during sorting the -// clauses in an arena or the probes during probing). The first argument -// is the limit up to which we use the standard sort. Above the limit we -// use radix sort. As usual we do not want to hard code it here (default -// is '800') in order to make fuzzing and delta debugging more effective. - -#define MSORT(LIMIT, FIRST, LAST, RANK, LESS) \ - do { \ - const size_t N = LAST - FIRST; \ - if (N <= (size_t) (LIMIT)) \ - sort (FIRST, LAST, LESS); \ - else \ - rsort (FIRST, LAST, RANK); \ - } while (0) - -} // namespace CaDiCaL - -ABC_NAMESPACE_CXX_HEADER_END - -#endif diff --git a/src/sat/cadical/random.h b/src/sat/cadical/random.h deleted file mode 100644 index 62fe5ed19c..0000000000 --- a/src/sat/cadical/random.h +++ /dev/null @@ -1,50 +0,0 @@ -#ifndef _random_h_INCLUDED -#define _random_h_INCLUDED - -#include "global.h" - -#include <assert.h> -#include <stdbool.h> -#include <stdint.h> - -ABC_NAMESPACE_HEADER_START - -typedef uint64_t generator; - -static inline uint64_t kissat_next_random64 (generator *rng) { - *rng *= 6364136223846793005ul; - *rng += 1442695040888963407ul; - return *rng; -} - -static inline unsigned kissat_next_random32 (generator *rng) { - return kissat_next_random64 (rng) >> 32; -} - -static inline unsigned kissat_pick_random (generator *rng, unsigned l, - unsigned r) { - CADICAL_assert (l <= r); - if (l == r) - return l; - const unsigned delta = r - l; - const unsigned tmp = kissat_next_random32 (rng); - const double fraction = tmp / 4294967296.0; - CADICAL_assert (0 <= fraction), CADICAL_assert (fraction < 1); - const unsigned scaled = delta * fraction; - CADICAL_assert (scaled < delta); - const unsigned res = l + scaled; - CADICAL_assert (l <= res), CADICAL_assert (res < r); - return res; -} - -static inline bool kissat_pick_bool (generator *rng) { - return kissat_pick_random (rng, 0, 2); -} - -static inline double kissat_pick_double (generator *rng) { - return kissat_next_random32 (rng) / 4294967296.0; -} - -ABC_NAMESPACE_HEADER_END - -#endif diff --git a/src/sat/cadical/random.hpp b/src/sat/cadical/random.hpp deleted file mode 100644 index a5c78d38aa..0000000000 --- a/src/sat/cadical/random.hpp +++ /dev/null @@ -1,104 +0,0 @@ -#ifndef _random_hpp_INCLUDED -#define _random_hpp_INCLUDED - -#include "global.h" - -#include <cstdint> - -ABC_NAMESPACE_CXX_HEADER_START - -// Random number generator. - -namespace CaDiCaL { - -class Random { - - uint64_t state; - - void add (uint64_t a) { - if (!(state += a)) - state = 1; - next (); - } - -public: - // Without argument use a machine, process and time dependent seed. - // - Random (); - - Random (uint64_t seed) : state (seed) {} - void operator= (uint64_t seed) { state = seed; } - Random (const Random &other) : state (other.seed ()) {} - - void operator+= (uint64_t a) { add (a); } - uint64_t seed () const { return state; } - - uint64_t next () { - state *= 6364136223846793005ul; - state += 1442695040888963407ul; - CADICAL_assert (state); - return state; - } - - uint32_t generate () { - next (); - return state >> 32; - } - int generate_int () { return (int) generate (); } - bool generate_bool () { return generate () < 2147483648u; } - - // Generate 'double' value in the range '[0,1]' excluding '1'. - // - double generate_double () { return generate () / 4294967295.0; } - - // Generate 'int' value in the range '[l,r]'. - // - int pick_int (int l, int r) { - CADICAL_assert (l <= r); - const unsigned delta = 1 + r - (unsigned) l; - unsigned tmp = generate (), scaled; - if (delta) { - const double fraction = tmp / 4294967296.0; - scaled = delta * fraction; - } else - scaled = tmp; - const int res = scaled + l; - CADICAL_assert (l <= res); - CADICAL_assert (res <= r); - return res; - } - - int pick_log (int l, int r) { - CADICAL_assert (l <= r); - const unsigned delta = 1 + r - (unsigned) l; - int log_delta = delta ? 0 : 32; - while (log_delta < 32 && (1u << log_delta) < delta) - log_delta++; - const int log_res = pick_int (0, log_delta); - unsigned tmp = generate (); - if (log_res < 32) - tmp &= (1u << log_res) - 1; - if (delta) - tmp %= delta; - const int res = l + tmp; - CADICAL_assert (l <= res), CADICAL_assert (res <= r); - return res; - } - - // Generate 'double' value in the range '[l,r]'. - // - double pick_double (double l, double r) { - CADICAL_assert (l <= r); - double res = (r - l) * generate_double (); - res += l; - CADICAL_assert (l <= res); - CADICAL_assert (res <= r); - return res; - } -}; - -} // namespace CaDiCaL - -ABC_NAMESPACE_CXX_HEADER_END - -#endif diff --git a/src/sat/cadical/range.hpp b/src/sat/cadical/range.hpp deleted file mode 100644 index 7049dab42a..0000000000 --- a/src/sat/cadical/range.hpp +++ /dev/null @@ -1,105 +0,0 @@ -#ifndef _range_hpp_INCLUDED -#define _range_hpp_INCLUDED - -#include "global.h" - -#include <cassert> - -ABC_NAMESPACE_CXX_HEADER_START - -namespace CaDiCaL { - -struct Clause; - -/*----------------------------------------------------------------------*/ - -// Used for compact and safe iteration over positive ranges of integers, -// particularly for iterating over all variable indices. -// -// Range vars (max_var); -// for (auto idx : vars) ... -// -// This iterates over '1, ..., max_var' and is safe for non-negative -// numbers, thus also for 'max_var == 0' or 'max_var == INT_MAX'. -// -// Note that -// -// for (int idx = 1; idx <= max_var; idx++) ... -// -// leads to an overflow if 'max_var == INT_MAX' and thus depending on what -// the compiler does ('int' overflow is undefined) might lead to any -// behaviour (infinite loop or worse array access way out of bounds). -// -// If we make 'idx' in this last 'for' loop an 'unsigned' then it is safe to -// use this idiom, but we would need to cast 'max_var' explicitly to 'int' -// in order to avoid a warning in the loop condition and actually everywhere -// where 'idx' is compared to a 'signed' expression. Worse for instance -// 'vals[-idx]' will lead to out of bounds access too. This is awkward and -// using the range iterator provided here is safer in general. -// -// Another issue is that the dereferencing operator '*' below is required to -// return a reference to the internal index of the iterator. Thus the 'idx' -// in the auto loop is actually of the same type as the internal state of -// the iterator. To keep it 'signed' and still avoid overflow issues we -// just have to make sure to use the proper increment (with two implicit -// casts, i.e., from 'int' to 'unsigned', then 'unsigned' addition and the -// result is cast back from 'unsigned' to 'int'). -// -// For simplicity we keep a reference to the actual maximum integer, e.g., -// 'max_var', which makes the idiom 'for (auto idx : vars) ...' possible. -// Further note that the referenced integer has to be non-negative before -// starting to iterate (it can be zero though), otherwise it breaks. - -class Range { - static unsigned inc (unsigned u) { return u + 1u; } - class iterator { - int idx; - - public: - iterator (int i) : idx (i) {} - void operator++ () { idx = inc (idx); } - const int &operator* () const { return idx; } - friend bool operator!= (const iterator &a, const iterator &b) { - return a.idx != b.idx; - } - }; - int &n; - -public: - iterator begin () const { return CADICAL_assert (n >= 0), iterator (inc (0)); } - iterator end () const { return CADICAL_assert (n >= 0), iterator (inc (n)); } - Range (int &m) : n (m) { CADICAL_assert (m >= 0); } -}; - -// Same, but iterating over literals '-1,1,-2,2,....,-max_var,max_var'. -// -// The only difference to 'Range' is the 'inc' function, but I am too lazy -// to figure out how to properly factor the code into a generic range -// template with 'inc' as only parameter. This gives at least clean code. - -class Sange { - static unsigned inc (unsigned u) { return ~u + (u >> 31); } - class iterator { - int lit; - - public: - iterator (int i) : lit (i) {} - void operator++ () { lit = inc (lit); } - const int &operator* () const { return lit; } - friend bool operator!= (const iterator &a, const iterator &b) { - return a.lit != b.lit; - } - }; - int &n; - -public: - iterator begin () const { return CADICAL_assert (n >= 0), iterator (inc (0)); } - iterator end () const { return CADICAL_assert (n >= 0), iterator (inc (n)); } - Sange (int &m) : n (m) { CADICAL_assert (m >= 0); } -}; - -} // namespace CaDiCaL - -ABC_NAMESPACE_CXX_HEADER_END - -#endif diff --git a/src/sat/cadical/reap.hpp b/src/sat/cadical/reap.hpp deleted file mode 100644 index 1ea10e6e34..0000000000 --- a/src/sat/cadical/reap.hpp +++ /dev/null @@ -1,34 +0,0 @@ -#ifndef _reap_h_INCLUDED -#define _reap_h_INCLUDED - -#include "global.h" - -#include <cstddef> -#include <vector> - -ABC_NAMESPACE_CXX_HEADER_START - -class Reap { -public: - Reap (); - void init (); - void release (); - inline bool empty () { return !num_elements; } - - inline size_t size () { return num_elements; } - - void push (unsigned); - void clear (); - unsigned pop (); - -private: - size_t num_elements; - unsigned last_deleted; - unsigned min_bucket; - unsigned max_bucket; - std::vector<unsigned> buckets[33]; -}; - -ABC_NAMESPACE_CXX_HEADER_END - -#endif diff --git a/src/sat/cadical/reluctant.hpp b/src/sat/cadical/reluctant.hpp deleted file mode 100644 index 495382b56b..0000000000 --- a/src/sat/cadical/reluctant.hpp +++ /dev/null @@ -1,88 +0,0 @@ -#ifndef _reluctant_hpp_INCLUDED -#define _reluctant_hpp_INCLUDED - -#include "global.h" - -#include <cassert> -#include <cstdint> - -ABC_NAMESPACE_CXX_HEADER_START - -namespace CaDiCaL { - -// This is Donald Knuth's version of the Luby restart sequence which he -// called 'reluctant doubling'. His bit-twiddling formulation in line (DK) -// requires to keep two words around which are updated every time the -// reluctant doubling sequence is advanced. The original version in the -// literature uses a complex recursive function which computes the length of -// the next inactive sub-sequence every time (but is state-less). -// -// In our code we incorporate a base interval 'period' and only after period -// many calls to 'tick' times the current sequence value we update the -// reluctant doubling sequence value. The 'tick' call is decoupled from -// the activation signal of the sequence (the 'bool ()' operator) through -// 'trigger'. It is also possible to set an upper limit to the length of an -// inactive sub-sequence. If that limit is reached the whole reluctant -// doubling sequence starts over with the initial values. - -class Reluctant { - - uint64_t u, v, limit; - uint64_t period, countdown; - bool trigger, limited; - -public: - Reluctant () : period (0), trigger (false) {} - - void enable (int p, int64_t l) { - CADICAL_assert (p > 0); - u = v = 1; - period = countdown = p; - trigger = false; - if (l <= 0) - limited = false; - else - limited = true, limit = l; - }; - - void disable () { period = 0, trigger = false; } - - // Increments the count until the 'period' is hit. Then it performs the - // actual increment of reluctant doubling. This gives the common 'Luby' - // sequence with the specified base interval period. As soon the limit is - // reached (countdown goes to zero) we remember this event and then - // disable updating the reluctant sequence until the signal is delivered. - - void tick () { - - if (!period) - return; // disabled - if (trigger) - return; // already triggered - if (--countdown) - return; // not there yet - - if ((u & -u) == v) - u = u + 1, v = 1; - else - v = 2 * v; // (DK) - - if (limited && v >= limit) - u = v = 1; - countdown = v * period; - trigger = true; - } - - operator bool () { - if (!trigger) - return false; - trigger = false; - return true; - } -}; - -} // namespace CaDiCaL - -ABC_NAMESPACE_CXX_HEADER_END - -#endif diff --git a/src/sat/cadical/resources.hpp b/src/sat/cadical/resources.hpp deleted file mode 100644 index 852668b320..0000000000 --- a/src/sat/cadical/resources.hpp +++ /dev/null @@ -1,22 +0,0 @@ -#ifndef _resources_hpp_INCLUDED -#define _resources_hpp_INCLUDED - -#include "global.h" - -#include <cstdint> - -ABC_NAMESPACE_CXX_HEADER_START - -namespace CaDiCaL { - -double absolute_real_time (); -double absolute_process_time (); - -uint64_t maximum_resident_set_size (); -uint64_t current_resident_set_size (); - -} // namespace CaDiCaL - -ABC_NAMESPACE_CXX_HEADER_END - -#endif // ifndef _resources_hpp_INCLUDED diff --git a/src/sat/cadical/score.hpp b/src/sat/cadical/score.hpp deleted file mode 100644 index 88196121e8..0000000000 --- a/src/sat/cadical/score.hpp +++ /dev/null @@ -1,22 +0,0 @@ -#ifndef _score_hpp_INCLUDED -#define _score_hpp_INCLUDED - -#include "global.h" - -ABC_NAMESPACE_CXX_HEADER_START - -namespace CaDiCaL { - -struct score_smaller { - Internal *internal; - score_smaller (Internal *i) : internal (i) {} - bool operator() (unsigned a, unsigned b); -}; - -typedef heap<score_smaller> ScoreSchedule; - -} // namespace CaDiCaL - -ABC_NAMESPACE_CXX_HEADER_END - -#endif diff --git a/src/sat/cadical/signal.hpp b/src/sat/cadical/signal.hpp deleted file mode 100644 index 1dc2fb3b70..0000000000 --- a/src/sat/cadical/signal.hpp +++ /dev/null @@ -1,39 +0,0 @@ -#ifndef _signal_hpp_INCLUDED -#define _signal_hpp_INCLUDED - -#include "global.h" - -ABC_NAMESPACE_CXX_HEADER_START - -namespace CaDiCaL { - -// Helper class for handling signals in applications. - -class Handler { -public: - Handler () {} - virtual ~Handler () {} - virtual void catch_signal (int sig) = 0; -#ifndef WIN32 - virtual void catch_alarm (); -#endif -}; - -class Signal { - -public: - static void set (Handler *); - static void reset (); -#ifndef WIN32 - static void alarm (int seconds); - static void reset_alarm (); -#endif - - static const char *name (int sig); -}; - -} // namespace CaDiCaL - -ABC_NAMESPACE_CXX_HEADER_END - -#endif diff --git a/src/sat/cadical/stack.h b/src/sat/cadical/stack.h deleted file mode 100644 index 89f270388a..0000000000 --- a/src/sat/cadical/stack.h +++ /dev/null @@ -1,116 +0,0 @@ -#ifndef _stack_h_INCLUDED -#define _stack_h_INCLUDED - -#include "global.h" - -#include <stdlib.h> - -ABC_NAMESPACE_HEADER_START - -#define STACK(TYPE) \ - struct { \ - TYPE *begin; \ - TYPE *end; \ - TYPE *allocated; \ - } - -#define FULL_STACK(S) ((S).end == (S).allocated) -#define EMPTY_STACK(S) ((S).begin == (S).end) -#define SIZE_STACK(S) ((size_t) ((S).end - (S).begin)) -#define CAPACITY_STACK(S) ((size_t) ((S).allocated - (S).begin)) - -#define INIT_STACK(S) \ - do { \ - (S).begin = (S).end = (S).allocated = 0; \ - } while (0) - -#define TOP_STACK(S) (END_STACK (S)[CADICAL_assert (!EMPTY_STACK (S)), -1]) - -#define PEEK_STACK(S, N) \ - (BEGIN_STACK (S)[CADICAL_assert ((N) < SIZE_STACK (S)), (N)]) - -#define POKE_STACK(S, N, E) \ - do { \ - PEEK_STACK (S, N) = (E); \ - } while (0) - -#define POP_STACK(S) (CADICAL_assert (!EMPTY_STACK (S)), *--(S).end) - -#define PUSH_STACK(S, E) \ - do { \ - if (FULL_STACK (S)) \ - ENLARGE_STACK (S); \ - *(S).end++ = (E); \ - } while (0) - -#define BEGIN_STACK(S) (S).begin - -#define END_STACK(S) (S).end - -#define CLEAR_STACK(S) \ - do { \ - (S).end = (S).begin; \ - } while (0) - -#define RESIZE_STACK(S, NEW_SIZE) \ - do { \ - const size_t TMP_NEW_SIZE = (NEW_SIZE); \ - CADICAL_assert (TMP_NEW_SIZE <= SIZE_STACK (S)); \ - (S).end = (S).begin + TMP_NEW_SIZE; \ - } while (0) - -#define SET_END_OF_STACK(S, P) \ - do { \ - CADICAL_assert (BEGIN_STACK (S) <= (P)); \ - CADICAL_assert ((P) <= END_STACK (S)); \ - if ((P) == END_STACK (S)) \ - break; \ - (S).end = (P); \ - } while (0) - -#define RELEASE_STACK(S) \ - do { \ - DEALLOC ((S).begin, CAPACITY_STACK (S)); \ - INIT_STACK (S); \ - } while (0) - -#define REMOVE_STACK(T, S, E) \ - do { \ - CADICAL_assert (!EMPTY_STACK (S)); \ - T *END_REMOVE_STACK = END_STACK (S); \ - T *P_REMOVE_STACK = BEGIN_STACK (S); \ - while (*P_REMOVE_STACK != (E)) { \ - P_REMOVE_STACK++; \ - CADICAL_assert (P_REMOVE_STACK != END_REMOVE_STACK); \ - } \ - P_REMOVE_STACK++; \ - while (P_REMOVE_STACK != END_REMOVE_STACK) { \ - P_REMOVE_STACK[-1] = *P_REMOVE_STACK; \ - P_REMOVE_STACK++; \ - } \ - (S).end--; \ - } while (0) - -#define all_stack(T, E, S) \ - T E, *E##_PTR = BEGIN_STACK (S), *const E##_END = END_STACK (S); \ - E##_PTR != E##_END && (E = *E##_PTR, true); \ - ++E##_PTR - -#define all_pointers(T, E, S) \ - T *E, *const *E##_PTR = BEGIN_STACK (S), \ - *const *const E##_END = END_STACK (S); \ - E##_PTR != E##_END && (E = *E##_PTR, true); \ - ++E##_PTR - -// clang-format off - -typedef STACK (char) chars; -typedef STACK (int) ints; -typedef STACK (size_t) sizes; -typedef STACK (unsigned) unsigneds; - -// clang-format on - -ABC_NAMESPACE_HEADER_END - -#endif diff --git a/src/sat/cadical/stats.hpp b/src/sat/cadical/stats.hpp deleted file mode 100644 index 4a6afa9c9f..0000000000 --- a/src/sat/cadical/stats.hpp +++ /dev/null @@ -1,376 +0,0 @@ -#ifndef _stats_hpp_INCLUDED -#define _stats_hpp_INCLUDED - -#include "global.h" - -#include <cstdint> -#include <cstdlib> -#include <vector> - -ABC_NAMESPACE_CXX_HEADER_START - -namespace CaDiCaL { - -struct Internal; - -struct Stats { - - Internal *internal; - - int64_t vars = 0; // internal initialized variables - - int64_t conflicts = 0; // generated conflicts in 'propagate' - int64_t decisions = 0; // number of decisions in 'decide' - - struct { - int64_t cover = 0; // propagated during covered clause elimination - int64_t instantiate = 0; // propagated during variable instantiation - int64_t probe = 0; // propagated during probing - int64_t search = 0; // propagated literals during search - int64_t transred = 0; // propagated during transitive reduction - int64_t vivify = 0; // propagated during vivification - int64_t walk = 0; // propagated during local search - } propagations; - - struct { - int64_t search[2] = {0}; - int64_t factor = 0; - int64_t probe = 0; - int64_t sweep = 0; - int64_t ternary = 0; - int64_t vivify = 0; - } ticks; - - struct { - int64_t ext_cb = 0; // number of times any external callback was called - int64_t eprop_call = 0; // number of times external_propagate was called - int64_t eprop_prop = 0; // number of times external propagate propagated - int64_t eprop_conf = - 0; // number of times ex-propagate was already falsified - int64_t eprop_expl = - 0; // number of times external propagate was explained - int64_t elearn_call = - 0; // number of times external clause learning was tried - int64_t elearned = - 0; // learned external clauses (incl. eprop explanations) - int64_t elearn_prop = - 0; // number of learned and propagating external clauses - int64_t elearn_conf = - 0; // number of learned and conflicting external clauses - int64_t echeck_call = 0; // number of checking found complete solutions - } ext_prop; - - int64_t condassinit = 0; // initial assigned literals - int64_t condassirem = 0; // initial assigned literals for blocked - int64_t condassrem = 0; // remaining assigned literals for blocked - int64_t condassvars = 0; // sum of active variables at initial assignment - int64_t condautinit = 0; // initial literals in autarky part - int64_t condautrem = 0; // remaining literals in autarky part for blocked - int64_t condcands = 0; // globally blocked candidate clauses - int64_t condcondinit = 0; // initial literals in conditional part - int64_t condcondrem = - 0; // remaining literals in conditional part for blocked - int64_t conditioned = 0; // globally blocked clauses eliminated - int64_t conditionings = 0; // globally blocked clause eliminations - int64_t condprops = 0; // propagated unassigned literals - - struct { - int64_t block = 0; // block marked literals - int64_t elim = 0; // elim marked variables - int64_t subsume = 0; // subsume marked variables - int64_t ternary = 0; // ternary marked variables - int64_t factor = 0; - } mark; - - struct { - int64_t total = 0; - int64_t redundant = 0; - int64_t irredundant = 0; - } current, added; // Clauses. - - struct { - double process = 0, real = 0; - } time; - - struct { - int64_t count = 0; // number of covered clause elimination rounds - int64_t asymmetric = 0; // number of asymmetric tautologies in CCE - int64_t blocked = 0; // number of blocked covered tautologies - int64_t total = 0; // total number of eliminated clauses - } cover; - - struct { - int64_t tried = 0; - int64_t succeeded = 0; - struct { - int64_t one = 0, zero = 0; - } constant, forward, backward; - struct { - int64_t positive = 0, negative = 0; - } horn; - } lucky; - - struct { - int64_t total = 0; // total number of happened rephases - int64_t best = 0; // how often reset to best phases - int64_t flipped = 0; // how often reset phases by flipping - int64_t inverted = 0; // how often reset to inverted phases - int64_t original = 0; // how often reset to original phases - int64_t random = 0; // how often randomly reset phases - int64_t walk = 0; // phases improved through random walked - } rephased; - - struct { - int64_t count = 0; - int64_t broken = 0; - int64_t flips = 0; - int64_t minimum = 0; - } walk; - - struct { - int64_t count = 0; // flushings of learned clauses counter - int64_t learned = 0; // flushed learned clauses - int64_t hyper = 0; // flushed hyper binary/ternary clauses - } flush; - - int64_t compacts = 0; // number of compactifications - int64_t shuffled = 0; // shuffled queues and scores - int64_t restarts = 0; // actual number of happened restarts - int64_t restartlevels = 0; // levels at restart - int64_t restartstable = 0; // actual number of happened restarts - int64_t stabphases = 0; // number of stabilization phases - int64_t stabconflicts = - 0; // number of search conflicts during stabilizing - int64_t rescored = 0; // number of times scores were rescored - int64_t reused = 0; // number of reused trails - int64_t reusedlevels = 0; // reused levels at restart - int64_t reusedstable = 0; // number of reused trails during stabilizing - int64_t sections = 0; // 'section' counter - int64_t chrono = 0; // chronological backtracks - int64_t backtracks = 0; // number of backtracks - int64_t improvedglue = 0; // improved glue during bumping - int64_t promoted1 = 0; // promoted clauses to tier one - int64_t promoted2 = 0; // promoted clauses to tier two - int64_t bumped = 0; // seen and bumped variables in 'analyze' - int64_t recomputed = 0; // recomputed glues 'recompute_glue' - int64_t searched = 0; // searched decisions in 'decide' - int64_t reductions = 0; // 'reduce' counter - int64_t reduced = 0; // number of reduced clauses - int64_t reduced_sqrt = 0; - int64_t reduced_prct = 0; - int64_t collected = 0; // number of collected bytes - int64_t collections = 0; // number of garbage collections - int64_t hbrs = 0; // hyper binary resolvents - int64_t hbrsizes = 0; // sum of hyper resolved base clauses - int64_t hbreds = 0; // redundant hyper binary resolvents - int64_t hbrsubs = 0; // subsuming hyper binary resolvents - int64_t instried = 0; // number of tried instantiations - int64_t instantiated = 0; // number of successful instantiations - int64_t instrounds = 0; // number of instantiation rounds - int64_t subsumed = 0; // number of subsumed clauses - int64_t deduplicated = 0; // number of removed duplicated binary clauses - int64_t deduplications = 0; // number of deduplication phases - int64_t strengthened = 0; // number of strengthened clauses - - int64_t eliminated_equi = - 0; // number of successful equivalence eliminations - int64_t eliminated_and = 0; // number of successful AND gate eliminations - int64_t eliminated_ite = 0; // number of successful ITE gate eliminations - int64_t eliminated_xor = 0; // number of successful XOR gate eliminations - int64_t eliminated_def = - 0; // number of successful definition eliminations - - int64_t definitions_checked = 0; - int64_t definitions_extracted = 0; - int64_t definition_units = 0; - int64_t definition_ticks = 0; - - int64_t factor = 0; - int64_t factored = 0; - int64_t factor_added = 0; - int64_t variables_extension = 0; - int64_t variables_original = 0; - int64_t literals_factored = 0; - int64_t clauses_unfactored = 0; - int64_t literals_unfactored = 0; - - int64_t elimotfstr = - 0; // number of on-the-fly strengthened during elimination - int64_t subirr = 0; // number of subsumed irredundant clauses - int64_t subred = 0; // number of subsumed redundant clauses - int64_t subtried = 0; // number of tried subsumptions - int64_t subchecks = 0; // number of pair-wise subsumption checks - int64_t subchecks2 = 0; // same but restricted to binary clauses - int64_t elimotfsub = - 0; // number of on-the-fly subsumed during elimination - int64_t subsumerounds = 0; // number of subsumption rounds - int64_t subsumephases = 0; // number of scheduled subsumption phases - int64_t eagertried = 0; // number of traversed eager subsumed candidates - int64_t eagersub = - 0; // number of eagerly subsumed recently learned clauses - int64_t elimres = 0; // number of resolved clauses in BVE - int64_t elimrestried = 0; // number of tried resolved clauses in BVE - int64_t elimfastrounds = 0; // number of elimination rounds - int64_t elimrounds = 0; // number of elimination rounds - int64_t elimphases = 0; // number of scheduled elimination phases - int64_t elimfastphases = 0; // number of scheduled elimination phases - int64_t elimcompleted = 0; // number complete elimination procedures - int64_t elimtried = 0; // number of variable elimination attempts - int64_t elimsubst = 0; // number of eliminations through substitutions - int64_t elimgates = 0; // number of gates found during elimination - int64_t elimequivs = 0; // number of equivalences found during elimination - int64_t elimands = 0; // number of AND gates found during elimination - int64_t elimites = 0; // number of ITE gates found during elimination - int64_t elimxors = 0; // number of XOR gates found during elimination - int64_t elimbwsub = 0; // number of eager backward subsumed clauses - int64_t elimbwstr = 0; // number of eager backward strengthened clauses - int64_t ternary = 0; // number of ternary resolution phases - int64_t ternres = 0; // number of ternary resolutions - int64_t htrs = 0; // number of hyper ternary resolvents - int64_t htrs2 = 0; // number of binary hyper ternary resolvents - int64_t htrs3 = 0; // number of ternary hyper ternary resolvents - int64_t decompositions = 0; // number of SCC + ELS - int64_t vivifications = 0; // number of vivifications - int64_t vivifychecks = 0; // checked clauses during vivification - int64_t vivifydecs = 0; // vivification decisions - int64_t vivifyreused = 0; // reused vivification decisions - int64_t vivifysched = 0; // scheduled clauses for vivification - int64_t vivifysubs = 0; // subsumed clauses during vivification - int64_t vivifysubred = 0; // subsumed clauses during vivification - int64_t vivifysubirr = 0; // subsumed clauses during vivification - int64_t vivifystrs = 0; // strengthened clauses during vivification - int64_t vivifystrirr = 0; // strengthened irredundant clause - int64_t vivifystred1 = 0; // strengthened redundant clause (1) - int64_t vivifystred2 = 0; // strengthened redundant clause (2) - int64_t vivifystred3 = 0; // strengthened redundant clause (3) - int64_t vivifyunits = 0; // units during vivification - int64_t vivifyimplied = 0; // implied during vivification - int64_t vivifyinst = 0; // instantiation during vivification - int64_t vivifydemote = 0; // demoting during vivification - int64_t transreds = 0; - int64_t transitive = 0; - struct { - int64_t literals = 0; - int64_t clauses = 0; - } learned; - int64_t minimized = 0; // minimized literals - int64_t shrunken = 0; // shrunken literals - int64_t minishrunken = 0; // shrunken during minimization literals - - int64_t irrlits = 0; // literals in irredundant clauses - struct { - int64_t bytes = 0; - int64_t clauses = 0; - int64_t literals = 0; - } garbage; - - int64_t sweep_units = 0; - int64_t sweep_flip_backbone = 0; - int64_t sweep_fixed_backbone = 0; - int64_t sweep_flipped_backbone = 0; - int64_t sweep_solved_backbone = 0; - int64_t sweep_sat_backbone = 0; - int64_t sweep_unsat_backbone = 0; - int64_t sweep_unknown_backbone = 0; - int64_t sweep_flip_equivalences = 0; - int64_t sweep_flipped_equivalences = 0; - int64_t sweep_sat_equivalences = 0; - int64_t sweep_unsat_equivalences = 0; - int64_t sweep_unknown_equivalences = 0; - int64_t sweep_solved_equivalences = 0; - int64_t sweep_equivalences = 0; - int64_t sweep_variables = 0; - int64_t sweep_completed = 0; - int64_t sweep_solved = 0; - int64_t sweep_sat = 0; - int64_t sweep_unsat = 0; - int64_t sweep_depth = 0; - int64_t sweep_environment = 0; - int64_t sweep_clauses = 0; - int64_t sweep = 0; - - int64_t units = 0; // learned unit clauses - int64_t binaries = 0; // learned binary clauses - int64_t inprobingphases = 0; // number of scheduled probing phases - int64_t probingrounds = 0; // number of probing rounds - int64_t inprobesuccess = 0; // number successful probing phases - int64_t probed = 0; // number of probed literals - int64_t failed = 0; // number of failed literals - int64_t hyperunary = 0; // hyper unary resolved unit clauses - int64_t probefailed = 0; // failed literals from probing - int64_t transredunits = 0; // units derived in transitive reduction - int64_t blockings = 0; // number of blocked clause eliminations - int64_t blocked = 0; // number of actually blocked clauses - int64_t blockres = 0; // number of resolutions during blocking - int64_t blockcands = 0; // number of clause / pivot pairs tried - int64_t blockpured = 0; // number of clauses blocked through pure literals - int64_t blockpurelits = 0; // number of pure literals - int64_t extensions = 0; // number of extended witnesses - int64_t extended = 0; // number of flipped literals during extension - int64_t weakened = 0; // number of clauses pushed to extension stack - int64_t weakenedlen = 0; // lengths of weakened clauses - int64_t restorations = 0; // number of restore calls - int64_t restored = 0; // number of restored clauses - int64_t reactivated = 0; // number of reactivated clauses - int64_t restoredlits = 0; // number of restored literals - - int64_t preprocessings = 0; - - int64_t ilbtriggers = 0; - int64_t ilbsuccess = 0; - int64_t levelsreused = 0; - int64_t literalsreused = 0; - int64_t assumptionsreused = 0; - int64_t tierecomputed = 0; // number of tier recomputation; - - struct { - int64_t fixed = 0; // number of top level assigned variables - int64_t eliminated = 0; // number of eliminated variables - int64_t fasteliminated = 0; // number of fast eliminated variables only - int64_t substituted = 0; // number of substituted variables - int64_t pure = 0; // number of pure literals - } all, now; - - struct { - int64_t strengthened = 0; // number of clauses strengthened during OTFS - int64_t subsumed = 0; // number of clauses subsumed by OTFS - } otfs; - - int64_t unused = 0; // number of unused variables - int64_t active = 0; // number of active variables - int64_t inactive = 0; // number of inactive variables - std::vector<uint64_t> bump_used = {0, 0}; - std::vector<std::vector<uint64_t>> used; // used clauses in focused mode - - struct { - int64_t gates = 0; - int64_t ands = 0; - int64_t ites = 0; - int64_t xors = 0; - int64_t units = 0; - int64_t congruent = 0; - int64_t rounds = 0; - int64_t unary_and = 0; - int64_t unaries = 0; - int64_t rewritten_ands = 0; - int64_t simplified = 0; - int64_t simplified_ands = 0; - int64_t simplified_xors = 0; - int64_t simplified_ites = 0; - int64_t subsumed = 0; - int64_t trivial_ite = 0; - int64_t unary_ites = 0; - } congruence; - - Stats (); - - void print (Internal *); -}; - -/*------------------------------------------------------------------------*/ - -} // namespace CaDiCaL - -ABC_NAMESPACE_CXX_HEADER_END - -#endif diff --git a/src/sat/cadical/sweep.hpp b/src/sat/cadical/sweep.hpp deleted file mode 100644 index ad093e070a..0000000000 --- a/src/sat/cadical/sweep.hpp +++ /dev/null @@ -1,67 +0,0 @@ -#ifndef _sweep_hpp_INCLUDED -#define _sweep_hpp_INCLUDED - -#include "global.h" - -ABC_NAMESPACE_CXX_HEADER_START - -namespace CaDiCaL { - -struct Internal; - -struct sweep_proof_clause { - unsigned sweep_id; // index for sweeper.clauses - int64_t cad_id; // cadical id - unsigned kit_id; // cadical_kitten id - bool learned; - vector<int> literals; - vector<unsigned> chain; // lrat -}; - -struct sweep_blocked_clause { - int blit; - int64_t id; - vector<int> literals; -}; - -struct sweep_binary { - int lit; - int other; - int64_t id; -}; - -struct Sweeper { - Sweeper (Internal *internal); - ~Sweeper (); - Internal *internal; - Random random; - vector<unsigned> depths; - int *reprs; - vector<int> next, prev; - int first, last, blit; - unsigned encoded; - unsigned save; - vector<int> vars; - vector<Clause *> clauses; - vector<sweep_blocked_clause> blocked_clauses; - bool flush_blocked_clauses; - vector<int> blockable; - vector<int> clause; - vector<int> propagate; - vector<int> backbone; - vector<int> partition; - vector<bool> prev_units; - vector<sweep_binary> binaries; - vector<sweep_proof_clause> core[2]; - uint64_t current_ticks; - struct { - uint64_t ticks; - unsigned clauses, depth, vars; - } limit; -}; - -} // namespace CaDiCaL - -ABC_NAMESPACE_CXX_HEADER_END - -#endif diff --git a/src/sat/cadical/terminal.hpp b/src/sat/cadical/terminal.hpp deleted file mode 100644 index a99346b7cd..0000000000 --- a/src/sat/cadical/terminal.hpp +++ /dev/null @@ -1,104 +0,0 @@ -#ifndef _terminal_hpp_INCLUDED -#define _terminal_hpp_INCLUDED - -#include "global.h" - -ABC_NAMESPACE_CXX_HEADER_START - -namespace CaDiCaL { - -class Terminal { - - FILE *file; // 'stdout' or 'stderr' - bool connected; // Connected to terminal. - bool use_colors; // Use colors. - bool reset_on_exit; // Reset on exit. - - void escape () { - CADICAL_assert (connected); - fputs ("\033[", file); - } - - void color (int color, bool bright) { - if (!use_colors) - return; - CADICAL_assert (connected); - escape (); - fputc (bright ? '1' : '0', file); - fprintf (file, ";%dm", color); - fflush (file); - } - - void code (const char *str) { - if (!use_colors) - return; - if (!connected) - return; - escape (); - fputs (str, file); - fflush (file); - } - -public: - Terminal (FILE *file); - ~Terminal (); - - void disable (); // Assume disconnected in any case. - void force_colors (); - void force_no_colors (); - void force_reset_on_exit (); - - bool colors () { return use_colors; } - - operator bool () const { return connected; } - - void red (bool bright = false) { color (31, bright); } - void green (bool bright = false) { color (32, bright); } - void yellow (bool bright = false) { color (33, bright); } - void blue (bool bright = false) { color (34, bright); } - void magenta (bool bright = false) { color (35, bright); } - void black (bool bright = false) { color (90, bright); } - void cyan (bool bright = false) { color (96, bright); } - - void bold () { code ("1m"); } - void normal () { code ("0m"); } - void inverse () { code ("7m"); } - void underline () { code ("4m"); } - -#define MODIFY(CODE) (use_colors ? "\033[" CODE "m" : "") - - const char *bright_magenta_code () { return MODIFY ("1;35"); } - const char *magenta_code () { return MODIFY ("0;35"); } - const char *blue_code () { return MODIFY ("0;34"); } - const char *bright_blue_code () { return MODIFY ("1;34"); } - const char *yellow_code () { return MODIFY ("0;33"); } - const char *bright_yellow_code () { return MODIFY ("1;33"); } - const char *green_code () { return MODIFY ("0;32"); } - const char *red_code () { return MODIFY ("0;31"); } - const char *cyan_code () { return MODIFY ("0;96"); } - const char *bright_red_code () { return MODIFY ("1;31"); } - const char *normal_code () { return MODIFY ("0"); } - const char *bold_code () { return MODIFY ("1"); } - - void cursor (bool on) { code (on ? "?25h" : "?25l"); } - - void erase_until_end_of_line () { code ("K"); } - - void erase_line_if_connected_otherwise_new_line () { - if (connected) - code ("1G"); - else - fputc ('\n', file), fflush (file); - } - - void reset (); -}; - -extern Terminal tout; // Terminal of 'stdout' (file descriptor '1') -extern Terminal terr; // Terminal of 'stderr' (file descriptor '2') - -} // namespace CaDiCaL - -ABC_NAMESPACE_CXX_HEADER_END - -#endif diff --git a/src/sat/cadical/testing.hpp b/src/sat/cadical/testing.hpp deleted file mode 100644 index f90569e5c5..0000000000 --- a/src/sat/cadical/testing.hpp +++ /dev/null @@ -1,30 +0,0 @@ -#ifndef _testing_hpp_INCLUDED -#define _testing_hpp_INCLUDED - -#include "global.h" - -// This class provides access to 'internal' which should only be used for -// internal testing and not for any other purpose as 'internal' is supposed -// to be hidden. - -#include "cadical.hpp" - -ABC_NAMESPACE_CXX_HEADER_START - -namespace CaDiCaL { - -class Testing { - Solver *solver; - -public: - Testing (Solver &s) : solver (&s) {} - Testing (Solver *s) : solver (s) {} - Internal *internal () { return solver->internal; } - External *external () { return solver->external; } -}; - -} // namespace CaDiCaL - -ABC_NAMESPACE_CXX_HEADER_END - -#endif diff --git a/src/sat/cadical/tracer.hpp b/src/sat/cadical/tracer.hpp deleted file mode 100644 index d1a9f1137b..0000000000 --- a/src/sat/cadical/tracer.hpp +++ /dev/null @@ -1,183 +0,0 @@ -#ifndef _tracer_hpp_INCLUDED -#define _tracer_hpp_INCLUDED - -#include "global.h" - -#include <cstdint> -#include <vector> - -ABC_NAMESPACE_CXX_HEADER_START - -namespace CaDiCaL { - -struct Internal; - -enum ConclusionType { CONFLICT = 1, ASSUMPTIONS = 2, CONSTRAINT = 4 }; - -// Proof tracer class to observer all possible proof events, -// such as added or deleted clauses. -// An implementation can decide on which events to act. -// -class Tracer { - -public: - Tracer () {} - virtual ~Tracer () {} - - /*------------------------------------------------------------------------*/ - /* */ - /* Basic Events */ - /* */ - /*------------------------------------------------------------------------*/ - - // Notify the tracer that a original clause has been added. - // Includes ID and whether the clause is redundant or irredundant - // Arguments: ID, redundant, clause, restored - // - virtual void add_original_clause (int64_t, bool, const std::vector<int> &, - bool = false) {} - - // Notify the observer that a new clause has been derived. - // Includes ID and whether the clause is redundant or irredundant - // If antecedents are derived they will be included here. - // Arguments: ID, redundant, clause, antecedents - // - virtual void add_derived_clause (int64_t, bool, const std::vector<int> &, - const std::vector<int64_t> &) {} - - // Notify the observer that a clause is deleted. - // Includes ID and redundant/irredundant - // Arguments: ID, redundant, clause - // - virtual void delete_clause (int64_t, bool, const std::vector<int> &) {} - - // Notify the observer that a clause is deleted. - // Includes ID and redundant/irredundant - // Arguments: ID, redundant, clause - // - virtual void demote_clause (uint64_t, const std::vector<int> &) {} - - // Notify the observer to remember that the clause might be restored later - // Arguments: ID, clause - // - virtual void weaken_minus (int64_t, const std::vector<int> &) {} - - // Notify the observer that a clause is strengthened - // Arguments: ID - // - virtual void strengthen (int64_t) {} - - // Notify the observer that the solve call ends with status StatusType - // If the status is UNSAT and an empty clause has been derived, the second - // argument will contain its id. - // Note that the empty clause is already added through add_derived_clause - // and finalized with finalize_clause - // Arguments: int, ID - // - virtual void report_status (int, int64_t) {} - - /*------------------------------------------------------------------------*/ - /* */ - /* Specifically non-incremental */ - /* */ - /*------------------------------------------------------------------------*/ - - // Notify the observer that a clause is finalized. - // Arguments: ID, clause - // - virtual void finalize_clause (int64_t, const std::vector<int> &) {} - - // Notify the observer that the proof begins with a set of reserved ids - // for original clauses. Given ID is the first derived clause ID. - // Arguments: ID - // - virtual void begin_proof (int64_t) {} - - /*------------------------------------------------------------------------*/ - /* */ - /* Specifically incremental */ - /* */ - /*------------------------------------------------------------------------*/ - - // Notify the observer that an assumption has been added - // Arguments: assumption_literal - // - virtual void solve_query () {} - - // Notify the observer that an assumption has been added - // Arguments: assumption_literal - // - virtual void add_assumption (int) {} - - // Notify the observer that a constraint has been added - // Arguments: constraint_clause - // - virtual void add_constraint (const std::vector<int> &) {} - - // Notify the observer that assumptions and constraints are reset - // - virtual void reset_assumptions () {} - - // Notify the observer that this clause could be derived, which - // is the negation of a core of failing assumptions/constraints. - // If antecedents are derived they will be included here. - // Arguments: ID, clause, antecedents - // - virtual void add_assumption_clause (int64_t, const std::vector<int> &, - const std::vector<int64_t> &) {} - - // Notify the observer that conclude unsat was requested. - // will give either the id of the empty clause, the id of a failing - // assumption clause or the ids of the failing constrain clauses - // Arguments: conclusion_type, clause_ids - // - virtual void conclude_unsat (ConclusionType, - const std::vector<int64_t> &) {} - - // Notify the observer that conclude sat was requested. - // will give the complete model as a vector. - // - virtual void conclude_sat (const std::vector<int> &) {} - - // Notify the observer that conclude unknown was requested. - // will give the current trail as a vector. - // - virtual void conclude_unknown (const std::vector<int> &) {} -}; - -/*--------------------------------------------------------------------------*/ - -// Following tracers for internal use. - -struct InternalTracer : public Tracer { -public: - InternalTracer () {} - virtual ~InternalTracer () {} - - virtual void connect_internal (Internal *) {} -}; - -class StatTracer : public InternalTracer { -public: - StatTracer () {} - virtual ~StatTracer () {} - - virtual void print_stats () {} -}; - -class FileTracer : public InternalTracer { - -public: - FileTracer () {} - virtual ~FileTracer () {} - - virtual bool closed () = 0; - virtual void close (bool print = false) = 0; - virtual void flush (bool print = false) = 0; -}; - -} // namespace CaDiCaL - -ABC_NAMESPACE_CXX_HEADER_END - -#endif diff --git a/src/sat/cadical/util.hpp b/src/sat/cadical/util.hpp deleted file mode 100644 index 3882c6ede8..0000000000 --- a/src/sat/cadical/util.hpp +++ /dev/null @@ -1,173 +0,0 @@ -#ifndef _util_hpp_INCLUDED -#define _util_hpp_INCLUDED - -#include "global.h" - -#include <cassert> -#include <cstdint> -#include <vector> - -ABC_NAMESPACE_CXX_HEADER_START - -namespace CaDiCaL { - -using namespace std; - -// Common simple utility functions independent from 'Internal'. - -/*------------------------------------------------------------------------*/ - -inline double relative (double a, double b) { return b ? a / b : 0; } -inline double percent (double a, double b) { return relative (100 * a, b); } -inline int sign (int lit) { return (lit > 0) - (lit < 0); } -inline unsigned bign (int lit) { return 1 + (lit < 0); } - -/*------------------------------------------------------------------------*/ - -bool has_suffix (const char *str, const char *suffix); -bool has_prefix (const char *str, const char *prefix); - -/*------------------------------------------------------------------------*/ - -// Parse integer string in the form of -// -// true -// false -// [-]<mantissa>[e<exponent>] -// -// and in the latter case '<val>' has to be within [-INT_MAX,INT_MAX]. -// -// The function returns true if parsing is successful and then also sets -// the second argument to the parsed value. - -bool parse_int_str (const char *str, int &); - -/*------------------------------------------------------------------------*/ - -inline bool is_power_of_two (unsigned n) { return n && !(n & (n - 1)); } - -inline bool contained (int64_t c, int64_t l, int64_t u) { - return l <= c && c <= u; -} - -inline bool aligned (size_t n, size_t alignment) { - CADICAL_assert (is_power_of_two (alignment)); - const size_t mask = alignment - 1; - return !(n & mask); -} - -inline size_t align (size_t n, size_t alignment) { - size_t res = n; - CADICAL_assert (is_power_of_two (alignment)); - const size_t mask = alignment - 1; - if (res & mask) - res = (res | mask) + 1; - CADICAL_assert (aligned (res, alignment)); - return res; -} - -/*------------------------------------------------------------------------*/ - -inline bool parity (unsigned a) { - CADICAL_assert (sizeof a == 4); - unsigned tmp = a; - tmp ^= (tmp >> 16); - tmp ^= (tmp >> 8); - tmp ^= (tmp >> 4); - tmp ^= (tmp >> 2); - tmp ^= (tmp >> 1); - return tmp & 1; -} - -/*------------------------------------------------------------------------*/ - -// The standard 'Effective STL' way (though not guaranteed) to clear a -// vector and reduce its capacity to zero, thus deallocating all its -// internal memory. This is quite important for keeping the actual -// allocated size of watched and occurrence lists small particularly during -// bounded variable elimination where many clauses are added and removed. - -template <class T> void erase_vector (std::vector<T> &v) { - if (v.capacity ()) { - std::vector<T> ().swap (v); - } - CADICAL_assert (!v.capacity ()); // not guaranteed though -} - -// The standard 'Effective STL' way (though not guaranteed) to shrink the -// capacity of a vector to its size thus kind of releasing all the internal -// excess memory not needed at the moment any more. - -template <class T> void shrink_vector (std::vector<T> &v) { - if (v.capacity () > v.size ()) { - std::vector<T> (v).swap (v); - } - CADICAL_assert (v.capacity () == v.size ()); // not guaranteed though -} - -template <class T> -static void enlarge_init (vector<T> &v, size_t N, const T &i) { - if (v.size () < N) - v.resize (N, i); -} - -template <class T> static void enlarge_only (vector<T> &v, size_t N) { - if (v.size () < N) - v.resize (N, T ()); -} - -template <class T> static void enlarge_zero (vector<T> &v, size_t N) { - enlarge_init (v, N, (const T &) 0); -} - -// Clean-up class for bad_alloc error safety. - -template <typename T> struct DeferDeleteArray { - T *data; - DeferDeleteArray (T *t) : data (t) {} - ~DeferDeleteArray () { delete[] data; } - void release () { data = nullptr; } - void free () { - delete[] data; - data = nullptr; - } -}; - -template <typename T> struct DeferDeletePtr { - T *data; - DeferDeletePtr (T *t) : data (t) {} - ~DeferDeletePtr () { delete data; } - void release () { data = nullptr; } - void free () { - delete data; - data = nullptr; - } -}; - -/*------------------------------------------------------------------------*/ - -template <class T> inline void clear_n (T *base, size_t n) { - memset (base, 0, sizeof (T) * n); -} - -/*------------------------------------------------------------------------*/ - -// These are options both to 'cadical' and 'mobical'. After wasting some -// on not remembering the spelling (British vs American), nor singular vs -// plural and then wanted to use '--color=false', and '--colours=0' too, I -// just factored this out into these two utility functions. - -bool is_color_option (const char *arg); // --color, --colour, ... -bool is_no_color_option (const char *arg); // --no-color, --no-colour, ... - -/*------------------------------------------------------------------------*/ - -uint64_t hash_string (const char *str); - -/*------------------------------------------------------------------------*/ - -} // namespace CaDiCaL - -ABC_NAMESPACE_CXX_HEADER_END - -#endif diff --git a/src/sat/cadical/var.hpp b/src/sat/cadical/var.hpp deleted file mode 100644 index 8793afbc4c..0000000000 --- a/src/sat/cadical/var.hpp +++ /dev/null @@ -1,28 +0,0 @@ -#ifndef _var_hpp_INCLUDED -#define _var_hpp_INCLUDED - -#include "global.h" - -ABC_NAMESPACE_CXX_HEADER_START - -namespace CaDiCaL { - -struct Clause; - -// This structure captures data associated with an assigned variable. - -struct Var { - - // Note that none of these members is valid unless the variable is - // assigned. During unassigning a variable we do not reset it. - - int level; // decision level - int trail; // trail height at assignment - Clause *reason; // implication graph edge during search -}; - -} // namespace CaDiCaL - -ABC_NAMESPACE_CXX_HEADER_END - -#endif diff --git a/src/sat/cadical/veripbtracer.hpp b/src/sat/cadical/veripbtracer.hpp deleted file mode 100644 index b9c47362fe..0000000000 --- a/src/sat/cadical/veripbtracer.hpp +++ /dev/null @@ -1,108 +0,0 @@ -#ifndef _veripbtracer_h_INCLUDED -#define _veripbtracer_h_INCLUDED - -#include "global.h" - -ABC_NAMESPACE_CXX_HEADER_START - -class FileTracer; - -namespace CaDiCaL { - -struct HashId { - HashId *next; // collision chain link for hash table - uint64_t hash; // previously computed full 64-bit hash - int64_t id; // id of clause -}; - -class VeripbTracer : public FileTracer { - - Internal *internal; - File *file; -#ifndef CADICAL_NDEBUG - bool binary; -#endif - bool with_antecedents; - bool checked_deletions; - - // hash table for checked deletions - // - uint64_t num_clauses; // number of clauses in hash table - uint64_t size_clauses; // size of clause hash table - HashId **clauses; // hash table of clauses - - static const unsigned num_nonces = 4; - - uint64_t nonces[num_nonces]; // random numbers for hashing - uint64_t last_hash; // last computed hash value of clause - int64_t last_id; // id of the last added clause - HashId *last_clause; - uint64_t compute_hash (int64_t); // compute and save hash value of clause - - HashId *new_clause (); - void delete_clause (HashId *); - - // Reduce hash value to the actual size. - // - static uint64_t reduce_hash (uint64_t hash, uint64_t size); - - void enlarge_clauses (); // enlarge hash table for clauses - void insert (); // insert clause in hash table - bool - find_and_delete (const int64_t); // find clause position in hash table - -#ifndef CADICAL_QUIET - int64_t added, deleted; -#endif - vector<int64_t> delete_ids; - - void put_binary_zero (); - void put_binary_lit (int external_lit); - void put_binary_id (int64_t id, bool = false); - - // support veriPB - void veripb_add_derived_clause (int64_t, bool redundant, - const vector<int> &clause, - const vector<int64_t> &chain); - void veripb_add_derived_clause (int64_t, bool redundant, - const vector<int> &clause); - void veripb_begin_proof (int64_t reserved_ids); - void veripb_delete_clause (int64_t id, bool redundant); - void veripb_report_status (bool unsat, int64_t conflict_id); - void veripb_strengthen (int64_t); - -public: - // own and delete 'file' - VeripbTracer (Internal *, File *file, bool, bool, bool); - ~VeripbTracer (); - - void connect_internal (Internal *i) override; - void begin_proof (int64_t) override; - - void add_original_clause (int64_t, bool, const vector<int> &, - bool = false) override {} // skip - - void add_derived_clause (int64_t, bool, const vector<int> &, - const vector<int64_t> &) override; - - void delete_clause (int64_t, bool, const vector<int> &) override; - void finalize_clause (int64_t, const vector<int> &) override {} // skip - - void report_status (int, int64_t) override; - - void weaken_minus (int64_t, const vector<int> &) override; - void strengthen (int64_t) override; - -#ifndef CADICAL_QUIET - void print_statistics (); -#endif - bool closed () override; - void close (bool) override; - void flush (bool) override; -}; - -} // namespace CaDiCaL - -ABC_NAMESPACE_CXX_HEADER_END - -#endif diff --git a/src/sat/cadical/version.hpp b/src/sat/cadical/version.hpp deleted file mode 100644 index d63aefa35d..0000000000 --- a/src/sat/cadical/version.hpp +++ /dev/null @@ -1,19 +0,0 @@ -#include "global.h" - -ABC_NAMESPACE_CXX_HEADER_START - -namespace CaDiCaL { - -const char *version (); -const char *copyright (); -const char *authors (); -const char *affiliations (); -const char *signature (); -const char *identifier (); -const char *compiler (); -const char *date (); -const char *flags (); - -} // namespace CaDiCaL - -ABC_NAMESPACE_CXX_HEADER_END diff --git a/src/sat/cadical/vivify.hpp b/src/sat/cadical/vivify.hpp deleted file mode 100644 index a5c6c0e109..0000000000 --- a/src/sat/cadical/vivify.hpp +++ /dev/null @@ -1,68 +0,0 @@ -#ifndef _vivify_hpp_INCLUDED -#define _vivify_hpp_INCLUDED - -#include "global.h" - -#include "util.hpp" - -#include <array> -#include <cstdint> -#include <vector> - -ABC_NAMESPACE_CXX_HEADER_START - -namespace CaDiCaL { - -struct Clause; - -enum class Vivify_Mode { TIER1, TIER2, TIER3, IRREDUNDANT }; - -#define COUNTREF_COUNTS 2 - -struct vivify_ref { - bool vivify; - std::size_t size; - uint64_t count[COUNTREF_COUNTS]; - Clause *clause; -}; - -// In the vivifier structure, we put the schedules in an array in order to -// be able to iterate over them, but we provide the reference to them to -// make sure that you do need to remember the order. -struct Vivifier { - std::array<std::vector<vivify_ref>, 4> refs_schedules; - std::vector<vivify_ref> &refs_schedule_tier1, &refs_schedule_tier2, - &refs_schedule_tier3, &refs_schedule_irred; - std::array<std::vector<Clause *>, 4> schedules; - std::vector<Clause *> &schedule_tier1, &schedule_tier2, &schedule_tier3, - &schedule_irred; - std::vector<int> sorted; - Vivify_Mode tier; - char tag; - int tier1_limit; - int tier2_limit; - int64_t ticks; - std::vector<std::tuple<int, Clause *, bool>> lrat_stack; - Vivifier (Vivify_Mode mode_tier) - : refs_schedule_tier1 (refs_schedules[0]), - refs_schedule_tier2 (refs_schedules[1]), - refs_schedule_tier3 (refs_schedules[2]), - refs_schedule_irred (refs_schedules[3]), - schedule_tier1 (schedules[0]), schedule_tier2 (schedules[1]), - schedule_tier3 (schedules[2]), schedule_irred (schedules[3]), - tier (mode_tier) {} - - void erase () { - erase_vector (refs_schedule_tier1); - erase_vector (refs_schedule_tier2); - erase_vector (refs_schedule_tier3); - erase_vector (refs_schedule_irred); - erase_vector (sorted); - } -}; - -} // namespace CaDiCaL - -ABC_NAMESPACE_CXX_HEADER_END - -#endif diff --git a/src/sat/cadical/watch.hpp b/src/sat/cadical/watch.hpp deleted file mode 100644 index 1acab094b7..0000000000 --- a/src/sat/cadical/watch.hpp +++ /dev/null @@ -1,78 +0,0 @@ -#ifndef _watch_hpp_INCLUDED -#define _watch_hpp_INCLUDED - -#include "global.h" - -#include <cassert> -#include <vector> - -ABC_NAMESPACE_CXX_HEADER_START - -namespace CaDiCaL { - -// Watch lists for CDCL search. The blocking literal (see also comments -// related to 'propagate') is a must and thus combining that with a 64 bit -// pointer will give a 16 byte (8 byte aligned) structure anyhow, which -// means the additional 4 bytes for the size come for free. As alternative -// one could use a 32-bit reference instead of the pointer which would -// however limit the number of clauses to '2^32 - 1'. One would also need -// to use at least one more bit (either taken away from the variable space -// or the clauses) to denote whether the watch is binary. - -// in fashion of Intel Sat 10.4230/LIPIcs.SAT.2022.8 we try to -// guarantee the following invariant: -// For both watches: -// if the watched literal is negatively assigned -// either it will be propagated in the future -// or the corresponding blocking literal is positively assigned -// and its level is smaller than the level of the watched literal -// - -struct Clause; - -struct Watch { - - Clause *clause; - int blit; - int size; - - Watch (int b, Clause *c) : clause (c), blit (b), size (c->size) {} - Watch () {} - - bool binary () const { return size == 2; } -}; - -typedef vector<Watch> Watches; // of one literal - -typedef Watches::iterator watch_iterator; -typedef Watches::const_iterator const_watch_iterator; - -inline void remove_watch (Watches &ws, Clause *clause) { - const auto end = ws.end (); - auto i = ws.begin (); - for (auto j = i; j != end; j++) { - const Watch &w = *i++ = *j; - if (w.clause == clause) - i--; - } - CADICAL_assert (i + 1 == end); - ws.resize (i - ws.begin ()); -} - -// search for the clause and updates the size marked in the watch lists -inline void update_watch_size (Watches &ws, int blit, Clause *conflict) { - bool found = false; - const int size = conflict->size; - for (Watch &w : ws) { - if (w.clause == conflict) - w.size = size, w.blit = blit, found = true; - CADICAL_assert (w.clause->garbage || w.size == 2 || w.clause->size != 2); - } - CADICAL_assert (found), (void) found; -} - -} // namespace CaDiCaL - -ABC_NAMESPACE_CXX_HEADER_END - -#endif From f2d68d590fa6f8fc32295a2edd79afc0d14a1414 Mon Sep 17 00:00:00 2001 From: Miodrag Milanovic <mmicko@gmail.com> Date: Wed, 12 Mar 2025 07:28:47 +0100 Subject: [PATCH 65/79] Fix mingw compilation --- src/sat/kissat/colors.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sat/kissat/colors.c b/src/sat/kissat/colors.c index f45260b297..c6141b7491 100644 --- a/src/sat/kissat/colors.c +++ b/src/sat/kissat/colors.c @@ -1,6 +1,6 @@ #include "colors.h" -#ifdef WIN32 +#if defined(WIN32) && !defined(__MINGW32__) #define isatty _isatty #else #include <unistd.h> From 44a2996f7fc1c7e08f716354103b8b6a66d3d44d Mon Sep 17 00:00:00 2001 From: Miodrag Milanovic <mmicko@gmail.com> Date: Tue, 8 Apr 2025 13:28:06 +0200 Subject: [PATCH 66/79] Revert "Revert addition of CaDiCaL" This reverts commit 5ecc7c333ceeadef9b2f3e4a82f7ec72fa512d86. --- Makefile | 2 +- abclib.dsp | 368 + src/base/abci/abc.c | 3 + src/sat/cadical/LICENSE | 28 + src/sat/cadical/VERSION | 1 + src/sat/cadical/arena.hpp | 111 + src/sat/cadical/averages.hpp | 43 + src/sat/cadical/bins.hpp | 28 + src/sat/cadical/block.hpp | 43 + src/sat/cadical/cadical.hpp | 1351 +++ src/sat/cadical/cadicalSolver.c | 305 + src/sat/cadical/cadicalSolver.h | 76 + src/sat/cadical/cadicalTest.c | 210 + src/sat/cadical/cadical_analyze.cpp | 1285 +++ src/sat/cadical/cadical_arena.cpp | 36 + src/sat/cadical/cadical_assume.cpp | 613 ++ src/sat/cadical/cadical_averages.cpp | 40 + src/sat/cadical/cadical_backtrack.cpp | 179 + src/sat/cadical/cadical_backward.cpp | 237 + src/sat/cadical/cadical_bins.cpp | 28 + src/sat/cadical/cadical_block.cpp | 830 ++ src/sat/cadical/cadical_ccadical.cpp | 211 + src/sat/cadical/cadical_checker.cpp | 654 ++ src/sat/cadical/cadical_clause.cpp | 649 ++ src/sat/cadical/cadical_collect.cpp | 551 ++ src/sat/cadical/cadical_compact.cpp | 557 ++ src/sat/cadical/cadical_condition.cpp | 946 +++ src/sat/cadical/cadical_config.cpp | 107 + src/sat/cadical/cadical_congruence.cpp | 7567 +++++++++++++++++ src/sat/cadical/cadical_constrain.cpp | 70 + src/sat/cadical/cadical_contract.cpp | 33 + src/sat/cadical/cadical_cover.cpp | 710 ++ src/sat/cadical/cadical_decide.cpp | 258 + src/sat/cadical/cadical_decompose.cpp | 739 ++ src/sat/cadical/cadical_deduplicate.cpp | 176 + src/sat/cadical/cadical_definition.cpp | 289 + src/sat/cadical/cadical_drattracer.cpp | 159 + src/sat/cadical/cadical_elim.cpp | 1178 +++ src/sat/cadical/cadical_elimfast.cpp | 576 ++ src/sat/cadical/cadical_ema.cpp | 101 + src/sat/cadical/cadical_extend.cpp | 287 + src/sat/cadical/cadical_external.cpp | 1028 +++ .../cadical/cadical_external_propagate.cpp | 1232 +++ src/sat/cadical/cadical_factor.cpp | 927 ++ src/sat/cadical/cadical_file.cpp | 527 ++ src/sat/cadical/cadical_flags.cpp | 141 + src/sat/cadical/cadical_flip.cpp | 275 + src/sat/cadical/cadical_format.cpp | 95 + src/sat/cadical/cadical_frattracer.cpp | 283 + src/sat/cadical/cadical_gates.cpp | 772 ++ src/sat/cadical/cadical_idruptracer.cpp | 572 ++ src/sat/cadical/cadical_instantiate.cpp | 371 + src/sat/cadical/cadical_internal.cpp | 1196 +++ src/sat/cadical/cadical_ipasir.cpp | 46 + src/sat/cadical/cadical_kitten.c | 2609 ++++++ src/sat/cadical/cadical_lidruptracer.cpp | 662 ++ src/sat/cadical/cadical_limit.cpp | 135 + src/sat/cadical/cadical_logging.cpp | 221 + src/sat/cadical/cadical_lookahead.cpp | 526 ++ src/sat/cadical/cadical_lratchecker.cpp | 835 ++ src/sat/cadical/cadical_lrattracer.cpp | 206 + src/sat/cadical/cadical_lucky.cpp | 440 + src/sat/cadical/cadical_message.cpp | 218 + src/sat/cadical/cadical_minimize.cpp | 230 + src/sat/cadical/cadical_occs.cpp | 58 + src/sat/cadical/cadical_options.cpp | 365 + src/sat/cadical/cadical_parse.cpp | 442 + src/sat/cadical/cadical_phases.cpp | 50 + src/sat/cadical/cadical_probe.cpp | 993 +++ src/sat/cadical/cadical_profile.cpp | 113 + src/sat/cadical/cadical_proof.cpp | 663 ++ src/sat/cadical/cadical_propagate.cpp | 586 ++ src/sat/cadical/cadical_queue.cpp | 96 + src/sat/cadical/cadical_random.cpp | 233 + src/sat/cadical/cadical_reap.cpp | 142 + src/sat/cadical/cadical_reduce.cpp | 284 + src/sat/cadical/cadical_rephase.cpp | 342 + src/sat/cadical/cadical_report.cpp | 302 + src/sat/cadical/cadical_resources.cpp | 166 + src/sat/cadical/cadical_restart.cpp | 165 + src/sat/cadical/cadical_restore.cpp | 273 + src/sat/cadical/cadical_score.cpp | 57 + src/sat/cadical/cadical_shrink.cpp | 513 ++ src/sat/cadical/cadical_signal.cpp | 144 + src/sat/cadical/cadical_solution.cpp | 56 + src/sat/cadical/cadical_solver.cpp | 1764 ++++ src/sat/cadical/cadical_stable.cpp | 39 + src/sat/cadical/cadical_stats.cpp | 826 ++ src/sat/cadical/cadical_subsume.cpp | 652 ++ src/sat/cadical/cadical_sweep.cpp | 1959 +++++ src/sat/cadical/cadical_terminal.cpp | 49 + src/sat/cadical/cadical_ternary.cpp | 456 + src/sat/cadical/cadical_tier.cpp | 59 + src/sat/cadical/cadical_transred.cpp | 259 + src/sat/cadical/cadical_unstable.cpp | 38 + src/sat/cadical/cadical_util.cpp | 135 + src/sat/cadical/cadical_var.cpp | 45 + src/sat/cadical/cadical_veripbtracer.cpp | 400 + src/sat/cadical/cadical_version.cpp | 113 + src/sat/cadical/cadical_vivify.cpp | 1962 +++++ src/sat/cadical/cadical_walk.cpp | 710 ++ src/sat/cadical/cadical_watch.cpp | 132 + src/sat/cadical/ccadical.h | 74 + src/sat/cadical/checker.hpp | 178 + src/sat/cadical/clause.hpp | 194 + src/sat/cadical/config.hpp | 26 + src/sat/cadical/congruence.hpp | 720 ++ src/sat/cadical/contract.hpp | 142 + src/sat/cadical/cover.hpp | 36 + src/sat/cadical/decompose.hpp | 29 + src/sat/cadical/delay.hpp | 44 + src/sat/cadical/drattracer.hpp | 59 + src/sat/cadical/elim.hpp | 59 + src/sat/cadical/ema.hpp | 74 + src/sat/cadical/external.hpp | 467 + src/sat/cadical/factor.hpp | 60 + src/sat/cadical/file.hpp | 221 + src/sat/cadical/flags.hpp | 91 + src/sat/cadical/format.hpp | 42 + src/sat/cadical/frattracer.hpp | 68 + src/sat/cadical/global.h | 21 + src/sat/cadical/heap.hpp | 218 + src/sat/cadical/idruptracer.hpp | 116 + src/sat/cadical/instantiate.hpp | 51 + src/sat/cadical/internal.hpp | 1866 ++++ src/sat/cadical/inttypes.hpp | 30 + src/sat/cadical/ipasir.h | 35 + src/sat/cadical/kitten.h | 95 + src/sat/cadical/level.hpp | 39 + src/sat/cadical/lidruptracer.hpp | 123 + src/sat/cadical/limit.hpp | 162 + src/sat/cadical/logging.hpp | 104 + src/sat/cadical/lratchecker.hpp | 170 + src/sat/cadical/lrattracer.hpp | 63 + src/sat/cadical/message.hpp | 71 + src/sat/cadical/module.make | 91 + src/sat/cadical/occs.hpp | 42 + src/sat/cadical/options.hpp | 422 + src/sat/cadical/parse.hpp | 78 + src/sat/cadical/phases.hpp | 24 + src/sat/cadical/profile.hpp | 280 + src/sat/cadical/proof.hpp | 120 + src/sat/cadical/queue.hpp | 70 + src/sat/cadical/radix.hpp | 186 + src/sat/cadical/random.h | 50 + src/sat/cadical/random.hpp | 104 + src/sat/cadical/range.hpp | 105 + src/sat/cadical/reap.hpp | 34 + src/sat/cadical/reluctant.hpp | 88 + src/sat/cadical/resources.hpp | 22 + src/sat/cadical/score.hpp | 22 + src/sat/cadical/signal.hpp | 39 + src/sat/cadical/stack.h | 116 + src/sat/cadical/stats.hpp | 376 + src/sat/cadical/sweep.hpp | 67 + src/sat/cadical/terminal.hpp | 104 + src/sat/cadical/testing.hpp | 30 + src/sat/cadical/tracer.hpp | 183 + src/sat/cadical/util.hpp | 173 + src/sat/cadical/var.hpp | 28 + src/sat/cadical/veripbtracer.hpp | 108 + src/sat/cadical/version.hpp | 19 + src/sat/cadical/vivify.hpp | 68 + src/sat/cadical/watch.hpp | 78 + 164 files changed, 59697 insertions(+), 1 deletion(-) create mode 100644 src/sat/cadical/LICENSE create mode 100644 src/sat/cadical/VERSION create mode 100644 src/sat/cadical/arena.hpp create mode 100644 src/sat/cadical/averages.hpp create mode 100644 src/sat/cadical/bins.hpp create mode 100644 src/sat/cadical/block.hpp create mode 100644 src/sat/cadical/cadical.hpp create mode 100644 src/sat/cadical/cadicalSolver.c create mode 100644 src/sat/cadical/cadicalSolver.h create mode 100644 src/sat/cadical/cadicalTest.c create mode 100644 src/sat/cadical/cadical_analyze.cpp create mode 100644 src/sat/cadical/cadical_arena.cpp create mode 100644 src/sat/cadical/cadical_assume.cpp create mode 100644 src/sat/cadical/cadical_averages.cpp create mode 100644 src/sat/cadical/cadical_backtrack.cpp create mode 100644 src/sat/cadical/cadical_backward.cpp create mode 100644 src/sat/cadical/cadical_bins.cpp create mode 100644 src/sat/cadical/cadical_block.cpp create mode 100644 src/sat/cadical/cadical_ccadical.cpp create mode 100644 src/sat/cadical/cadical_checker.cpp create mode 100644 src/sat/cadical/cadical_clause.cpp create mode 100644 src/sat/cadical/cadical_collect.cpp create mode 100644 src/sat/cadical/cadical_compact.cpp create mode 100644 src/sat/cadical/cadical_condition.cpp create mode 100644 src/sat/cadical/cadical_config.cpp create mode 100644 src/sat/cadical/cadical_congruence.cpp create mode 100644 src/sat/cadical/cadical_constrain.cpp create mode 100644 src/sat/cadical/cadical_contract.cpp create mode 100644 src/sat/cadical/cadical_cover.cpp create mode 100644 src/sat/cadical/cadical_decide.cpp create mode 100644 src/sat/cadical/cadical_decompose.cpp create mode 100644 src/sat/cadical/cadical_deduplicate.cpp create mode 100644 src/sat/cadical/cadical_definition.cpp create mode 100644 src/sat/cadical/cadical_drattracer.cpp create mode 100644 src/sat/cadical/cadical_elim.cpp create mode 100644 src/sat/cadical/cadical_elimfast.cpp create mode 100644 src/sat/cadical/cadical_ema.cpp create mode 100644 src/sat/cadical/cadical_extend.cpp create mode 100644 src/sat/cadical/cadical_external.cpp create mode 100644 src/sat/cadical/cadical_external_propagate.cpp create mode 100644 src/sat/cadical/cadical_factor.cpp create mode 100644 src/sat/cadical/cadical_file.cpp create mode 100644 src/sat/cadical/cadical_flags.cpp create mode 100644 src/sat/cadical/cadical_flip.cpp create mode 100644 src/sat/cadical/cadical_format.cpp create mode 100644 src/sat/cadical/cadical_frattracer.cpp create mode 100644 src/sat/cadical/cadical_gates.cpp create mode 100644 src/sat/cadical/cadical_idruptracer.cpp create mode 100644 src/sat/cadical/cadical_instantiate.cpp create mode 100644 src/sat/cadical/cadical_internal.cpp create mode 100644 src/sat/cadical/cadical_ipasir.cpp create mode 100644 src/sat/cadical/cadical_kitten.c create mode 100644 src/sat/cadical/cadical_lidruptracer.cpp create mode 100644 src/sat/cadical/cadical_limit.cpp create mode 100644 src/sat/cadical/cadical_logging.cpp create mode 100644 src/sat/cadical/cadical_lookahead.cpp create mode 100644 src/sat/cadical/cadical_lratchecker.cpp create mode 100644 src/sat/cadical/cadical_lrattracer.cpp create mode 100644 src/sat/cadical/cadical_lucky.cpp create mode 100644 src/sat/cadical/cadical_message.cpp create mode 100644 src/sat/cadical/cadical_minimize.cpp create mode 100644 src/sat/cadical/cadical_occs.cpp create mode 100644 src/sat/cadical/cadical_options.cpp create mode 100644 src/sat/cadical/cadical_parse.cpp create mode 100644 src/sat/cadical/cadical_phases.cpp create mode 100644 src/sat/cadical/cadical_probe.cpp create mode 100644 src/sat/cadical/cadical_profile.cpp create mode 100644 src/sat/cadical/cadical_proof.cpp create mode 100644 src/sat/cadical/cadical_propagate.cpp create mode 100644 src/sat/cadical/cadical_queue.cpp create mode 100644 src/sat/cadical/cadical_random.cpp create mode 100644 src/sat/cadical/cadical_reap.cpp create mode 100644 src/sat/cadical/cadical_reduce.cpp create mode 100644 src/sat/cadical/cadical_rephase.cpp create mode 100644 src/sat/cadical/cadical_report.cpp create mode 100644 src/sat/cadical/cadical_resources.cpp create mode 100644 src/sat/cadical/cadical_restart.cpp create mode 100644 src/sat/cadical/cadical_restore.cpp create mode 100644 src/sat/cadical/cadical_score.cpp create mode 100644 src/sat/cadical/cadical_shrink.cpp create mode 100644 src/sat/cadical/cadical_signal.cpp create mode 100644 src/sat/cadical/cadical_solution.cpp create mode 100644 src/sat/cadical/cadical_solver.cpp create mode 100644 src/sat/cadical/cadical_stable.cpp create mode 100644 src/sat/cadical/cadical_stats.cpp create mode 100644 src/sat/cadical/cadical_subsume.cpp create mode 100644 src/sat/cadical/cadical_sweep.cpp create mode 100644 src/sat/cadical/cadical_terminal.cpp create mode 100644 src/sat/cadical/cadical_ternary.cpp create mode 100644 src/sat/cadical/cadical_tier.cpp create mode 100644 src/sat/cadical/cadical_transred.cpp create mode 100644 src/sat/cadical/cadical_unstable.cpp create mode 100644 src/sat/cadical/cadical_util.cpp create mode 100644 src/sat/cadical/cadical_var.cpp create mode 100644 src/sat/cadical/cadical_veripbtracer.cpp create mode 100644 src/sat/cadical/cadical_version.cpp create mode 100644 src/sat/cadical/cadical_vivify.cpp create mode 100644 src/sat/cadical/cadical_walk.cpp create mode 100644 src/sat/cadical/cadical_watch.cpp create mode 100644 src/sat/cadical/ccadical.h create mode 100644 src/sat/cadical/checker.hpp create mode 100644 src/sat/cadical/clause.hpp create mode 100644 src/sat/cadical/config.hpp create mode 100644 src/sat/cadical/congruence.hpp create mode 100644 src/sat/cadical/contract.hpp create mode 100644 src/sat/cadical/cover.hpp create mode 100644 src/sat/cadical/decompose.hpp create mode 100644 src/sat/cadical/delay.hpp create mode 100644 src/sat/cadical/drattracer.hpp create mode 100644 src/sat/cadical/elim.hpp create mode 100644 src/sat/cadical/ema.hpp create mode 100644 src/sat/cadical/external.hpp create mode 100644 src/sat/cadical/factor.hpp create mode 100644 src/sat/cadical/file.hpp create mode 100644 src/sat/cadical/flags.hpp create mode 100644 src/sat/cadical/format.hpp create mode 100644 src/sat/cadical/frattracer.hpp create mode 100644 src/sat/cadical/global.h create mode 100644 src/sat/cadical/heap.hpp create mode 100644 src/sat/cadical/idruptracer.hpp create mode 100644 src/sat/cadical/instantiate.hpp create mode 100644 src/sat/cadical/internal.hpp create mode 100644 src/sat/cadical/inttypes.hpp create mode 100644 src/sat/cadical/ipasir.h create mode 100644 src/sat/cadical/kitten.h create mode 100644 src/sat/cadical/level.hpp create mode 100644 src/sat/cadical/lidruptracer.hpp create mode 100644 src/sat/cadical/limit.hpp create mode 100644 src/sat/cadical/logging.hpp create mode 100644 src/sat/cadical/lratchecker.hpp create mode 100644 src/sat/cadical/lrattracer.hpp create mode 100644 src/sat/cadical/message.hpp create mode 100644 src/sat/cadical/module.make create mode 100644 src/sat/cadical/occs.hpp create mode 100644 src/sat/cadical/options.hpp create mode 100644 src/sat/cadical/parse.hpp create mode 100644 src/sat/cadical/phases.hpp create mode 100644 src/sat/cadical/profile.hpp create mode 100644 src/sat/cadical/proof.hpp create mode 100644 src/sat/cadical/queue.hpp create mode 100644 src/sat/cadical/radix.hpp create mode 100644 src/sat/cadical/random.h create mode 100644 src/sat/cadical/random.hpp create mode 100644 src/sat/cadical/range.hpp create mode 100644 src/sat/cadical/reap.hpp create mode 100644 src/sat/cadical/reluctant.hpp create mode 100644 src/sat/cadical/resources.hpp create mode 100644 src/sat/cadical/score.hpp create mode 100644 src/sat/cadical/signal.hpp create mode 100644 src/sat/cadical/stack.h create mode 100644 src/sat/cadical/stats.hpp create mode 100644 src/sat/cadical/sweep.hpp create mode 100644 src/sat/cadical/terminal.hpp create mode 100644 src/sat/cadical/testing.hpp create mode 100644 src/sat/cadical/tracer.hpp create mode 100644 src/sat/cadical/util.hpp create mode 100644 src/sat/cadical/var.hpp create mode 100644 src/sat/cadical/veripbtracer.hpp create mode 100644 src/sat/cadical/version.hpp create mode 100644 src/sat/cadical/vivify.hpp create mode 100644 src/sat/cadical/watch.hpp diff --git a/Makefile b/Makefile index 2da81734b6..a546f65fdb 100644 --- a/Makefile +++ b/Makefile @@ -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/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/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 \ diff --git a/abclib.dsp b/abclib.dsp index 7f6af6f819..921030b34f 100644 --- a/abclib.dsp +++ b/abclib.dsp @@ -2886,6 +2886,374 @@ 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" diff --git a/src/base/abci/abc.c b/src/base/abci/abc.c index 7ec94e2cb6..c126df352a 100644 --- a/src/base/abci/abc.c +++ b/src/base/abci/abc.c @@ -56155,6 +56155,9 @@ 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; diff --git a/src/sat/cadical/LICENSE b/src/sat/cadical/LICENSE new file mode 100644 index 0000000000..ead29d7a34 --- /dev/null +++ b/src/sat/cadical/LICENSE @@ -0,0 +1,28 @@ +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. diff --git a/src/sat/cadical/VERSION b/src/sat/cadical/VERSION new file mode 100644 index 0000000000..a1bba89215 --- /dev/null +++ b/src/sat/cadical/VERSION @@ -0,0 +1 @@ +2.2.0-rc1 diff --git a/src/sat/cadical/arena.hpp b/src/sat/cadical/arena.hpp new file mode 100644 index 0000000000..522fce36e6 --- /dev/null +++ b/src/sat/cadical/arena.hpp @@ -0,0 +1,111 @@ +#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 diff --git a/src/sat/cadical/averages.hpp b/src/sat/cadical/averages.hpp new file mode 100644 index 0000000000..3ebdcddfaa --- /dev/null +++ b/src/sat/cadical/averages.hpp @@ -0,0 +1,43 @@ +#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 diff --git a/src/sat/cadical/bins.hpp b/src/sat/cadical/bins.hpp new file mode 100644 index 0000000000..aeefeceeb1 --- /dev/null +++ b/src/sat/cadical/bins.hpp @@ -0,0 +1,28 @@ +#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 diff --git a/src/sat/cadical/block.hpp b/src/sat/cadical/block.hpp new file mode 100644 index 0000000000..202355f825 --- /dev/null +++ b/src/sat/cadical/block.hpp @@ -0,0 +1,43 @@ +#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 diff --git a/src/sat/cadical/cadical.hpp b/src/sat/cadical/cadical.hpp new file mode 100644 index 0000000000..c9ed413328 --- /dev/null +++ b/src/sat/cadical/cadical.hpp @@ -0,0 +1,1351 @@ +#ifndef _cadical_hpp_INCLUDED +#define _cadical_hpp_INCLUDED + +#include "global.h" + +#include <cstdint> +#include <cstdio> +#include <vector> + +ABC_NAMESPACE_CXX_HEADER_START + +namespace CaDiCaL { + +/*========================================================================*/ + +// This provides the actual API of the CaDiCaL solver, which is implemented +// in the class 'Solver' below. Beside its constructor and destructor most +// important is the IPASIR part which you can find between 'BEGIN IPASIR' +// and 'END IPASIR' comments below. The following '[Example]' below might +// also be a good starting point to understand the API. + +/*========================================================================*/ + +// The SAT competition standardized the exit code of SAT solvers to the +// following which then is also used return code for 'solve' functions. +// In the following example we use those constants for brevity though. + +enum Status { + SATISFIABLE = 10, + UNSATISFIABLE = 20, + UNKNOWN = 0, +}; + +/*========================================================================*/ + +// [Example] +// +// The internal solver state follows the IPASIR API model used in the +// incremental track of the SAT competition. State transitions are +// triggered by member function calls, declared and described below. +// +// Consider the following code (from 'test/api/example.cpp') of API usage: +// +// CaDiCaL::Solver * solver = new CaDiCaL::Solver; +// +// // ------------------------------------------------------------------ +// // Encode Problem and check without assumptions. +// +// enum { TIE = 1, SHIRT = 2 }; +// +// solver->add (-TIE), solver->add (SHIRT), solver->add (0); +// solver->add (TIE), solver->add (SHIRT), solver->add (0); +// solver->add (-TIE), solver->add (-SHIRT), solver->add (0); +// +// int res = solver->solve (); // Solve instance. +// CADICAL_assert (res == 10); // Check it is 'SATISFIABLE'. +// +// res = solver->val (TIE); // Obtain assignment of 'TIE'. +// CADICAL_assert (res < 0); // Check 'TIE' assigned to 'false'. +// +// res = solver->val (SHIRT); // Obtain assignment of 'SHIRT'. +// CADICAL_assert (res > 0); // Check 'SHIRT' assigned to 'true'. +// +// // ------------------------------------------------------------------ +// // Incrementally solve again under one assumption. +// +// solver->assume (TIE); // Now force 'TIE' to true. +// +// res = solver->solve (); // Solve again incrementally. +// CADICAL_assert (res == 20); // Check it is 'UNSATISFIABLE'. +// +// res = solver->failed (TIE); // Check 'TIE' responsible. +// CADICAL_assert (res); // Yes, 'TIE' in core. +// +// res = solver->failed (SHIRT); // Check 'SHIRT' responsible. +// CADICAL_assert (!res); // No, 'SHIRT' not in core. +// +// // ------------------------------------------------------------------ +// // Incrementally solve once more under another assumption. +// +// solver->assume (-SHIRT); // Now force 'SHIRT' to false. +// +// res = solver->solve (); // Solve again incrementally. +// CADICAL_assert (res == 20); // Check it is 'UNSATISFIABLE'. +// +// res = solver->failed (TIE); // Check 'TIE' responsible. +// CADICAL_assert (!res); // No, 'TIE' not in core. +// +// res = solver->failed (-SHIRT); // Check '!SHIRT' responsible. +// CADICAL_assert (res); // Yes, '!SHIRT' in core. +// +// // ------------------------------------------------------------------ +// +// delete solver; + +/*========================================================================*/ + +// [States and Transitions] +// +// Compared to IPASIR we also use an 'ADDING' state in which the solver +// stays while adding non-zero literals until the clause is completed +// through adding a zero literal. The additional 'INITIALIZING', +// 'CONFIGURING' and 'DELETING' states are also not part of IPASIR but also +// useful for testing and debugging. +// +// We have the following transitions which are all synchronous except for +// the reentrant 'terminate' call: +// +// new +// INITIALIZING --------------------------> CONFIGURING +// +// set / trace +// CONFIGURING --------------------------> CONFIGURING +// +// add (non zero literal) +// VALID --------------------------> ADDING +// +// add (zero literal) +// VALID --------------------------> STEADY +// +// assume (non zero literal) +// READY --------------------------> STEADY +// +// solve +// READY --------------------------> SOLVING +// +// (internal) +// SOLVING --------------------------> SOLVED +// +// val (non zero literal) +// SATISFIED --------------------------> SATISFIED +// +// failed (non zero literal) +// UNSATISFIED --------------------------> UNSATISFIED +// +// implied (non zero literal) +// INCONCLUSIVE --------------------------> INCONCLUSIVE +// +// delete +// VALID --------------------------> DELETING +// +// where +// +// SOLVED = SATISFIED | UNSATISFIED | INCONCLUSIVE +// READY = CONFIGURING | STEADY | SOLVED +// VALID = READY | ADDING +// INVALID = INITIALIZING | DELETING +// +// The 'SOLVING' state is only visible in different contexts, i.e., from +// another thread or from a signal handler. It is used to implement +// 'terminate'. Here is the only asynchronous transition: +// +// terminate (asynchronously) +// SOLVING -------------------------> STEADY +// +// The important behaviour to remember is that adding, assuming or +// constraining a literal (immediately) destroys the satisfying assignment +// in the 'SATISFIED' state and vice versa resets all assumptions in the +// 'UNSATISFIED' state. This is exactly the behaviour required by the IPASIR +// interface. +// +// Furthermore, the model can only be queried through 'val' in the +// 'SATISFIED' state, while extracting failed assumptions with 'failed' only +// in the 'UNSATISFIED' state. Solving can only be started in the 'STEADY ' +// or 'CONFIGURING' state or after the previous call to 'solve' yielded an +// 'INCONCLUSIVE , 'SATISFIED' or 'UNSATISFIED' state. +// +// All literals have to be valid literals too, i.e., 32-bit integers +// different from 'INT_MIN'. If any of these requirements is violated the +// solver aborts with an 'API contract violation' message. +// +// HINT: If you do not understand why a contract is violated you can run +// 'mobical' on the failing API call trace. Point the environment variable +// 'CADICAL_API_TRACE' to the file where you want to save the trace during +// execution of your program linking against the library. You probably need +// for 'mobical' to use the option '--do-not-enforce-contracts' though to +// force running into the same contract violation. +// +// Additional API calls (like 'freeze' and 'melt') do not change the state +// of the solver and are all described below. + +/*========================================================================*/ + +// States are represented by a bit-set in order to combine them. + +enum State { + INITIALIZING = 1, // during initialization (invalid) + CONFIGURING = 2, // configure options (with 'set') + STEADY = 4, // ready to call 'solve' + ADDING = 8, // adding clause literals (zero missing) + SOLVING = 16, // while solving (within 'solve') + SATISFIED = 32, // satisfiable allows 'val' + UNSATISFIED = 64, // unsatisfiable allows 'failed' + DELETING = 128, // during and after deletion (invalid) + INCONCLUSIVE = 256, // unknown allows 'implied' + + // These combined states are used to check contracts. + + READY = CONFIGURING | STEADY | SATISFIED | UNSATISFIED | INCONCLUSIVE, + VALID = READY | ADDING, + INVALID = INITIALIZING | DELETING +}; + +/*------------------------------------------------------------------------*/ + +// Opaque classes needed in the API and declared in the same namespace. + +class File; +class Testing; +struct Internal; +struct External; + +/*------------------------------------------------------------------------*/ + +// Forward declaration of call-back classes. See bottom of this file. + +class Learner; +class FixedAssignmentListener; +class Terminator; +class ClauseIterator; +class WitnessIterator; +class ExternalPropagator; +class Tracer; +struct InternalTracer; +class FileTracer; +class StatTracer; + +/*------------------------------------------------------------------------*/ + +class Solver { + +public: + // ====== BEGIN IPASIR =================================================== + + // This section implements the corresponding IPASIR functionality. + + Solver (); + ~Solver (); + + static const char *signature (); // name of this library + + // Core functionality as in the IPASIR incremental SAT solver interface. + // (recall 'READY = CONFIGURING | STEADY | SATISFIED | UNSATISFIED'). + // Further note that 'lit' is required to be different from 'INT_MIN' and + // different from '0' except for 'add'. + + // Add valid literal to clause or zero to terminate clause. + // + // require (VALID) // recall 'VALID = READY | ADDING' + // if (lit) ensure (ADDING) // and thus VALID but not READY + // if (!lit) ensure (STEADY ) // and thus READY + // + void add (int lit); + + // Here are functions simplifying clause addition. The given literals + // should all be valid (different from 'INT_MIN' and different from '0'). + // + // require (VALID) + // ensure (STEADY ) + // + void clause (int); // Add unit clause. + void clause (int, int); // Add binary clause. + void clause (int, int, int); // Add ternary clause. + void clause (int, int, int, int); // Add quaternary clause. + void clause (int, int, int, int, int); // Add quinternary clause. + void clause (const std::vector<int> &); // Add literal vector as clause. + void clause (const int *, size_t); // Add literal array as clause. + + // This function can be used to check if the formula is already + // inconsistent (contains the empty clause or was proven to be + // root-level unsatisfiable). + + bool inconsistent (); + + // Assume valid non zero literal for next call to 'solve'. These + // assumptions are reset after the call to 'solve' as well as after + // returning from 'simplify' and 'lookahead. + // + // require (READY) + // ensure (STEADY ) + // + void assume (int lit); + + // Try to solve the current formula. Returns + // + // 0 = UNKNOWN (limit reached or interrupted through 'terminate') + // 10 = SATISFIABLE + // 20 = UNSATISFIABLE + // + // require (READY) + // ensure (INCONCLUSIVE | SATISFIED | UNSATISFIED) + // + // Note, that while in this call the solver actually transitions to state + // 'SOLVING', which however is only visible from a different context, + // i.e., from a different thread or from a signal handler. Only right + // before returning from this call it goes into a 'READY' state. + // + int solve (); + + // Get value (-lit=false, lit=true) of valid non-zero literal. + // + // require (SATISFIED) + // ensure (SATISFIED) + // + int val (int lit); + + // Try to flip the value of the given literal without falsifying the + // formula. Returns 'true' if this was successful. Otherwise the model is + // not changed and 'false' is returned. If a literal was eliminated or + // substituted flipping will fail on that literal and in particular the + // solver will not taint it nor restore any clauses. + // + // The 'flip' function can only flip the value of a variables not acting + // as witness on the reconstruction stack. + // + // As a side effect of calling this function first all assigned variables + // are propagated again without using blocking literal. Thus the first + // call to this function after obtaining a model adds a substantial + // overhead. Subsequent calls will not need to properly propagate again. + // + // Furthermore if the reconstruction stack is non-empty and has been + // traversed to reconstruct a full extended model for eliminated + // variables (and to satisfy removed blocked clauses), the values of these + // witness variables obtained via 'val' before become invalid. The user + // thus will need to call 'val' again after calling 'flip' which will + // trigger then a traversal of the reconstruction stack. + // + // So try to avoid mixing 'flip' and 'val' (for efficiency only). + // Further, this functionality is currently not supported in the presence + // of an external propagator. + // + // require (SATISFIED) + // ensure (SATISFIED) + // + bool flip (int lit); + + // Same as 'flip' without actually flipping it. This functionality is + // currently not supported in the presence of an external propagator. + // + // require (SATISFIED) + // ensure (SATISFIED) + // + bool flippable (int lit); + + // Determine whether the valid non-zero literal is in the core. + // Returns 'true' if the literal is in the core and 'false' otherwise. + // Note that the core does not have to be minimal. + // + // require (UNSATISFIED) + // ensure (UNSATISFIED) + // + bool failed (int lit); + + // Add call-back which is checked regularly for termination. There can + // only be one terminator connected. If a second (non-zero) one is added + // the first one is implicitly disconnected. + // + // require (VALID) + // ensure (VALID) + // + void connect_terminator (Terminator *terminator); + void disconnect_terminator (); + + // Add call-back which allows to export learned clauses. + // + // require (VALID) + // ensure (VALID) + // + void connect_learner (Learner *learner); + void disconnect_learner (); + + // ====== END IPASIR ===================================================== + + // Add call-back which allows to observe when a variable is fixed. + // + // require (VALID) + // ensure (VALID) + // + void connect_fixed_listener (FixedAssignmentListener *fixed_listener); + void disconnect_fixed_listener (); + + // ====== BEGIN IPASIR-UP ================================================ + + // Add call-back which allows to learn, propagate and backtrack based on + // external constraints. Only one external propagator can be connected + // and after connection every related variables must be 'observed' (use + // 'add_observed_var' function). + // Disconnection of the external propagator resets all the observed + // variables. + // + // require (VALID) + // ensure (VALID) + // + void connect_external_propagator (ExternalPropagator *propagator); + void disconnect_external_propagator (); + + // Mark as 'observed' those variables that are relevant to the external + // propagator. External propagation, clause addition during search and + // notifications are all over these observed variables. + // A variable can not be observed without having an external propagator + // connected. Observed variables are "frozen" internally, and so + // inprocessing will not consider them as candidates for elimination. + // An observed variable is allowed to be a fresh variable and it can be + // added also during solving. + // + // require (VALID_OR_SOLVING) + // ensure (VALID_OR_SOLVING) + // + void add_observed_var (int var); + + // Removes the 'observed' flag from the given variable. A variable can be + // set unobserved only between solve calls, not during it (to guarantee + // that no yet unexplained external propagation involves it). + // + // require (VALID) + // ensure (VALID) + // + void remove_observed_var (int var); + + // Removes all the 'observed' flags from the variables. Disconnecting the + // propagator invokes this step as well. + // + // require (VALID) + // ensure (VALID) + // + void reset_observed_vars (); + + // Get reason of valid observed literal (true = it is an observed variable + // and it got assigned by a decision during the CDCL loop. Otherwise: + // false. + // + // require (VALID_OR_SOLVING) + // ensure (VALID_OR_SOLVING) + // + bool is_decision (int lit); + + // Force solve to backtrack to certain decision level. Can be called only + // during 'cb_decide' of a connected External Propagator. + // Invoking in any other time will not have an effect. + // If the call had an effect, the External Propagator will be notified + // about the backtrack via 'notify_backtrack'. + // + // require (SOLVING) + // ensure (SOLVING) + // + void force_backtrack (size_t new_level); + + // ====== END IPASIR-UP ================================================== + + //------------------------------------------------------------------------ + // Adds a literal to the constraint clause. Same functionality as 'add' + // but the clause only exists for the next call to solve (same lifetime as + // assumptions). Only one constraint may exists at a time. A new + // constraint replaces the old. The main application of this functionality + // is the model checking algorithm IC3. See our FMCAD'21 paper + // [FroleyksBiere-FMCAD'19] for more details. + // + // Add valid literal to the constraint clause or zero to terminate it. + // + // require (VALID) // recall 'VALID = READY | + // ADDING' if (lit) ensure (ADDING) // and thus VALID but not + // READY if (!lit) && !adding_clause ensure (STEADY ) // and thus READY + // + void constrain (int lit); + + // Determine whether the constraint was used to proof the + // unsatisfiability. Note that the formula might still be unsatisfiable + // without the constraint. + // + // require (UNSATISFIED) + // ensure (UNSATISFIED) + // + bool constraint_failed (); + + // Collects a subset of those literals that are implied by unit + // propagation by assuming the currently defined (potentially empty) set + // of assumptions (see IPASIR assume(lit)) function. In case unit + // propgation over the defined set of assumptions (or over the clause + // database on its own) leads to conflict, the function returns 20 and the + // content of 'implicants' is undefined. In case unit propagation happens + // to satisfy all the clauses (not probable, but not impossible), the + // function returns 10 and 'implicants' is a solution of the current + // formula under the current assumptions (after solution reconstruction). + // In any other case, the function returns 0 (indicating 'UNKNOWN') and + // 'implicants' lists the non-conflicting current value of the trail. + + // Returns + // + // 0 = UNKNOWN (unit propagation did not lead to a conflict nor to a + // complete assignment, or limit reached or interrupted + // through 'terminate') + // 10 = SATISFIABLE + // 20 = UNSATISFIABLE + + // require (READY) + // ensure (INCONCLUSIVE | SATISFIED | UNSATISFIED) + int propagate (); + + // + // require (INCONCLUSIVE) + // ensure (INCONCLUSIVE) + // + void implied (std::vector<int> &implicants); + + //------------------------------------------------------------------------ + // This function determines a good splitting literal. The result can be + // zero if the formula is proven to be satisfiable or unsatisfiable. This + // can then be checked by 'state ()'. If the formula is empty and + // the function is not able to determine satisfiability also zero is + // returned but the state remains steady. + // + // require (READY) + // ensure (INCONCLUSIVE |SATISFIED|UNSATISFIED) + // + int lookahead (void); + + struct CubesWithStatus { + int status; + std::vector<std::vector<int>> cubes; + }; + + CubesWithStatus generate_cubes (int, int min_depth = 0); + + void reset_assumptions (); + void reset_constraint (); + + // Return the current state of the solver as defined above. + // + const State &state () const { return _state; } + + // Similar to 'state ()' but using the standard competition exit codes of + // '10' for 'SATISFIABLE', '20' for 'UNSATISFIABLE' and '0' otherwise. + // + int status () const { + if (_state == SATISFIED) + return 10; + else if (_state == UNSATISFIED) + return 20; + else + return 0; + } + + /*----------------------------------------------------------------------*/ + + static const char *version (); // return version string + + /*----------------------------------------------------------------------*/ + // Copy 'this' into a fresh 'other'. The copy procedure is not a deep + // clone, but only copies irredundant clauses and units. It also makes + // sure that witness reconstruction works with the copy as with the + // original formula such that both solvers have the same models. + // Assumptions are not copied. Options however are copied as well as + // flags which remember the current state of variables in preprocessing. + // + // require (READY) // for 'this' + // ensure (READY) // for 'this' + // + // other.require (CONFIGURING) + // other.ensure (CONFIGURING | STEADY ) + // + void copy (Solver &other) const; + + /*----------------------------------------------------------------------*/ + // Variables are usually added and initialized implicitly whenever a + // literal is used as an argument except for the functions 'val', 'fixed', + // 'failed' and 'frozen'. However, the library internally keeps a maximum + // variable index, which can be queried. + // With factor (BVA) the solver might also add new variables. In that case + // the user is required to use this to check which variables are currently + // free before adding new variables of their own. + // The alternative is to reserve variables in batches with + // 'reserve_difference'. Using 'reserve' in combination with any technique + // that could add variables (currently only factor) is not advised. + // + // require (VALID | SOLVING) + // ensure (VALID | SOLVING) + // + int vars (); + + // Increase the maximum variable index explicitly. This function makes + // sure that at least 'min_max_var' variables are initialized. Since it + // might need to reallocate tables, it destroys a satisfying assignment + // and has the same state transition and conditions as 'assume' etc. + // + // require (READY) + // ensure (STEADY ) + // + void reserve (int min_max_var); + + // Increase the maximum variable index by a number of new variables. + // initializes 'number_of_vars' new variables and protects them from + // being used by the solver as extension variables (BVA). + // It returns the new maximum variable index which is the highest + // variable name of the consecutive range of newly reserved variables. + // It has the same state transition and conditions as 'reserve' above. + // + // require (READY) + // ensure (STEADY ) + // + int reserve_difference (int number_of_vars); + +#ifndef CADICAL_NTRACING + //------------------------------------------------------------------------ + // This function can be used to write API calls to a file. The same + // format is used which 'mobical' can read, execute and also shrink + // through delta debugging. + // + // Tracing API calls can also be achieved by using the environment + // variable 'CADICAL_API_TRACE'. That alternative is useful if you do not + // want to change the source code using the solver, e.g., if you only have + // a binary with the solver linked in. However, that method only allows + // to trace one solver instance, while with the following function API + // tracing can be enabled for different solver instances individually. + // + // The solver will flush the file after every trace API call but does not + // close it during deletion. It remains owned by the user of the library. + // + // require (VALID) + // ensure (VALID) + // + void trace_api_calls (FILE *file); +#endif + + //------------------------------------------------------------------------ + // Option handling. + + // Determine whether 'name' is a valid option name. + // + static bool is_valid_option (const char *name); + + // Determine whether 'name' enables a specific preprocessing technique. + // + static bool is_preprocessing_option (const char *name); + + // Determine whether 'arg' is a valid long option of the form '--<name>', + // '--<name>=<val>' or '--no-<name>' similar to 'set_long_option' below. + // Legal values are 'true', 'false', or '[-]<mantissa>[e<exponent>]'. + + static bool is_valid_long_option (const char *arg); + + // Get the current value of the option 'name'. If 'name' is invalid then + // zero is returned. Here '--...' arguments as invalid options. + // + int get (const char *name); + + // Set the default verbose message prefix (default "c "). + // + void prefix (const char *verbose_message_prefix); + + // Explicit version of setting an option. If the option '<name>' exists + // and '<val>' can be parsed then 'true' is returned. If the option value + // is out of range the actual value is computed as the closest (minimum or + // maximum) value possible, but still 'true' is returned. + // + // require (CONFIGURING) + // ensure (CONFIGURING) + // + // Thus options can only bet set right after initialization. + // + bool set (const char *name, int val); + + // This function accepts options in command line syntax: + // + // '--<name>=<val>', '--<name>', or '--no-<name>' + // + // It actually calls the previous 'set' function after parsing 'arg'. The + // same values are expected as for 'is_valid_long_option' above and as + // with 'set' any value outside of the range of legal values for a + // particular option are set to either the minimum or maximum depending on + // which side of the valid interval they lie. + // + // require (CONFIGURING) + // ensure (CONFIGURING) + // + bool set_long_option (const char *arg); + + // Determine whether 'name' is a valid configuration. + // + static bool is_valid_configuration (const char *); + + // Overwrite (some) options with the forced values of the configuration. + // The result is 'true' iff the 'name' is a valid configuration. + // + // require (CONFIGURING) + // ensure (CONFIGURING) + // + bool configure (const char *); + + // Increase preprocessing and inprocessing limits by '10^<val>'. Values + // below '0' are ignored and values above '9' are reduced to '9'. + // + // require (READY) + // ensure (READY) + // + void optimize (int val); + + // Specify search limits, where currently 'name' can be "conflicts", + // "decisions", "preprocessing", or "localsearch". The first two limits + // are unbounded by default. Thus using a negative limit for conflicts or + // decisions switches back to the default of unlimited search (for that + // particular limit). The preprocessing limit determines the number of + // preprocessing rounds, which is zero by default. Similarly, the local + // search limit determines the number of local search rounds (also zero by + // default). As with 'set', the return value denotes whether the limit + // 'name' is valid. These limits are only valid for the next 'solve' or + // 'simplify' call and reset to their default after 'solve' returns (as + // well as overwritten and reset during calls to 'simplify' and + // 'lookahead'). We actually also have an internal "terminate" limit + // which however should only be used for testing and debugging. + // + // require (READY) + // ensure (READY) + // + bool limit (const char *arg, int val); + bool is_valid_limit (const char *arg); + + // The number of currently active variables and clauses can be queried by + // these functions. Variables become active if a clause is added with it. + // They become inactive if they are eliminated or fixed at the root level + // Clauses become inactive if they are satisfied, subsumed, eliminated. + // Redundant clauses are reduced regularly and thus the 'redundant' + // function is less useful. + // + // require (VALID) + // ensure (VALID) + // + int active () const; // Number of active variables. + int64_t redundant () const; // Number of active redundant clauses. + int64_t irredundant () const; // Number of active irredundant clauses. + + //------------------------------------------------------------------------ + // This function executes the given number of preprocessing rounds. It is + // similar to 'solve' with 'limits ("preprocessing", rounds)' except that + // no CDCL nor local search, nor lucky phases are executed. The result + // values are also the same: 0=UNKNOWN, 10=SATISFIABLE, 20=UNSATISFIABLE. + // As 'solve' it resets current assumptions and limits before returning. + // The numbers of rounds should not be negative. If the number of rounds + // is zero only clauses are restored (if necessary) and top level unit + // propagation is performed, which both take some time. + // + // require (READY) + // ensure (INCONCLUSIVE | SATISFIED | UNSATISFIED) + // + int simplify (int rounds = 3); + + //------------------------------------------------------------------------ + // Force termination of 'solve' asynchronously. + // + // require (SOLVING | READY) + // ensure (INCONCLUSIVE ) // actually not immediately (synchronously) + // + void terminate (); + + //------------------------------------------------------------------------ + + // We have the following common reference counting functions, which avoid + // to restore clauses but require substantial user guidance. This was the + // only way to use inprocessing in incremental SAT solving in Lingeling + // (and before in MiniSAT's 'freeze' / 'thaw') and which did not use + // automatic clause restoring. In general this is slower than + // restoring clauses and should not be used. + // + // In essence the user freezes variables which potentially are still + // needed in clauses added or assumptions used after the next 'solve' + // call. As in Lingeling you can freeze a variable multiple times, but + // then have to melt it the same number of times again in order to enable + // variable eliminating on it etc. The arguments can be literals + // (negative indices) but conceptually variables are frozen. + // + // In the old way of doing things without restore you should not use a + // variable incrementally (in 'add' or 'assume'), which was used before + // and potentially could have been eliminated in a previous 'solve' call. + // This can lead to spurious satisfying assignment. In order to check + // this API contract one can use the 'checkfrozen' option. This has the + // drawback that restoring clauses implicitly would fail with a fatal + // error message even if in principle the solver could just restore + // clauses. Thus this option is disabled by default. + // + // See our SAT'19 paper [FazekasBiereScholl-SAT'19] for more details. + // + // require (VALID) + // ensure (VALID) + // + bool frozen (int lit) const; + void freeze (int lit); + void melt (int lit); // Also needs 'require (frozen (lit))'. + + //------------------------------------------------------------------------ + + // Root level assigned variables can be queried with this function. + // It returns '1' if the literal is implied by the formula, '-1' if its + // negation is implied, or '0' if this is unclear at this point. + // + // require (VALID) + // ensure (VALID) + // + int fixed (int lit) const; + + //------------------------------------------------------------------------ + // Force the default decision phase of a variable to a certain value. + // + void phase (int lit); + void unphase (int lit); + + //------------------------------------------------------------------------ + + // Enables clausal proof tracing in DRAT format and returns 'true' if + // successfully opened for writing. Writing proofs has to be enabled + // before calling 'solve', 'add' and 'dimacs', that is in state + // 'CONFIGURING'. Otherwise only partial proofs would be written. + // + // require (CONFIGURING) + // ensure (CONFIGURING) + // + bool trace_proof (FILE *file, const char *name); // Write DRAT proof. + bool trace_proof (const char *path); // Open & write proof. + + // Flushing the proof trace file eventually calls 'fflush' on the actual + // file or pipe and thus if this function returns all the proof steps + // should have been written (with the same guarantees as 'fflush'). + // + // The additional optional argument forces to print the number of addition + // and deletion steps in the proof even if the verbosity level is zero but + // not if quiet is set as well. The default for the stand-alone solver is + // to print this information (in the 'closing proof' section) but for API + // usage of the library we want to stay silent unless explicitly requested + // or verbosity is non-zero (and as explained quiet is not set). + // + // This function can be called multiple times. + // + // require (VALID) + // ensure (VALID) + // + void flush_proof_trace (bool print = false); + + // Close proof trace early. Similar to 'flush' we allow the user to + // control with 'print' in a more fine-grained way whether statistics + // about the size of the written proof file and if compressed on-the-fly + // the number of actual bytes written (including deflation percentage) are + // printed. Before actually closing (or detaching in case of writing to + // '<stdout>') we check whether 'flush_proof_trace' was called since the + // last time a proof step (addition or deletion) was traced. If this is + // not the case we would call 'flush_proof_trace' with the same 'print' + // argument. + // + // require (VALID) + // ensure (VALID) + // + void close_proof_trace (bool print = false); + + // Enables clausal proof tracing with or without antecedents using + // the Tracer interface defined in 'tracer.hpp' + // + // InternalTracer, StatTracer and FileTracer for internal use + // + // require (CONFIGURING) + // ensure (CONFIGURING) + // + void connect_proof_tracer (Tracer *tracer, bool antecedents, + bool finalize_clauses = false); + void connect_proof_tracer (InternalTracer *tracer, bool antecedents, + bool finalize_clauses = false); + void connect_proof_tracer (StatTracer *tracer, bool antecedents, + bool finalize_clauses = false); + void connect_proof_tracer (FileTracer *tracer, bool antecedents, + bool finalize_clauses = false); + + // Triggers the conclusion of incremental proofs. + // if the solver is SATISFIED it will trigger extend () + // and give the model to the proof tracer through conclude_sat () + // if the solver is UNSATISFIED it will trigger failing () + // which will learn new clauses as explained below: + // In case of failed assumptions will provide a core negated + // as a clause through the proof tracer interface. + // With a failing constraint these can be multiple clauses. + // Then it will trigger a conclude_unsat event with the id(s) + // of the newly learnt clauses or the id of the global conflict. + // In case the solver is in UNKNOWN, it will collect the currently + // entrailed literals and add them to the proof. + // + // require (SATISFIED || UNSATISFIED || UNKNOWN) + // ensure (SATISFIED || UNSATISFIED || UNKNOWN) + // + void conclude (); + + // Disconnect proof tracer. If this is not done before deleting + // the tracer will be deleted. Returns true if successful. + // + // require (VALID) + // ensure (VALID) + // + bool disconnect_proof_tracer (Tracer *tracer); + bool disconnect_proof_tracer (StatTracer *tracer); + bool disconnect_proof_tracer (FileTracer *tracer); + + //------------------------------------------------------------------------ + + static void usage (); // print usage information for long options + + static void configurations (); // print configuration usage options + + // require (!DELETING) + // ensure (!DELETING) + // + void statistics (); // print statistics + void resources (); // print resource usage (time and memory) + + // require (VALID) + // ensure (VALID) + // + void options (); // print current option and value list + + //------------------------------------------------------------------------ + // Traverse irredundant clauses or the extension stack in reverse order. + // + // The return value is false if traversal is aborted early due to one of + // the visitor functions returning false. See description of the + // iterators below for more details on how to use these functions. + // + // require (VALID) + // ensure (VALID) + // + bool traverse_clauses (ClauseIterator &) const; + bool traverse_witnesses_backward (WitnessIterator &) const; + bool traverse_witnesses_forward (WitnessIterator &) const; + + //------------------------------------------------------------------------ + // Files with explicit path argument support compressed input and output + // if appropriate helper functions 'gzip' etc. are available. They are + // called through opening a pipe to an external command. + // + // If the 'strict' argument is zero then the number of variables and + // clauses specified in the DIMACS headers are ignored, i.e., the header + // 'p cnf 0 0' is always legal. If the 'strict' argument is larger '1' + // strict formatting of the header is required, i.e., single spaces + // everywhere and no trailing white space. + // + // Returns zero if successful and otherwise an error message. + // + // require (VALID) + // ensure (VALID) + // + const char *read_dimacs (FILE *file, const char *name, int &vars, + int strict = 1); + + const char *read_dimacs (const char *path, int &vars, int strict = 1); + + // The following routines work the same way but parse both DIMACS and + // INCCNF files (with 'p inccnf' header and 'a <cube>' lines). If the + // parser finds and 'p inccnf' header or cubes then '*incremental' is set + // to true and the cubes are stored in the given vector (each cube + // terminated by a zero). + + const char *read_dimacs (FILE *file, const char *name, int &vars, + int strict, bool &incremental, + std::vector<int> &cubes); + + const char *read_dimacs (const char *path, int &vars, int strict, + bool &incremental, std::vector<int> &cubes); + + //------------------------------------------------------------------------ + // Write current irredundant clauses and all derived unit clauses + // to a file in DIMACS format. Clauses on the extension stack are + // not included, nor any redundant clauses. + // + // The 'min_max_var' parameter gives a lower bound on the number '<vars>' + // of variables used in the DIMACS 'p cnf <vars> ...' header. + // + // Returns zero if successful and otherwise an error message. + // + // require (VALID) + // ensure (VALID) + // + const char *write_dimacs (const char *path, int min_max_var = 0); + + // The extension stack for reconstruction a solution can be written too. + // + const char *write_extension (const char *path); + + // Print build configuration to a file with prefix 'c '. If the file + // is '<stdout>' or '<stderr>' then terminal color codes might be used. + // + static void build (FILE *file, const char *prefix = "c "); + +private: + //==== start of state ==================================================== + + // The solver is in the state ADDING if either the current clause or the + // constraint (or both) is not yet terminated. + bool adding_clause; + bool adding_constraint; + + State _state; // API states as discussed above. + + /*----------------------------------------------------------------------*/ + + // The 'Solver' class is a 'facade' object for 'External'. It exposes the + // public API of 'External' but hides everything else (except for the some + // private functions). It is supposed to make it easier to understand the + // API and use the solver through the API. + + // This approach has the benefit of decoupling this header file from all + // internal data structures, which is particularly useful if the rest of + // the source is not available. For instance if only a CaDiCaL library is + // installed in a system, then only this header file has to be installed + // too, and still allows to compile and link against the library. + + /*----------------------------------------------------------------------*/ + + // More precisely the CaDiCaL code is split into three layers: + // + // Solver: facade object providing the actual API of the solver + // External: communication layer between 'Solver' and 'Internal' + // Internal: the actual solver code + // + // The 'External' and 'Internal' layers are declared and implemented in + // the corresponding '{external,internal}.{hpp,cpp}' files (as expected), + // while the 'Solver' facade class is defined in 'cadical.hpp' (here) but + // implemented in 'solver.cpp'. The reason for this naming mismatch is, + // that we want to use 'cadical.hpp' for the library header (this header + // file) and call the binary of the stand alone SAT also 'cadical', which + // is more naturally implemented in 'cadical.cpp'. + // + // Separating 'External' from 'Internal' also allows us to map external + // literals to internal literals, which is useful with many fixed or + // eliminated variables (during 'compact' the internal variable range is + // reduced and external variables are remapped). Such an approach is also + // necessary, if we want to use extended resolution in the future (such as + // bounded variable addition). + // + Internal *internal; // Hidden internal solver. + External *external; // Hidden API to internal solver mapping. + + friend class Testing; // Access to 'internal' for testing only! + +#ifndef CADICAL_NTRACING + // The API calls to the solver can be traced by setting the environment + // variable 'CADICAL_API_TRACE' to point to the path of a file to which + // API calls are written. The same format is used which 'mobical' can + // read, execute and also shrink through delta debugging. + // + // The environment variable is read in the constructor and the trace is + // opened for writing and then closed again in the destructor. + // + // Alternatively one case use 'trace_api_calls'. Both + // + bool close_trace_api_file; // Close file if owned by solver it. + FILE *trace_api_file; // Also acts as flag that we are tracing. + + static bool tracing_api_through_environment; + + //===== end of state ==================================================== + + void trace_api_call (const char *) const; + void trace_api_call (const char *, int) const; + void trace_api_call (const char *, const char *) const; + void trace_api_call (const char *, const char *, int) const; +#endif + + void transition_to_steady_state (); + + //------------------------------------------------------------------------ + // Used in the stand alone solver application 'App' and the model based + // tester 'Mobical'. So only these two classes need direct access to the + // otherwise more application specific functions listed here together with + // the internal DIMACS parser. + + friend class App; + friend class Mobical; + friend class Parser; + + // Read solution in competition format for debugging and testing. + // + // require (VALID) + // ensure (VALID) + // + const char *read_solution (const char *path); + + // Cross-compilation with 'MinGW' needs some work-around for 'printf' + // style printing of 64-bit numbers including warning messages. The + // followings lines are copies of similar code in 'inttypes.hpp' but we + // want to keep the 'cadical.hpp' header file stand-alone. + +#ifndef PRINTF_FORMAT +#ifdef __MINGW32__ +#define __USE_MINGW_ANSI_STDIO 1 +#define PRINTF_FORMAT __MINGW_PRINTF_FORMAT +#else +#define PRINTF_FORMAT printf +#endif +#endif + + // This gives warning messages for wrong 'printf' style format string + // usage. Apparently (on 'gcc 9' at least) the first argument is 'this' + // here. + // + // TODO: support for other compilers (beside 'gcc' and 'clang'). + + /* +#define CADICAL_ATTRIBUTE_FORMAT(FORMAT_POSITION, \ + VARIADIC_ARGUMENT_POSITION) \ + __attribute__ ((format (PRINTF_FORMAT, FORMAT_POSITION, \ + VARIADIC_ARGUMENT_POSITION))) + */ +#define CADICAL_ATTRIBUTE_FORMAT(FORMAT_POSITION, VARIADIC_ARGUMENT_POSITION) + + // Messages in a common style. + // + // require (VALID | DELETING) + // ensure (VALID | DELETING) + // + void section (const char *); // print section header + void message (const char *, ...) // ordinary message + CADICAL_ATTRIBUTE_FORMAT (2, 3); + + void message (); // empty line - only prefix + void error (const char *, ...) // produce error message + CADICAL_ATTRIBUTE_FORMAT (2, 3); + + // Explicit verbose level ('section' and 'message' use '0'). + // + // require (VALID | DELETING) + // ensure (VALID | DELETING) + // + void verbose (int level, const char *, ...) + CADICAL_ATTRIBUTE_FORMAT (3, 4); + + // Factoring out common code to both 'read_dimacs' functions above. + // + // require (VALID) + // ensure (VALID) + // + const char *read_dimacs (File *, int &, int strict, bool *incremental = 0, + std::vector<int> * = 0); + + // Factored out common code for 'solve', 'simplify' and 'lookahead'. + // + int call_external_solve_and_check_results (bool preprocess_only); + + //------------------------------------------------------------------------ + // Print DIMACS file to '<stdout>' for debugging and testing purposes, + // including derived units and assumptions. Since it will print in terms + // of internal literals it is otherwise not really useful. To write a + // DIMACS formula in terms of external variables use 'write_dimacs'. + // + // require (!INITIALIZING) + // ensure (!INITIALIZING) + // + void dump_cnf (); + friend struct DumpCall; // Mobical calls 'dump_cnf' in 'DumpCall::execute' + + /*----------------------------------------------------------------------*/ + + // Used in mobical to test external propagation internally. + // These functions should not be called for any other purposes. + // + ExternalPropagator *get_propagator (); + bool observed (int lit); + bool is_witness (int lit); + + friend struct LemmaCall; + friend struct ObserveCall; + friend struct DisconnectCall; + friend class MockPropagator; +}; + +/*========================================================================*/ + +// Connected terminators are checked for termination regularly. If the +// 'terminate' function of the terminator returns true the solver is +// terminated synchronously as soon it calls this function. + +class Terminator { +public: + virtual ~Terminator () {} + virtual bool terminate () = 0; +}; + +// Connected learners which can be used to export learned clauses. +// The 'learning' can check the size of the learn clause and only if it +// returns true then the individual literals of the learned clause are given +// to the learn through 'learn' one by one terminated by a zero literal. + +class Learner { +public: + virtual ~Learner () {} + virtual bool learning (int size) = 0; + virtual void learn (int lit) = 0; +}; + +// Connected listener gets notified whenever the truth value of a variable +// is fixed (for example during inprocessing or due to some derived unit +// clauses). + +class FixedAssignmentListener { +public: + virtual ~FixedAssignmentListener () {} + + virtual void notify_fixed_assignment (int) = 0; +}; + +/*------------------------------------------------------------------------*/ + +// Allows to connect an external propagator to propagate values to variables +// with an external clause as a reason or to learn new clauses during the +// CDCL loop (without restart). + +class ExternalPropagator { + +public: + bool is_lazy = false; // lazy propagator only checks complete assignments + bool are_reasons_forgettable = + false; // Reason external clauses can be deleted + + virtual ~ExternalPropagator () {} + + // Notify the propagator about assignments to observed variables. + // The notification is not necessarily eager. It usually happens before + // the call of propagator callbacks and when a driving clause is leading + // to an assignment. + // + // virtual void notify_assignment (int lit, bool is_fixed) = 0; + virtual void notify_assignment (const std::vector<int> &lits) = 0; + virtual void notify_new_decision_level () = 0; + virtual void notify_backtrack (size_t new_level) = 0; + + // Check by the external propagator the found complete solution (after + // solution reconstruction). If it returns false, the propagator should + // provide an external clause during the next callback or introduce new + // observed variables during this callback. + // + virtual bool cb_check_found_model (const std::vector<int> &model) = 0; + + // Ask the external propagator for the next decision literal. If it + // returns 0, the solver makes its own choice. + // + virtual int cb_decide () { return 0; }; + + // Ask the external propagator if there is an external propagation to make + // under the current assignment. It returns either a literal to be + // propagated or 0, indicating that there is no external propagation under + // the current assignment. + // + virtual int cb_propagate () { return 0; }; + + // Ask the external propagator for the reason clause of a previous + // external propagation step (done by cb_propagate). The clause must be + // added literal-by-literal closed with a 0. Further, the clause must + // contain the propagated literal. + // + // The clause will be learned as an Irredundant Non-Forgettable Clause + // (see below at 'cb_has_external_clause' more details about it). + // + virtual int cb_add_reason_clause_lit (int propagated_lit) { + (void) propagated_lit; + return 0; + }; + + // The following two functions are used to add external clauses to the + // solver during the CDCL loop. The external clause is added + // literal-by-literal and learned by the solver as an irredundant + // (original) input clause. The clause can be arbitrary, but if it is + // root-satisfied or tautology, the solver will ignore it without learning + // it. Root-falsified literals are eagerly removed from the clause. + // Falsified clauses trigger conflict analysis, propagating clauses + // trigger propagation. In case chrono is 0, the solver backtracks to + // propagate the new literal on the right decision level, otherwise it + // potentially will be an out-of-order assignment on the current level. + // Unit clauses always (unless root-satisfied, see above) trigger + // backtracking (independently from the value of the chrono option and + // independently from being falsified or satisfied or unassigned) to level + // 0. Empty clause (or root falsified clause, see above) makes the problem + // unsat and stops the search immediately. A literal 0 must close the + // clause. + // + // The external propagator indicates that there is a clause to add. + // The parameter of the function allows the user to indicate that how + // 'forgettable' is the external clause. Forgettable clauses are allowed + // to be removed by the SAT solver during clause database reduction. + // However, it is up to the solver to decide when actually the clause is + // deleted. For example, unit clauses, even forgettable ones, will not be + // deleted. In case the clause is not 'forgettable' (the parameter is + // false), the solver considers the clause to be irredundant. + // + // In case the solver produces incremental proofs, these external clauses + // are added to the proof during solving at real-time, i.e., the proof + // checker can ignore them until that point (so added as input clause, but + // input after the query line). + // + // Reason clauses of external propagation steps are assumed to be + // forgettable, parameter 'reason_forgettable' can be used to change it. + // + // Currently, every external clause is expected to be over observed + // (therefore frozen) variables, hence no tainting or restore steps + // are performed upon their addition. This will be changed in later + // versions probably. + // + virtual bool cb_has_external_clause (bool &is_forgettable) = 0; + + // The actual function called to add the external clause. + // + virtual int cb_add_external_clause_lit () = 0; +}; + +/*------------------------------------------------------------------------*/ + +// Allows to traverse all remaining irredundant clauses. Satisfied and +// eliminated clauses are not included, nor any derived units unless such +// a unit literal is frozen. Falsified literals are skipped. If the solver +// is inconsistent only the empty clause is traversed. +// +// If 'clause' returns false traversal aborts early. + +class ClauseIterator { +public: + virtual ~ClauseIterator () {} + virtual bool clause (const std::vector<int> &) = 0; +}; + +/*------------------------------------------------------------------------*/ + +// Allows to traverse all clauses on the extension stack together with their +// witness cubes. If the solver is inconsistent, i.e., an empty clause is +// found and the formula is unsatisfiable, then nothing is traversed. +// +// The clauses traversed in 'traverse_clauses' together with the clauses on +// the extension stack are logically equivalent to the original clauses. +// See our SAT'19 paper for more details. +// +// The witness literals can be used to extend and fix an assignment on the +// remaining clauses to satisfy the clauses on the extension stack too. +// +// All derived units of non-frozen variables are included too. +// +// If 'witness' returns false traversal aborts early. + +class WitnessIterator { +public: + virtual ~WitnessIterator () {} + virtual bool witness (const std::vector<int> &clause, + const std::vector<int> &witness, + int64_t id = 0) = 0; +}; + +/*------------------------------------------------------------------------*/ + +} // namespace CaDiCaL + +ABC_NAMESPACE_CXX_HEADER_END + +#endif diff --git a/src/sat/cadical/cadicalSolver.c b/src/sat/cadical/cadicalSolver.c new file mode 100644 index 0000000000..9caea66052 --- /dev/null +++ b/src/sat/cadical/cadicalSolver.c @@ -0,0 +1,305 @@ +/**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 diff --git a/src/sat/cadical/cadicalSolver.h b/src/sat/cadical/cadicalSolver.h new file mode 100644 index 0000000000..0192ada3c0 --- /dev/null +++ b/src/sat/cadical/cadicalSolver.h @@ -0,0 +1,76 @@ +/**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 /// +//////////////////////////////////////////////////////////////////////// diff --git a/src/sat/cadical/cadicalTest.c b/src/sat/cadical/cadicalTest.c new file mode 100644 index 0000000000..f3919160ba --- /dev/null +++ b/src/sat/cadical/cadicalTest.c @@ -0,0 +1,210 @@ +/**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 diff --git a/src/sat/cadical/cadical_analyze.cpp b/src/sat/cadical/cadical_analyze.cpp new file mode 100644 index 0000000000..74b67fb065 --- /dev/null +++ b/src/sat/cadical/cadical_analyze.cpp @@ -0,0 +1,1285 @@ +#include "global.h" + +#include "internal.hpp" + +ABC_NAMESPACE_IMPL_START + +namespace CaDiCaL { + +/*------------------------------------------------------------------------*/ + +// Code for conflict analysis, i.e., to generate the first UIP clause. The +// main function is 'analyze' below. It further uses 'minimize' to minimize +// the first UIP clause, which is in 'minimize.cpp'. An important side +// effect of conflict analysis is to update the decision queue by bumping +// variables. Similarly analyzed clauses are bumped to mark them as active. + +/*------------------------------------------------------------------------*/ + +void Internal::learn_empty_clause () { + CADICAL_assert (!unsat); + build_chain_for_empty (); + LOG ("learned empty clause"); + external->check_learned_empty_clause (); + int64_t id = ++clause_id; + if (proof) { + proof->add_derived_empty_clause (id, lrat_chain); + } + unsat = true; + conflict_id = id; + marked_failed = true; + conclusion.push_back (id); + lrat_chain.clear (); +} + +void Internal::learn_unit_clause (int lit) { + CADICAL_assert (!unsat); + LOG ("learned unit clause %d, stored at position %d", lit, vlit (lit)); + external->check_learned_unit_clause (lit); + int64_t id = ++clause_id; + if (lrat || frat) { + const unsigned uidx = vlit (lit); + unit_clauses (uidx) = id; + } + if (proof) { + proof->add_derived_unit_clause (id, lit, lrat_chain); + } + mark_fixed (lit); +} + +/*------------------------------------------------------------------------*/ + +// Move bumped variables to the front of the (VMTF) decision queue. The +// 'bumped' time stamp is updated accordingly. It is used to determine +// whether the 'queue.assigned' pointer has to be moved in 'unassign'. + +void Internal::bump_queue (int lit) { + CADICAL_assert (opts.bump); + const int idx = vidx (lit); + if (!links[idx].next) + return; + queue.dequeue (links, idx); + queue.enqueue (links, idx); + CADICAL_assert (stats.bumped != INT64_MAX); + btab[idx] = ++stats.bumped; + LOG ("moved to front variable %d and bumped to %" PRId64 "", idx, + btab[idx]); + if (!vals[idx]) + update_queue_unassigned (idx); +} + +/*------------------------------------------------------------------------*/ + +// It would be better to use 'isinf' but there are some historical issues +// with this function. On some platforms it is a macro and even for C++ it +// changed the scope (in pre 5.0 gcc) from '::isinf' to 'std::isinf'. I do +// not want to worry about these strange incompatibilities and thus use the +// same trick as in older solvers (since the MiniSAT team invented EVSIDS) +// and simply put a hard limit here. It is less elegant but easy to port. + +static inline bool evsids_limit_hit (double score) { + CADICAL_assert (sizeof (score) == 8); // assume IEEE 754 64-bit double + return score > 1e150; // MAX_DOUBLE is around 1.8e308 +} + +/*------------------------------------------------------------------------*/ + +// Classical exponential VSIDS as pioneered by MiniSAT. + +void Internal::rescale_variable_scores () { + stats.rescored++; + double divider = score_inc; + for (auto idx : vars) { + const double tmp = stab[idx]; + if (tmp > divider) + divider = tmp; + } + PHASE ("rescore", stats.rescored, "rescoring %d variable scores by 1/%g", + max_var, divider); + CADICAL_assert (divider > 0); + double factor = 1.0 / divider; + for (auto idx : vars) + stab[idx] *= factor; + score_inc *= factor; + PHASE ("rescore", stats.rescored, + "new score increment %g after %" PRId64 " conflicts", score_inc, + stats.conflicts); +} + +void Internal::bump_variable_score (int lit) { + CADICAL_assert (opts.bump); + int idx = vidx (lit); + double old_score = score (idx); + CADICAL_assert (!evsids_limit_hit (old_score)); + double new_score = old_score + score_inc; + if (evsids_limit_hit (new_score)) { + LOG ("bumping %g score of %d hits EVSIDS score limit", old_score, idx); + rescale_variable_scores (); + old_score = score (idx); + CADICAL_assert (!evsids_limit_hit (old_score)); + new_score = old_score + score_inc; + } + CADICAL_assert (!evsids_limit_hit (new_score)); + LOG ("new %g score of %d", new_score, idx); + score (idx) = new_score; + if (scores.contains (idx)) + scores.update (idx); +} + +// Important variables recently used in conflict analysis are 'bumped', + +void Internal::bump_variable (int lit) { + if (use_scores ()) + bump_variable_score (lit); + else + bump_queue (lit); +} + +// After every conflict the variable score increment is increased by a +// factor (if we are currently using scores). + +void Internal::bump_variable_score_inc () { + CADICAL_assert (use_scores ()); + CADICAL_assert (!evsids_limit_hit (score_inc)); + double f = 1e3 / opts.scorefactor; + double new_score_inc = score_inc * f; + if (evsids_limit_hit (new_score_inc)) { + LOG ("bumping %g increment by %g hits EVSIDS score limit", score_inc, + f); + rescale_variable_scores (); + new_score_inc = score_inc * f; + } + CADICAL_assert (!evsids_limit_hit (new_score_inc)); + LOG ("bumped score increment from %g to %g with factor %g", score_inc, + new_score_inc, f); + score_inc = new_score_inc; +} + +/*------------------------------------------------------------------------*/ + +struct analyze_bumped_rank { + Internal *internal; + analyze_bumped_rank (Internal *i) : internal (i) {} + typedef uint64_t Type; + Type operator() (const int &a) const { return internal->bumped (a); } +}; + +struct analyze_bumped_smaller { + Internal *internal; + analyze_bumped_smaller (Internal *i) : internal (i) {} + bool operator() (const int &a, const int &b) const { + const auto s = analyze_bumped_rank (internal) (a); + const auto t = analyze_bumped_rank (internal) (b); + return s < t; + } +}; + +/*------------------------------------------------------------------------*/ + +void Internal::bump_variables () { + + CADICAL_assert (opts.bump); + + START (bump); + + if (!use_scores ()) { + + // Variables are bumped in the order they are in the current decision + // queue. This maintains relative order between bumped variables in + // the queue and seems to work best. We also experimented with + // focusing on variables of the last decision level, but results were + // mixed. + + MSORT (opts.radixsortlim, analyzed.begin (), analyzed.end (), + analyze_bumped_rank (this), analyze_bumped_smaller (this)); + } + + for (const auto &lit : analyzed) + bump_variable (lit); + + if (use_scores ()) + bump_variable_score_inc (); + + STOP (bump); +} + +/*------------------------------------------------------------------------*/ + +// We use the glue time stamp table 'gtab' for fast glue computation. + +int Internal::recompute_glue (Clause *c) { + int res = 0; + const int64_t stamp = ++stats.recomputed; + for (const auto &lit : *c) { + int level = var (lit).level; + CADICAL_assert (gtab[level] <= stamp); + if (gtab[level] == stamp) + continue; + gtab[level] = stamp; + res++; + } + return res; +} + +// Clauses resolved since the last reduction are marked as 'used', their +// glue is recomputed and they are promoted if the glue shrinks. Note that +// promotion from 'tier3' to 'tier2' will set 'used' to '2'. + +inline void Internal::bump_clause (Clause *c) { + LOG (c, "bumping"); + c->used = max_used; + if (c->hyper) + return; + if (!c->redundant) + return; + int new_glue = recompute_glue (c); + if (new_glue < c->glue) + promote_clause (c, new_glue); + + const size_t glue = + std::min ((size_t) c->glue, stats.used[stable].size () - 1); + ++stats.used[stable][glue]; + ++stats.bump_used[stable]; +} + +void Internal::bump_clause2 (Clause *c) { bump_clause (c); } +/*------------------------------------------------------------------------*/ + +// During conflict analysis literals not seen yet either become part of the +// first unique implication point (UIP) clause (if on lower decision level), +// are dropped (if fixed), or are resolved away (if on the current decision +// level and different from the first UIP). At the same time we update the +// number of seen literals on a decision level. This helps conflict clause +// minimization. The number of seen levels is the glucose level (also +// called 'glue', or 'LBD'). + +inline void Internal::analyze_literal (int lit, int &open, + int &resolvent_size, + int &antecedent_size) { + CADICAL_assert (lit); + Var &v = var (lit); + Flags &f = flags (lit); + + if (!v.level) { + if (f.seen || !lrat) + return; + f.seen = true; + unit_analyzed.push_back (lit); + CADICAL_assert (val (lit) < 0); + int64_t id = unit_id (-lit); + unit_chain.push_back (id); + return; + } + ++antecedent_size; + if (f.seen) + return; + + // before marking as seen, get reason and check for missed unit + + CADICAL_assert (val (lit) < 0); + CADICAL_assert (v.level <= level); + if (v.reason == external_reason) { + CADICAL_assert (!opts.exteagerreasons); + v.reason = learn_external_reason_clause (-lit, 0, true); + if (!v.reason) { // actually a unit + --antecedent_size; + LOG ("%d unit after explanation", -lit); + if (f.seen || !lrat) + return; + f.seen = true; + unit_analyzed.push_back (lit); + CADICAL_assert (val (lit) < 0); + const unsigned uidx = vlit (-lit); + int64_t id = unit_clauses (uidx); + CADICAL_assert (id); + unit_chain.push_back (id); + return; + } + } + + f.seen = true; + analyzed.push_back (lit); + + CADICAL_assert (v.reason != external_reason); + if (v.level < level) + clause.push_back (lit); + Level &l = control[v.level]; + if (!l.seen.count++) { + LOG ("found new level %d contributing to conflict", v.level); + levels.push_back (v.level); + } + if (v.trail < l.seen.trail) + l.seen.trail = v.trail; + ++resolvent_size; + LOG ("analyzed literal %d assigned at level %d", lit, v.level); + if (v.level == level) + open++; +} + +inline void Internal::analyze_reason (int lit, Clause *reason, int &open, + int &resolvent_size, + int &antecedent_size) { + CADICAL_assert (reason); + CADICAL_assert (reason != external_reason); + bump_clause (reason); + if (lrat) + lrat_chain.push_back (reason->id); + for (const auto &other : *reason) + if (other != lit) + analyze_literal (other, open, resolvent_size, antecedent_size); +} + +/*------------------------------------------------------------------------*/ + +// This is an idea which was implicit in MapleCOMSPS 2016 for 'limit = 1'. +// See also the paragraph on 'bumping reason side literals' in their SAT'16 +// paper [LiangGaneshPoupartCzarnecki-SAT'16]. Reason side bumping was +// performed exactly when 'LRB' based decision heuristics was used, which in +// the original version was enabled after 10000 conflicts until a time limit +// of 2500 seconds was reached (half of the competition time limit). The +// Maple / Glucose / MiniSAT evolution winning the SAT race in 2019 made +// the schedule of reason side bumping deterministic, i.e., avoiding a time +// limit, by switching between 'LRB' and 'VSIDS' in an interval of initially +// 30 million propagations, which then is increased geometrically by 10%. + +inline bool Internal::bump_also_reason_literal (int lit) { + CADICAL_assert (lit); + CADICAL_assert (val (lit) < 0); + Flags &f = flags (lit); + if (f.seen) + return false; + const Var &v = var (lit); + if (!v.level) + return false; + f.seen = true; + analyzed.push_back (lit); + LOG ("bumping also reason literal %d assigned at level %d", lit, v.level); + return true; +} + +// We experimented with deeper reason bumping without much success though. + +inline void Internal::bump_also_reason_literals (int lit, int depth_limit, + size_t analyzed_limit) { + CADICAL_assert (lit); + CADICAL_assert (depth_limit > 0); + const Var &v = var (lit); + CADICAL_assert (val (lit)); + if (!v.level) + return; + Clause *reason = v.reason; + if (!reason || reason == external_reason) + return; + stats.ticks.search[stable]++; + for (const auto &other : *reason) { + if (other == lit) + continue; + if (!bump_also_reason_literal (other)) + continue; + if (depth_limit < 2) + continue; + bump_also_reason_literals (-other, depth_limit - 1, analyzed_limit); + if (analyzed.size () > analyzed_limit) + break; + } +} + +inline void Internal::bump_also_all_reason_literals () { + CADICAL_assert (opts.bump); + if (!opts.bumpreason) + return; + if (averages.current.decisions > opts.bumpreasonrate) { + LOG ("decisions per conflict rate %g > limit %d", + (double) averages.current.decisions, opts.bumpreasonrate); + return; + } + if (delay[stable].bumpreasons.limit) { + LOG ("delaying reason bumping %" PRId64 " more times", + delay[stable].bumpreasons.limit); + delay[stable].bumpreasons.limit--; + return; + } + CADICAL_assert (opts.bumpreasondepth > 0); + const int depth_limit = opts.bumpreasondepth + stable; + size_t saved_analyzed = analyzed.size (); + size_t analyzed_limit = saved_analyzed * opts.bumpreasonlimit; + for (const auto &lit : clause) + if (analyzed.size () <= analyzed_limit) + bump_also_reason_literals (-lit, depth_limit, analyzed_limit); + else + break; + if (analyzed.size () > analyzed_limit) { + LOG ("not bumping reason side literals as limit exhausted"); + for (size_t i = saved_analyzed; i != analyzed.size (); i++) { + const int lit = analyzed[i]; + Flags &f = flags (lit); + CADICAL_assert (f.seen); + f.seen = false; + } + delay[stable].bumpreasons.interval++; + analyzed.resize (saved_analyzed); + } else { + LOG ("bumping reasons up to depth %d", opts.bumpreasondepth); + delay[stable].bumpreasons.interval /= 2; + } + LOG ("delay internal %" PRId64, delay[stable].bumpreasons.interval); + delay[stable].bumpreasons.limit = delay[stable].bumpreasons.interval; +} + +/*------------------------------------------------------------------------*/ + +void Internal::clear_unit_analyzed_literals () { + LOG ("clearing %zd unit analyzed literals", unit_analyzed.size ()); + for (const auto &lit : unit_analyzed) { + Flags &f = flags (lit); + CADICAL_assert (f.seen); + CADICAL_assert (!var (lit).level); + f.seen = false; + CADICAL_assert (!f.keep); + CADICAL_assert (!f.poison); + CADICAL_assert (!f.removable); + } + unit_analyzed.clear (); +} + +void Internal::clear_analyzed_literals () { + LOG ("clearing %zd analyzed literals", analyzed.size ()); + for (const auto &lit : analyzed) { + Flags &f = flags (lit); + CADICAL_assert (f.seen); + f.seen = false; + CADICAL_assert (!f.keep); + CADICAL_assert (!f.poison); + CADICAL_assert (!f.removable); + } + analyzed.clear (); +#if 0 // to expensive, even for debugging mode + if (unit_analyzed.size ()) + return; + for (auto idx : vars) { + Flags &f = flags (idx); + CADICAL_assert (!f.seen); + } +#endif +} + +void Internal::clear_analyzed_levels () { + LOG ("clearing %zd analyzed levels", levels.size ()); + for (const auto &l : levels) + if (l < (int) control.size ()) + control[l].reset (); + levels.clear (); +} + +/*------------------------------------------------------------------------*/ + +// 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 analyze_trail_negative_rank { + Internal *internal; + analyze_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 analyze_trail_larger { + Internal *internal; + analyze_trail_larger (Internal *s) : internal (s) {} + bool operator() (const int &a, const int &b) const { + return analyze_trail_negative_rank (internal) (a) < + analyze_trail_negative_rank (internal) (b); + } +}; + +/*------------------------------------------------------------------------*/ + +// Generate new driving clause and compute jump level. + +Clause *Internal::new_driving_clause (const int glue, int &jump) { + + const size_t size = clause.size (); + Clause *res; + + if (!size) { + + jump = 0; + res = 0; + + } else if (size == 1) { + + iterating = true; + jump = 0; + res = 0; + + } else { + + CADICAL_assert (clause.size () > 1); + + // We have to get the last assigned literals into the watch position. + // Sorting all literals with respect to reverse assignment order is + // overkill but seems to get slightly faster run-time. For 'minimize' + // we sort the literals too heuristically along the trail order (so in + // the opposite order) with the hope to hit the recursion limit less + // frequently. Thus sorting effort is doubled here. + // + MSORT (opts.radixsortlim, clause.begin (), clause.end (), + analyze_trail_negative_rank (this), analyze_trail_larger (this)); + + jump = var (clause[1]).level; + res = new_learned_redundant_clause (glue); + res->used = 1 + (glue <= opts.reducetier2glue); + } + + LOG ("jump level %d", jump); + + return res; +} + +/*------------------------------------------------------------------------*/ + +// determine the OTFS level for OTFS. Unlike the find_conflict_level, we do +// not have to fix the clause + +inline int Internal::otfs_find_backtrack_level (int &forced) { + CADICAL_assert (opts.otfs); + int res = 0; + + for (const auto &lit : *conflict) { + const int tmp = var (lit).level; + if (tmp == level) { + forced = lit; + } else if (tmp > res) { + res = tmp; + LOG ("bt level is now %d due to %d", res, lit); + } + } + return res; +} + +/*------------------------------------------------------------------------*/ + +// If chronological backtracking is enabled we need to find the actual +// conflict level and then potentially can also reuse the conflict clause +// as driving clause instead of deriving a redundant new driving clause +// (forcing 'forced') if the number 'count' of literals in conflict assigned +// at the conflict level is exactly one. + +inline int Internal::find_conflict_level (int &forced) { + + CADICAL_assert (conflict); + CADICAL_assert (opts.chrono || opts.otfs || external_prop); + + int res = 0, count = 0; + + forced = 0; + + for (const auto &lit : *conflict) { + const int tmp = var (lit).level; + if (tmp > res) { + res = tmp; + forced = lit; + count = 1; + } else if (tmp == res) { + count++; + if (res == level && count > 1) + break; + } + } + + LOG ("%d literals on actual conflict level %d", count, res); + + const int size = conflict->size; + int *lits = conflict->literals; + + // Move the two highest level literals to the front. + // + for (int i = 0; i < 2; i++) { + + const int lit = lits[i]; + + int highest_position = i; + int highest_literal = lit; + int highest_level = var (highest_literal).level; + + for (int j = i + 1; j < size; j++) { + const int other = lits[j]; + const int tmp = var (other).level; + if (highest_level >= tmp) + continue; + highest_literal = other; + highest_position = j; + highest_level = tmp; + if (highest_level == res) + break; + } + + // No unwatched higher assignment level literal. + // + if (highest_position == i) + continue; + + if (highest_position > 1) { + LOG (conflict, "unwatch %d in", lit); + remove_watch (watches (lit), conflict); + } + + lits[highest_position] = lit; + lits[i] = highest_literal; + + if (highest_position > 1) + watch_literal (highest_literal, lits[!i], conflict); + } + + // Only if the number of highest level literals in the conflict is one + // then we can reuse the conflict clause as driving clause for 'forced'. + // + if (count != 1) + forced = 0; + + return res; +} + +/*------------------------------------------------------------------------*/ + +inline int Internal::determine_actual_backtrack_level (int jump) { + + int res; + + CADICAL_assert (level > jump); + + if (!opts.chrono) { + res = jump; + LOG ("chronological backtracking disabled using jump level %d", res); + } else if (opts.chronoalways) { + stats.chrono++; + res = level - 1; + LOG ("forced chronological backtracking to level %d", res); + } else if (jump >= level - 1) { + res = jump; + LOG ("jump level identical to chronological backtrack level %d", res); + } else if ((size_t) jump < assumptions.size ()) { + res = jump; + LOG ("using jump level %d since it is lower than assumption level %zd", + res, assumptions.size ()); + } else if (level - jump > opts.chronolevelim) { + stats.chrono++; + res = level - 1; + LOG ("back-jumping over %d > %d levels prohibited" + "thus backtracking chronologically to level %d", + level - jump, opts.chronolevelim, res); + } else if (opts.chronoreusetrail) { + int best_idx = 0, best_pos = 0; + + if (use_scores ()) { + for (size_t i = control[jump + 1].trail; i < trail.size (); i++) { + const int idx = abs (trail[i]); + if (best_idx && !score_smaller (this) (best_idx, idx)) + continue; + best_idx = idx; + best_pos = i; + } + LOG ("best variable score %g", score (best_idx)); + } else { + for (size_t i = control[jump + 1].trail; i < trail.size (); i++) { + const int idx = abs (trail[i]); + if (best_idx && bumped (best_idx) >= bumped (idx)) + continue; + best_idx = idx; + best_pos = i; + } + LOG ("best variable bumped %" PRId64 "", bumped (best_idx)); + } + CADICAL_assert (best_idx); + LOG ("best variable %d at trail position %d", best_idx, best_pos); + + // Now find the frame and decision level in the control stack of that + // best variable index. Note that, as in 'reuse_trail', the frame + // 'control[i]' for decision level 'i' contains the trail before that + // decision level, i.e., the decision 'control[i].decision' sits at + // 'control[i].trail' in the trail and we thus have to check the level + // of the control frame one higher than at the result level. + // + res = jump; + while (res < level - 1 && control[res + 1].trail <= best_pos) + res++; + + if (res == jump) + LOG ("default non-chronological back-jumping to level %d", res); + else { + stats.chrono++; + LOG ("chronological backtracking to level %d to reuse trail", res); + } + + } else { + res = jump; + LOG ("non-chronological back-jumping to level %d", res); + } + + return res; +} + +/*------------------------------------------------------------------------*/ + +void Internal::eagerly_subsume_recently_learned_clauses (Clause *c) { + CADICAL_assert (opts.eagersubsume); + LOG (c, "trying eager subsumption with"); + mark (c); + int64_t lim = stats.eagertried + opts.eagersubsumelim; + const auto begin = clauses.begin (); + auto it = clauses.end (); +#ifdef LOGGING + int64_t before = stats.eagersub; +#endif + while (it != begin && stats.eagertried++ <= lim) { + Clause *d = *--it; + if (c == d) + continue; + if (d->garbage) + continue; + if (!d->redundant) + continue; + int needed = c->size; + for (auto &lit : *d) { + if (marked (lit) <= 0) + continue; + if (!--needed) + break; + } + if (needed) + continue; + LOG (d, "eager subsumed"); + stats.eagersub++; + stats.subsumed++; + mark_garbage (d); + } + unmark (c); +#ifdef LOGGING + uint64_t subsumed = stats.eagersub - before; + if (subsumed) + LOG ("eagerly subsumed %" PRIu64 " clauses", subsumed); +#endif +} + +/*------------------------------------------------------------------------*/ + +Clause *Internal::on_the_fly_strengthen (Clause *new_conflict, int uip) { + CADICAL_assert (new_conflict); + CADICAL_assert (new_conflict->size > 2); + LOG (new_conflict, "applying OTFS on lit %d", uip); + auto sorted = std::vector<int> (); + sorted.reserve (new_conflict->size); + CADICAL_assert (sorted.empty ()); + ++stats.otfs.strengthened; + + int *lits = new_conflict->literals; + + CADICAL_assert (lits[0] == uip || lits[1] == uip); + const int other_init = lits[0] ^ lits[1] ^ uip; + + CADICAL_assert (mini_chain.empty ()); + + const int old_size = new_conflict->size; + int new_size = 0; + for (int i = 0; i < old_size; ++i) { + const int other = lits[i]; + sorted.push_back (other); + if (var (other).level) + lits[new_size++] = other; + } + + LOG (new_conflict, "removing all units in"); + + CADICAL_assert (lits[0] == uip || lits[1] == uip); + const int other = lits[0] ^ lits[1] ^ uip; + lits[0] = other; + lits[1] = lits[--new_size]; + LOG (new_conflict, "putting uip at pos 1"); + + if (other_init != other) + remove_watch (watches (other_init), new_conflict); + remove_watch (watches (uip), new_conflict); + + CADICAL_assert (!lrat || lrat_chain.back () == new_conflict->id); + if (lrat) { + CADICAL_assert (!lrat_chain.empty ()); + for (const auto &id : unit_chain) { + mini_chain.push_back (id); + } + const auto end = lrat_chain.rend (); + const auto begin = lrat_chain.rbegin (); + for (auto i = begin; i != end; i++) { + const auto id = *i; + mini_chain.push_back (id); + } + lrat_chain.clear (); + clear_unit_analyzed_literals (); + unit_chain.clear (); + } + CADICAL_assert (unit_analyzed.empty ()); + // sort the clause + { + int highest_pos = 0; + int highest_level = 0; + for (int i = 1; i < new_size; i++) { + const unsigned other = lits[i]; + CADICAL_assert (val (other) < 0); + const int level = var (other).level; + CADICAL_assert (level); + LOG ("checking %d", other); + if (level <= highest_level) + continue; + highest_pos = i; + highest_level = level; + } + LOG ("highest lit is %d", lits[highest_pos]); + if (highest_pos != 1) + swap (lits[1], lits[highest_pos]); + LOG ("removing %d literals", new_conflict->size - new_size); + + if (new_size == 1) { + LOG (new_conflict, "new size = 1, so interrupting"); + CADICAL_assert (!opts.exteagerreasons); + return 0; + } else { + otfs_strengthen_clause (new_conflict, uip, new_size, sorted); + CADICAL_assert (new_size == new_conflict->size); + } + } + + if (other_init != other) + watch_literal (other, lits[1], new_conflict); + else { + update_watch_size (watches (other), lits[1], new_conflict); + } + watch_literal (lits[1], other, new_conflict); + + LOG (new_conflict, "strengthened clause by OTFS"); + sorted.clear (); + + return new_conflict; +} + +/*------------------------------------------------------------------------*/ +inline void Internal::otfs_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' and units. +// +void Internal::otfs_strengthen_clause (Clause *c, int lit, int new_size, + const std::vector<int> &old) { + stats.strengthened++; + CADICAL_assert (c->size > 2); + (void) shrink_clause (c, new_size); + if (proof) { + proof->otfs_strengthen_clause (c, old, mini_chain); + } + if (!c->redundant) { + mark_removed (lit); + } + mini_chain.clear (); + c->used = true; + LOG (c, "strengthened"); + external->check_shrunken_clause (c); +} + +/*------------------------------------------------------------------------*/ + +// If the average number of decisions per conflict (analysis actually so not +// taking OTFS conflicts into account) is high we do not bump reasons. This +// is the function which updates the exponential moving decision rate +// average. + +void Internal::update_decision_rate_average () { + int64_t current = stats.decisions; + int64_t decisions = current - saved_decisions; + UPDATE_AVERAGE (averages.current.decisions, decisions); + saved_decisions = current; +} + +/*------------------------------------------------------------------------*/ + +// This is the main conflict analysis routine. It assumes that a conflict +// was found. Then we derive the 1st UIP clause, optionally minimize it, +// add it as learned clause, and then uses the clause for conflict directed +// back-jumping and flipping the 1st UIP literal. In combination with +// chronological backtracking (see discussion above) the algorithm becomes +// slightly more involved. + +void Internal::analyze () { + + START (analyze); + + CADICAL_assert (conflict); + CADICAL_assert (lrat_chain.empty ()); + CADICAL_assert (unit_chain.empty ()); + CADICAL_assert (unit_analyzed.empty ()); + CADICAL_assert (clause.empty ()); + + // First update moving averages of trail height at conflict. + // + UPDATE_AVERAGE (averages.current.trail.fast, num_assigned); + UPDATE_AVERAGE (averages.current.trail.slow, num_assigned); + update_decision_rate_average (); + + /*----------------------------------------------------------------------*/ + + if (external_prop && !external_prop_is_lazy && opts.exteagerreasons) { + explain_external_propagations (); + } + + if (opts.chrono || external_prop) { + + int forced; + + const int conflict_level = find_conflict_level (forced); + + // In principle we can perform conflict analysis as in non-chronological + // backtracking except if there is only one literal with the maximum + // assignment level in the clause. Then standard conflict analysis is + // unnecessary and we can use the conflict as a driving clause. In the + // pseudo code of the SAT'18 paper on chronological backtracking this + // corresponds to the situation handled in line 4-6 in Alg. 1, except + // that the pseudo code in the paper only backtracks while we eagerly + // assign the single literal on the highest decision level. + + if (forced) { + + CADICAL_assert (forced); + CADICAL_assert (conflict_level > 0); + LOG ("single highest level literal %d", forced); + + // The pseudo code in the SAT'18 paper actually backtracks to the + // 'second highest decision' level, while their code backtracks + // to 'conflict_level-1', which is more in the spirit of chronological + // backtracking anyhow and thus we also do the latter. + // + backtrack (conflict_level - 1); + + // if we are on decision level 0 search assign will learn unit + // so we need a valid chain here (of course if we are not on decision + // level 0 this will not result in a valid chain). + // we can just use build_chain_for_units in propagate + // + build_chain_for_units (forced, conflict, 0); + + LOG ("forcing %d", forced); + search_assign_driving (forced, conflict); + + conflict = 0; + STOP (analyze); + return; + } + + // Backtracking to the conflict level is in the pseudo code in the + // SAT'18 chronological backtracking paper, but not in their actual + // implementation. In principle we do not need to backtrack here. + // However, as a side effect of backtracking to the conflict level we + // set 'level' to the conflict level which then allows us to reuse the + // old 'analyze' code as is. The alternative (which we also tried but + // then abandoned) is to use 'conflict_level' instead of 'level' in the + // analysis, which however requires to pass it to the 'analyze_reason' + // and 'analyze_literal' functions. + // + backtrack (conflict_level); + } + + // Actual conflict on root level, thus formula unsatisfiable. + // + if (!level) { + learn_empty_clause (); + if (external->learner) + external->export_learned_empty_clause (); + STOP (analyze); + return; + } + + /*----------------------------------------------------------------------*/ + + // First derive the 1st UIP clause by going over literals assigned on the + // current decision level. Literals in the conflict are marked as 'seen' + // as well as all literals in reason clauses of already 'seen' literals on + // the current decision level. Thus the outer loop starts with the + // conflict clause as 'reason' and then uses the 'reason' of the next + // seen literal on the trail assigned on the current decision level. + // During this process maintain the number 'open' of seen literals on the + // current decision level with not yet processed 'reason'. As soon 'open' + // drops to one, we have found the first unique implication point. This + // is sound because the topological order in which literals are processed + // follows the assignment order and a more complex algorithm to find + // articulation points is not necessary. + // + Clause *reason = conflict; + LOG (reason, "analyzing conflict"); + + CADICAL_assert (clause.empty ()); + CADICAL_assert (lrat_chain.empty ()); + + const auto &t = &trail; + int i = t->size (); // Start at end-of-trail. + int open = 0; // Seen but not processed on this level. + int uip = 0; // The first UIP literal. + int resolvent_size = 0; // without the uip + int antecedent_size = 1; // with the uip and without unit literals + int conflict_size = 0; // without the uip and without unit literals + int resolved = 0; // number of resolution (0 = clause in CNF) + const bool otfs = opts.otfs; + + for (;;) { + antecedent_size = 1; // for uip + analyze_reason (uip, reason, open, resolvent_size, antecedent_size); + if (resolved == 0) + conflict_size = antecedent_size - 1; + CADICAL_assert (resolvent_size == open + (int) clause.size ()); + + if (otfs && resolved > 0 && antecedent_size > 2 && + resolvent_size < antecedent_size) { + CADICAL_assert (reason != conflict); + LOG (analyzed, "found candidate for OTFS conflict"); + LOG (clause, "found candidate for OTFS conflict"); + LOG (reason, "found candidate (size %d) for OTFS resolvent", + antecedent_size); + const int other = reason->literals[0] ^ reason->literals[1] ^ uip; + CADICAL_assert (other != uip); + reason = on_the_fly_strengthen (reason, uip); + if (opts.bump) + bump_variables (); + + CADICAL_assert (conflict_size); + if (!reason) { + uip = -other; + CADICAL_assert (open == 1); + LOG ("clause is actually unit %d, stopping", -uip); + reverse (begin (mini_chain), end (mini_chain)); + for (auto id : mini_chain) + lrat_chain.push_back (id); + mini_chain.clear (); + clear_analyzed_levels (); + CADICAL_assert (!opts.exteagerreasons); + clause.clear (); + break; + } + CADICAL_assert (conflict_size >= 2); + + if (resolved == 1 && resolvent_size < conflict_size) { + // here both clauses are part of the CNF, so one subsumes the other + otfs_subsume_clause (reason, conflict); + LOG (reason, "changing conflict to"); + --conflict_size; + CADICAL_assert (conflict_size == reason->size); + ++stats.otfs.subsumed; + ++stats.subsumed; + } + + LOG (reason, "changing conflict to"); + conflict = reason; + if (open == 1) { + int forced = 0; + const int conflict_level = otfs_find_backtrack_level (forced); + int new_level = determine_actual_backtrack_level (conflict_level); + UPDATE_AVERAGE (averages.current.level, new_level); + backtrack (new_level); + + LOG ("forcing %d", forced); + search_assign_driving (forced, conflict); + + // Clean up. + // + conflict = 0; + clear_analyzed_literals (); + clear_analyzed_levels (); + clause.clear (); + STOP (analyze); + return; + } + + stats.conflicts++; + + clear_analyzed_literals (); + clear_analyzed_levels (); + clause.clear (); + resolvent_size = 0; + antecedent_size = 1; + resolved = 0; + open = 0; + analyze_reason (0, reason, open, resolvent_size, antecedent_size); + conflict_size = antecedent_size - 1; + CADICAL_assert (open > 1); + } + + ++resolved; + + uip = 0; + while (!uip) { + CADICAL_assert (i > 0); + const int lit = (*t)[--i]; + if (!flags (lit).seen) + continue; + if (var (lit).level == level) + uip = lit; + } + if (!--open) + break; + reason = var (uip).reason; + if (reason == external_reason) { + CADICAL_assert (!opts.exteagerreasons); + reason = learn_external_reason_clause (-uip, 0, true); + var (uip).reason = reason; + } + CADICAL_assert (reason != external_reason); + LOG (reason, "analyzing %d reason", uip); + CADICAL_assert (resolvent_size); + --resolvent_size; + } + LOG ("first UIP %d", uip); + clause.push_back (-uip); + + // Update glue and learned (1st UIP literals) statistics. + // + int size = (int) clause.size (); + const int glue = (int) levels.size () - 1; + LOG (clause, "1st UIP size %d and glue %d clause", size, glue); + UPDATE_AVERAGE (averages.current.glue.fast, glue); + UPDATE_AVERAGE (averages.current.glue.slow, glue); + stats.learned.literals += size; + stats.learned.clauses++; + CADICAL_assert (glue < size); + + // up to this point lrat_chain contains the proof for current clause in + // reversed order. in minimize and shrink the clause is changed and + // therefore lrat_chain has to be extended. Unfortunately we cannot create + // the chain directly during minimization (or shrinking) but afterwards we + // can calculate it pretty easily and even better the same algorithm works + // for both shrinking and minimization. + + // Minimize the 1st UIP clause as pioneered by Niklas Soerensson in + // MiniSAT and described in our joint SAT'09 paper. + // + if (size > 1) { + if (opts.shrink) + shrink_and_minimize_clause (); + else if (opts.minimize) + minimize_clause (); + + size = (int) clause.size (); + + // Update decision heuristics. + // + if (opts.bump) { + bump_also_all_reason_literals (); + bump_variables (); + } + + if (external->learner) + external->export_learned_large_clause (clause); + } else if (external->learner) + external->export_learned_unit_clause (-uip); + + // Update actual size statistics. + // + stats.units += (size == 1); + stats.binaries += (size == 2); + UPDATE_AVERAGE (averages.current.size, size); + + // reverse lrat_chain. We could probably work with reversed iterators + // (views) to be more efficient but we would have to distinguish in proof + // + if (lrat) { + LOG (unit_chain, "unit chain: "); + for (auto id : unit_chain) + lrat_chain.push_back (id); + unit_chain.clear (); + reverse (lrat_chain.begin (), lrat_chain.end ()); + } + + // Determine back-jump level, learn driving clause, backtrack and assign + // flipped 1st UIP literal. + // + int jump; + Clause *driving_clause = new_driving_clause (glue, jump); + UPDATE_AVERAGE (averages.current.jump, jump); + + int new_level = determine_actual_backtrack_level (jump); + UPDATE_AVERAGE (averages.current.level, new_level); + backtrack (new_level); + + // It should hold that (!level <=> size == 1) + // and (!uip <=> size == 0) + // this means either we have already learned a clause => size >= 2 + // in this case we will not learn empty clause or unit here + // or we haven't actually learned a clause in new_driving_clause + // then lrat_chain is still valid and we will learn a unit or empty clause + // + if (uip) { + search_assign_driving (-uip, driving_clause); + } else + learn_empty_clause (); + + if (stable) + reluctant.tick (); // Reluctant has its own 'conflict' counter. + + // Clean up. + // + clear_analyzed_literals (); + clear_unit_analyzed_literals (); + clear_analyzed_levels (); + clause.clear (); + conflict = 0; + + lrat_chain.clear (); + STOP (analyze); + + if (driving_clause && opts.eagersubsume) + eagerly_subsume_recently_learned_clauses (driving_clause); + + if (lim.recompute_tier <= stats.conflicts) + recompute_tier (); +} + +// We wait reporting a learned unit until propagation of that unit is +// completed. Otherwise the 'i' report gives the number of remaining +// variables before propagating the unit (and hides the actual remaining +// variables after propagating it). + +void Internal::iterate () { + iterating = false; + report ('i'); +} + +} // namespace CaDiCaL + +ABC_NAMESPACE_IMPL_END diff --git a/src/sat/cadical/cadical_arena.cpp b/src/sat/cadical/cadical_arena.cpp new file mode 100644 index 0000000000..a8a9b183f3 --- /dev/null +++ b/src/sat/cadical/cadical_arena.cpp @@ -0,0 +1,36 @@ +#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 diff --git a/src/sat/cadical/cadical_assume.cpp b/src/sat/cadical/cadical_assume.cpp new file mode 100644 index 0000000000..e7d5b79b79 --- /dev/null +++ b/src/sat/cadical/cadical_assume.cpp @@ -0,0 +1,613 @@ +#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 diff --git a/src/sat/cadical/cadical_averages.cpp b/src/sat/cadical/cadical_averages.cpp new file mode 100644 index 0000000000..d40a98cdb9 --- /dev/null +++ b/src/sat/cadical/cadical_averages.cpp @@ -0,0 +1,40 @@ +#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 diff --git a/src/sat/cadical/cadical_backtrack.cpp b/src/sat/cadical/cadical_backtrack.cpp new file mode 100644 index 0000000000..b3c519ba29 --- /dev/null +++ b/src/sat/cadical/cadical_backtrack.cpp @@ -0,0 +1,179 @@ +#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 diff --git a/src/sat/cadical/cadical_backward.cpp b/src/sat/cadical/cadical_backward.cpp new file mode 100644 index 0000000000..5778029a50 --- /dev/null +++ b/src/sat/cadical/cadical_backward.cpp @@ -0,0 +1,237 @@ +#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 diff --git a/src/sat/cadical/cadical_bins.cpp b/src/sat/cadical/cadical_bins.cpp new file mode 100644 index 0000000000..fba96f2164 --- /dev/null +++ b/src/sat/cadical/cadical_bins.cpp @@ -0,0 +1,28 @@ +#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 diff --git a/src/sat/cadical/cadical_block.cpp b/src/sat/cadical/cadical_block.cpp new file mode 100644 index 0000000000..12f852f082 --- /dev/null +++ b/src/sat/cadical/cadical_block.cpp @@ -0,0 +1,830 @@ +#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 diff --git a/src/sat/cadical/cadical_ccadical.cpp b/src/sat/cadical/cadical_ccadical.cpp new file mode 100644 index 0000000000..ae5c1093f1 --- /dev/null +++ b/src/sat/cadical/cadical_ccadical.cpp @@ -0,0 +1,211 @@ +#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 diff --git a/src/sat/cadical/cadical_checker.cpp b/src/sat/cadical/cadical_checker.cpp new file mode 100644 index 0000000000..63a5f2b10e --- /dev/null +++ b/src/sat/cadical/cadical_checker.cpp @@ -0,0 +1,654 @@ +#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 diff --git a/src/sat/cadical/cadical_clause.cpp b/src/sat/cadical/cadical_clause.cpp new file mode 100644 index 0000000000..1c84994a64 --- /dev/null +++ b/src/sat/cadical/cadical_clause.cpp @@ -0,0 +1,649 @@ +#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 diff --git a/src/sat/cadical/cadical_collect.cpp b/src/sat/cadical/cadical_collect.cpp new file mode 100644 index 0000000000..c36e063b2a --- /dev/null +++ b/src/sat/cadical/cadical_collect.cpp @@ -0,0 +1,551 @@ +#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 diff --git a/src/sat/cadical/cadical_compact.cpp b/src/sat/cadical/cadical_compact.cpp new file mode 100644 index 0000000000..6747218cbf --- /dev/null +++ b/src/sat/cadical/cadical_compact.cpp @@ -0,0 +1,557 @@ +#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 diff --git a/src/sat/cadical/cadical_condition.cpp b/src/sat/cadical/cadical_condition.cpp new file mode 100644 index 0000000000..8c2f72cf74 --- /dev/null +++ b/src/sat/cadical/cadical_condition.cpp @@ -0,0 +1,946 @@ +#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 diff --git a/src/sat/cadical/cadical_config.cpp b/src/sat/cadical/cadical_config.cpp new file mode 100644 index 0000000000..d4268790cf --- /dev/null +++ b/src/sat/cadical/cadical_config.cpp @@ -0,0 +1,107 @@ +#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 diff --git a/src/sat/cadical/cadical_congruence.cpp b/src/sat/cadical/cadical_congruence.cpp new file mode 100644 index 0000000000..8fbceee24e --- /dev/null +++ b/src/sat/cadical/cadical_congruence.cpp @@ -0,0 +1,7567 @@ +#include "global.h" + +#include "congruence.hpp" +#include "internal.hpp" +#include <algorithm> +#include <cstdint> +#include <iterator> +#include <utility> +#include <vector> + +ABC_NAMESPACE_IMPL_START + +namespace CaDiCaL { + +Closure::Closure (Internal *i) + : internal (i), table (128, Hash (nonces)) +#ifdef LOGGING + , + fresh_id (internal->clause_id) +#endif +{ +} + +char &Closure::lazy_propagated (int lit) { + return lazy_propagated_idx[internal->vidx (lit)]; +} + +void update_ite_flags (Gate *g) { + int8_t f = g->degenerated_ite; + const int lhs = g->lhs; + const int cond = g->rhs [0]; + const int then_lit = g->rhs[1]; + const int else_lit = g->rhs[2]; + + if (lhs == cond) { + f |= Special_ITE_GATE::NO_NEG_THEN; + f |= Special_ITE_GATE::NO_PLUS_ELSE; + } + if (lhs == -cond) { + f |= Special_ITE_GATE::NO_PLUS_THEN; + f |= Special_ITE_GATE::NO_NEG_ELSE; + } + if (lhs == then_lit) { + f |= Special_ITE_GATE::NO_PLUS_THEN; + f |= Special_ITE_GATE::NO_NEG_THEN; + } + if (lhs == else_lit) { + f |= Special_ITE_GATE::NO_PLUS_ELSE; + f |= Special_ITE_GATE::NO_NEG_ELSE; + } + g->degenerated_ite = f; + CADICAL_assert (lhs != -then_lit); + CADICAL_assert (lhs != -else_lit); + CADICAL_assert (cond != then_lit); + CADICAL_assert (cond != else_lit); + CADICAL_assert (cond != -then_lit); + CADICAL_assert (cond != -else_lit); +} + +void check_correct_ite_flags (const Gate *const g) { +#ifndef CADICAL_NDEBUG + const int8_t f = g->degenerated_ite; + const int lhs = g->lhs; + const int cond = g->rhs [0]; + const int then_lit = g->rhs[1]; + const int else_lit = g->rhs[2]; + CADICAL_assert (g->pos_lhs_ids.size () == 4); + if (g->pos_lhs_ids[0].clause == nullptr) + CADICAL_assert ((f & Special_ITE_GATE::NO_PLUS_THEN)); + if (g->pos_lhs_ids[1].clause == nullptr) + CADICAL_assert (f & Special_ITE_GATE::NO_NEG_THEN); + if (g->pos_lhs_ids[2].clause == nullptr) + CADICAL_assert (f & Special_ITE_GATE::NO_PLUS_ELSE); + if (g->pos_lhs_ids[3].clause == nullptr) + CADICAL_assert (f & Special_ITE_GATE::NO_NEG_ELSE); + if (lhs == cond) { + CADICAL_assert (f & Special_ITE_GATE::NO_NEG_THEN); + CADICAL_assert (f & Special_ITE_GATE::NO_PLUS_ELSE); + } + if (lhs == -cond) { + CADICAL_assert (f & Special_ITE_GATE::NO_PLUS_THEN); + CADICAL_assert (f & Special_ITE_GATE::NO_NEG_ELSE); + } + if (lhs == then_lit) { + CADICAL_assert (f & Special_ITE_GATE::NO_PLUS_THEN); + CADICAL_assert (f & Special_ITE_GATE::NO_NEG_THEN); + } + if (lhs == else_lit) { + CADICAL_assert (f & Special_ITE_GATE::NO_PLUS_ELSE); + CADICAL_assert (f & Special_ITE_GATE::NO_NEG_ELSE); + } + CADICAL_assert (lhs != -then_lit); + CADICAL_assert (lhs != -else_lit); + CADICAL_assert (cond != then_lit); + CADICAL_assert (cond != else_lit); + CADICAL_assert (cond != -then_lit); + CADICAL_assert (cond != -else_lit); +#else + (void)g; +#endif +} + +/*------------------------------------------------------------------------*/ + +static size_t hash_lits (std::array<int, 16> &nonces, + const vector<int> &lits) { + size_t hash = 0; + const auto end_nonces = end (nonces); + const auto begin_nonces = begin (nonces); + auto n = begin_nonces; + for (auto lit : lits) { + hash += lit; + hash *= *n++; + hash = (hash << 4) | (hash >> 60); + if (n == end_nonces) + n = begin_nonces; + } + hash ^= hash >> 32; + return hash; +} + +size_t Hash::operator() (const Gate *const g) const { + CADICAL_assert (hash_lits (nonces, g->rhs) == g->hash); + return g->hash; +} + +bool gate_contains (Gate *g, int lit) { + return find (begin (g->rhs), end (g->rhs), lit) != end (g->rhs); +} +/*------------------------------------------------------------------------*/ +struct compact_binary_rank { + typedef uint64_t Type; + uint64_t operator() (const CompactBinary &a) { + return ((uint64_t) a.lit1 << 32) + a.lit2; + }; +}; + +struct compact_binary_order { + bool operator() (const CompactBinary &a, const CompactBinary &b) { + return compact_binary_rank () (a) < compact_binary_rank () (b); + }; +}; + +bool Closure::find_binary (int lit, int other) const { + const auto offsize = + offsetsize[internal->vlit (lit)]; // in C++17: [offset, size] = + const auto offset = offsize.first; + const auto size = offsize.second; + const auto begin = std::begin (binaries) + offset; + const auto end = std::begin (binaries) + size; + CADICAL_assert (end <= std::end (binaries)); + const CompactBinary target = CompactBinary (nullptr, 0, lit, other); + auto it = std::lower_bound (begin, end, target, compact_binary_order ()); + // search_binary only returns a bool + bool found = (it != end && it->lit1 == lit && it->lit2 == other); + if (found) { + LOG ("found binary [%zd] %d %d", it->id, lit, other); + if (internal->lrat) + lrat_chain.push_back (it->id); + } + return found; +} + +void Closure::extract_binaries () { + if (!internal->opts.congruencebinaries) + return; + START (extractbinaries); + offsetsize.resize (internal->max_var * 2 + 3, make_pair (0, 0)); + + // in kissat this is done during watch clearing. TODO: consider doing this + // too. + for (Clause *c : internal->clauses) { + if (c->garbage) + continue; + if (c->redundant && c->size != 2) + continue; + if (c->size > 2) + continue; + CADICAL_assert (c->size == 2); + const int lit = c->literals[0]; + const int other = c->literals[1]; + const bool already_sorted = + internal->vlit (lit) < internal->vlit (other); + binaries.push_back (CompactBinary (c, c->id, + already_sorted ? lit : other, + already_sorted ? other : lit)); + } + + MSORT (internal->opts.radixsortlim, begin (binaries), end (binaries), + compact_binary_rank (), compact_binary_order ()); + + { + const size_t size = binaries.size (); + size_t i = 0; + while (i < size) { + CompactBinary bin = binaries[i]; + const int lit = bin.lit1; + size_t j = i; + while (j < size && binaries[j].lit1 == lit) { + ++j; + } + CADICAL_assert (j >= i); + CADICAL_assert (j <= size); + offsetsize[internal->vlit (lit)] = make_pair (i, j); + i = j; + } + } + + size_t extracted = 0, already_present = 0, duplicated = 0; + + const size_t size = internal->clauses.size (); + for (size_t i = 0; i < size; ++i) { + Clause *d = internal->clauses[i]; // binary clauses are appended, so + // reallocation possible + if (d->garbage) + continue; + if (d->redundant) + continue; + if (d->size != 3) + continue; + const int *lits = d->literals; + const int a = lits[0]; + const int b = lits[1]; + const int c = lits[2]; // obfuscating d->literals[2] which triggers an error in pedandic mode + if (internal->val (a)) + continue; + if (internal->val (b)) + continue; + if (internal->val (c)) + continue; + int l = 0, k = 0; + if (find_binary (-a, b) || find_binary (-a, c)) { + l = b, k = c; + } else if (find_binary (-b, a) || find_binary (-b, c)) { + l = a, k = c; + } else if (find_binary (-c, a) || find_binary (-c, b)) { + l = a, k = b; + } else + continue; + LOG (d, "strengthening"); + if (!find_binary (l, k)) { + if (internal->lrat) + lrat_chain.push_back (d->id); + add_binary_clause (l, k); + ++extracted; + } else { + ++already_present; + if (internal->lrat) + lrat_chain.clear (); + } + } + lrat_chain.clear (); + + offsetsize.clear (); + + // kissat has code to remove duplicates, which we have already removed + // before starting congruence + MSORT (internal->opts.radixsortlim, begin (binaries), end (binaries), + compact_binary_rank (), compact_binary_order ()); + const size_t new_size = binaries.size (); + { + size_t i = 0; + for (size_t j = 1; j < new_size; ++j) { + CADICAL_assert (i < j); + if (binaries[i].lit1 == binaries[j].lit1 && + binaries[i].lit2 == binaries[j].lit2) { + // subsuming later clause + subsume_clause (binaries[i].clause, + binaries[j].clause); // the local one is specialized + ++duplicated; + } else { + binaries[++i] = binaries[j]; + } + } + CADICAL_assert (i <= new_size); + binaries.resize (i); + } + binaries.clear (); + STOP (extractbinaries); + LOG ("extracted %zu binaries (plus %zu already present and %zu " + "duplicates)", + extracted, already_present, duplicated); +} + +/*------------------------------------------------------------------------*/ +// marking structure for congruence closure, by reference +signed char &Closure::marked (int lit) { + CADICAL_assert (internal->vlit (lit) < marks.size ()); + return marks[internal->vlit (lit)]; +} + +void Closure::unmark_all () { + for (auto lit : internal->analyzed) { + CADICAL_assert (marked (lit)); + marked (lit) = 0; + } + internal->analyzed.clear (); +} + +void Closure::set_mu1_reason (int lit, Clause *c) { + CADICAL_assert (marked (lit) & 1); + LOG (c, "mu1 %d -> %zd", lit, c->id); + mu1_ids[internal->vlit (lit)] = LitClausePair (lit, c); +} + +void Closure::set_mu2_reason (int lit, Clause *c) { + CADICAL_assert (marked (lit) & 2); + if (!internal->lrat) + return; + LOG (c, "mu2 %d -> %zd", lit, c->id); + mu2_ids[internal->vlit (lit)] = LitClausePair (lit, c); +} + +void Closure::set_mu4_reason (int lit, Clause *c) { + CADICAL_assert (marked (lit) & 4); + if (!internal->lrat) + return; + LOG (c, "mu4 %d -> %zd", lit, c->id); + mu4_ids[internal->vlit (lit)] = LitClausePair (lit, c); +} + +LitClausePair Closure::marked_mu1 (int lit) { + return mu1_ids[internal->vlit (lit)]; +} + +LitClausePair Closure::marked_mu2 (int lit) { + return mu2_ids[internal->vlit (lit)]; +} + +LitClausePair Closure::marked_mu4 (int lit) { + return mu4_ids[internal->vlit (lit)]; +} + +struct sort_literals_by_var_rank { + CaDiCaL::Internal *internal; + sort_literals_by_var_rank (Internal *i) : internal (i) {} + + typedef uint64_t Type; + + Type operator() (const int &a) const { return internal->vlit (a); } +}; +struct sort_literals_by_var_rank_except { + CaDiCaL::Internal *internal; + int lhs; + int except; + sort_literals_by_var_rank_except (Internal *i, int my_lhs, int except2) + : internal (i), lhs (my_lhs), except (except2) {} + sort_literals_by_var_rank_except (Internal *i, int my_lhs) + : internal (i), lhs (my_lhs), except (0) {} + typedef uint64_t Type; + Type operator() (const int &a) const { + Type res = 0; + if (abs (a) == abs (except)) + res = 1 - (a > 0); + else if (abs (a) == abs (lhs)) + res = 3 - (a > 0); + else + res = internal->vlit (a) + 2; // probably +2 enough + return ~res; + } +}; + +struct sort_literals_by_var_smaller_except { + CaDiCaL::Internal *internal; + int lhs; + int except; + sort_literals_by_var_smaller_except (Internal *i, int my_lhs, int except2) + : internal (i), lhs (my_lhs), except (except2) {} + sort_literals_by_var_smaller_except (Internal *i, int my_lhs) + : internal (i), lhs (my_lhs), except (0) {} + bool operator() (const int &a, const int &b) const { + return sort_literals_by_var_rank_except (internal, lhs, except) (a) < + sort_literals_by_var_rank_except (internal, lhs, except) (b); + if (abs (a) == abs (except) && abs (b) != abs (except)) + return false; + if (abs (a) != abs (except) && abs (b) == abs (except)) + return true; + if (abs (a) == abs (lhs) && abs (b) != abs (lhs)) + return false; + if (abs (a) != abs (lhs) && abs (b) == abs (lhs)) + return true; + return sort_literals_by_var_rank (internal) (a) > + sort_literals_by_var_rank (internal) (b); + } +}; +struct sort_literals_by_var_smaller { + CaDiCaL::Internal *internal; + sort_literals_by_var_smaller (Internal *i) : internal (i) {} + bool operator() (const int &a, const int &b) const { + return sort_literals_by_var_rank (internal) (a) < + sort_literals_by_var_rank (internal) (b); + } +}; + +void Closure::sort_literals_by_var_except (vector<int> &rhs, int lhs, + int except2) { + MSORT (internal->opts.radixsortlim, begin (rhs), end (rhs), + sort_literals_by_var_rank_except (internal, lhs, except2), + sort_literals_by_var_smaller_except (internal, lhs, except2)); +} +void Closure::sort_literals_by_var (vector<int> &rhs) { + MSORT (internal->opts.radixsortlim, begin (rhs), end (rhs), + sort_literals_by_var_rank (internal), + sort_literals_by_var_smaller (internal)); +} +/*------------------------------------------------------------------------*/ +int &Closure::representative (int lit) { + CADICAL_assert (internal->vlit (lit) < representant.size ()); + return representant[internal->vlit (lit)]; +} +int Closure::representative (int lit) const { + CADICAL_assert (internal->vlit (lit) < representant.size ()); + return representant[internal->vlit (lit)]; +} + +int &Closure::eager_representative (int lit) { + CADICAL_assert (internal->vlit (lit) < eager_representant.size ()); + return eager_representant[internal->vlit (lit)]; +} + +int Closure::eager_representative (int lit) const { + CADICAL_assert (internal->vlit (lit) < eager_representant.size ()); + return eager_representant[internal->vlit (lit)]; +} + +int Closure::find_lrat_representative_with_marks (int lit) { + int res = lit; + int nxt = lit; + do { + res = nxt; + nxt = representative (nxt); + if (nxt != res) { + LOG ("%d has reason %" PRIu64, res, representative_id (res)); + lrat_chain.push_back (representative_id (res)); + } + } while (nxt != res || marked (nxt) || marked (-nxt)); + + return nxt; +} +int Closure::find_representative (int lit) { + int res = lit; + int nxt = lit; + do { + res = nxt; + nxt = representative (nxt); + } while (nxt != res); + + return res; +} + +int Closure::find_representative_and_compress (int lit, bool update_eager) { + LOG ("finding representative of %d", lit); + int res = lit; + int nxt = lit; + int path_length = 0; + do { + res = nxt; + nxt = representative (nxt); + ++path_length; + LOG ("updating %d -> %d", res, nxt); + } while (nxt != res); + + if (path_length > 2) { + LOG ("learning new rewriting from %d to %d (current path length: %d)", + lit, res, path_length); + if (update_eager) + eager_representative (lit) = res; + if (internal->lrat) { + produce_representative_lrat (lit); + Clause *equiv = add_tmp_binary_clause (-lit, res); + + if (equiv) { + representative_id (lit) = equiv->id; + if (update_eager) + eager_representative_id (lit) = equiv->id; + } + } + if (internal->lrat) + lrat_chain.clear (); + } else if (path_length == 2) { + if (update_eager) { + LOG ("updating information %d -> %d in eager", lit, res); + eager_representative (lit) = res; + if (internal->lrat) + eager_representative_id (lit) = representative_id (lit); + CADICAL_assert (!internal->lrat || eager_representative_id (lit)); + } + } + + if (lit != res) { + representative (lit) = res; + } + LOG ("representative of %d is %d", lit, res); + return res; +} + +void Closure::push_lrat_unit (int lit) { + if (!internal->lrat) + return; + CADICAL_assert (internal->val (lit) > 0); + LRAT_ID id = internal->unit_id (lit); + lrat_chain.push_back (id); +} + +int Closure::find_eager_representative (int lit) { + int res = lit; + int nxt = lit; + do { + res = nxt; + nxt = eager_representative (nxt); + } while (nxt != res); + + return res; +} + +int Closure::find_eager_representative_and_compress (int lit) { + if (!internal->lrat) + return find_representative (lit); + int res = lit; + int nxt = lit; + int path_length = 0; + do { + res = nxt; + nxt = eager_representative (nxt); + ++path_length; + } while (nxt != res); + + // CADICAL_assert (res == find_representative (lit)); + // we have to do path compression to support LRAT proofs + if (path_length > 2) { + LOG ("learning new rewriting from %d to %d (current path length: %d)", + lit, res, path_length); + std::vector<LRAT_ID> tmp_lrat_chain; + if (internal->lrat) { + tmp_lrat_chain = std::move (lrat_chain); + lrat_chain.clear (); + produce_eager_representative_lrat (lit); + } + eager_representative (lit) = res; + Clause *equiv = add_tmp_binary_clause (-lit, res); + equiv->hyper = true; + + if (internal->lrat && equiv) { + eager_representative_id (lit) = equiv->id; + } + if (internal->lrat) { + lrat_chain = std::move (tmp_lrat_chain); + } + } else if (path_length == 2) { + LOG ("duplicated information %d -> %d to eager with clause %" PRIu64, + lit, res, eager_representative_id (lit)); + CADICAL_assert (eager_representative (lit) == res); + CADICAL_assert (!internal->lrat || eager_representative_id (lit)); + } + CADICAL_assert (internal->clause.empty ()); + return res; +} + +void Closure::find_representative_and_compress_both (int lit) { + find_representative_and_compress (lit, false); + find_representative_and_compress (-lit, false); +} + +void Closure::import_lazy_and_find_eager_representative_and_compress_both ( + int lit) { + find_representative_and_compress (lit); + find_eager_representative_and_compress (lit); + find_representative_and_compress (-lit); + find_eager_representative_and_compress (-lit); +} + +void Closure::produce_representative_lrat (int lit) { + CADICAL_assert (internal->lrat); + LOG ("production of LRAT chain for %d with representative %" PRIu64, lit, + representative_id (lit)); + CADICAL_assert (internal->lrat); + CADICAL_assert (lrat_chain.empty ()); + int res = lit; + int nxt = lit; + CADICAL_assert (nxt != representative (nxt)); + do { + res = nxt; + nxt = representative (nxt); + if (nxt != res) { + LOG ("%d has reason %" PRIu64, res, representative_id (res)); + lrat_chain.push_back (representative_id (res)); + } + } while (nxt != res); +} + +void Closure::produce_eager_representative_lrat (int lit) { + CADICAL_assert (internal->lrat); + LOG ("production of LRAT chain for %d with representative %" PRIu64, lit, + eager_representative_id (lit)); + CADICAL_assert (internal->lrat); + CADICAL_assert (lrat_chain.empty ()); + int res = lit; + int nxt = lit; + CADICAL_assert (nxt != eager_representative (nxt)); + do { + res = nxt; + nxt = eager_representative (nxt); + if (nxt != res) { + LOG ("%d has reason %" PRIu64, res, eager_representative_id (res)); + lrat_chain.push_back (eager_representative_id (res)); + } + } while (nxt != res); +} + +LRAT_ID Closure::find_representative_lrat (int lit) { + if (!internal->lrat) + return 0; + int res = lit; +#ifndef CADICAL_NDEBUG + int nxt = representative (res); + CADICAL_assert (nxt == representative (res)); +#endif + LOG ("checking for existing LRAT chain for %d with clause %" PRIu64, lit, + eager_representative_id (res)); + CADICAL_assert (representative_id (res)); + return representative_id (res); +} + +LRAT_ID Closure::find_eager_representative_lrat (int lit) { + if (!internal->lrat) + return 0; + int res = lit; +#ifndef CADICAL_NDEBUG + int nxt = eager_representative (res); + CADICAL_assert (nxt == eager_representative (res)); +#endif + LOG ("checking for existing LRAT chain for %d with clause %" PRIu64, lit, + eager_representative_id (res)); + CADICAL_assert (eager_representative_id (res)); + return eager_representative_id (res); +} + +LRAT_ID &Closure::eager_representative_id (int lit) { + CADICAL_assert (internal->vlit (lit) < eager_representant_id.size ()); + return eager_representant_id[internal->vlit (lit)]; +} +LRAT_ID Closure::eager_representative_id (int lit) const { + CADICAL_assert (internal->vlit (lit) < eager_representant_id.size ()); + return eager_representant_id[internal->vlit (lit)]; +} + +LRAT_ID &Closure::representative_id (int lit) { + CADICAL_assert (internal->vlit (lit) < representant_id.size ()); + return representant_id[internal->vlit (lit)]; +} +LRAT_ID Closure::representative_id (int lit) const { + CADICAL_assert (internal->vlit (lit) < representant_id.size ()); + return representant_id[internal->vlit (lit)]; +} + +void Closure::mark_garbage (Gate *g) { + LOG (g, "marking as garbage"); + CADICAL_assert (!g->garbage); + g->garbage = true; + garbage.push_back (g); +} + +bool Closure::remove_gate (GatesTable::iterator git) { + CADICAL_assert (git != end (table)); + CADICAL_assert (!internal->unsat); + (*git)->indexed = false; + LOG ((*git), "removing from hash table"); + table.erase (git); + return true; +} + +bool Closure::remove_gate (Gate *g) { + if (!g->indexed) + return false; + CADICAL_assert (!internal->unsat); + CADICAL_assert (table.find (g) != end (table)); + table.erase (table.find (g)); + g->indexed = false; + LOG (g, "removing from hash table"); + return true; +} + +void Closure::index_gate (Gate *g) { + CADICAL_assert (!g->indexed); + CADICAL_assert (!internal->unsat); + CADICAL_assert (g->arity () > 1); + CADICAL_assert (g->hash == hash_lits (nonces, g->rhs)); + LOG (g, "adding to hash table"); + table.insert (g); + g->indexed = true; +} + +void Closure::produce_rewritten_clause_lrat_and_clean ( + std::vector<LitClausePair> &litIds, int except_lhs, bool remove_units) { + CADICAL_assert (internal->lrat_chain.empty ()); + for (auto &litId : litIds) { + CADICAL_assert (litId.clause); + litId.clause = produce_rewritten_clause_lrat (litId.clause, except_lhs, + remove_units); + litId.current_lit = find_eager_representative (litId.current_lit); + } + litIds.erase ( + std::remove_if (begin (litIds), end (litIds), + [] (const LitClausePair &p) { return !p.clause; }), + end (litIds)); +} + +void Closure::produce_rewritten_clause_lrat_and_clean ( + std::vector<LitClausePair> &litIds, int except_lhs, + size_t &old_position1, size_t &old_position2, bool remove_units) { + CADICAL_assert (internal->lrat_chain.empty ()); + CADICAL_assert (old_position1 != old_position2); + size_t j = 0; + for (size_t i = 0; i < litIds.size (); ++i) { + CADICAL_assert (j <= i); + litIds[j].clause = produce_rewritten_clause_lrat ( + litIds[i].clause, except_lhs, remove_units); + litIds[j].current_lit = + find_eager_representative (litIds[i].current_lit); + if (i == old_position1) { + if (litIds[j].clause) + old_position1 = j; + else + old_position1 = -1; + } + if (i == old_position2) { + if (litIds[j].clause) + old_position2 = j; + else + old_position2 = -1; + } + if (litIds[j].clause) + ++j; + } + litIds.resize (j); +} + +void Closure::compute_rewritten_clause_lrat_simple (Clause *c, int except) { + CADICAL_assert (internal->clause.empty ()); + CADICAL_assert (internal->lrat_chain.empty ()); + CADICAL_assert (clause.empty ()); + CADICAL_assert (lrat_chain.empty ()); + bool changed = false; + bool tautology = false; + for (auto lit : *c) { + LOG ("checking if %d is required", lit); + if (internal->marked2 (lit)) { + continue; + } + if (internal->marked2 (-lit)) { + tautology = true; + break; + } + if (lit == except || lit == -except) { + internal->mark2 (lit); + clause.push_back (lit); + continue; + } + if (internal->val (lit) < 0) { +#if 1 + LOG ("found unit %d, removing it", -lit); + LRAT_ID id = internal->unit_id (-lit); + lrat_chain.push_back (id); + changed = true; + continue; +#else + LOG ("found unit %d, but ignoring it", -lit); +#endif + } + if (internal->val (lit) > 0) { + LOG ("found positive unit, so clause is subsumed by unit"); + tautology = true; + } + const int other = find_eager_representative_and_compress (lit); + const bool marked = internal->marked2 (other); + const bool neg_marked = internal->marked2 (-other); + if (!marked) + internal->mark2 (other); + if (neg_marked) { + tautology = true; + LOG ("tautology due to %d -> %d", lit, other); + } else if (lit == other && marked) { + changed = true; + LOG ("%d -> %d already in", lit, other); + } else if (lit != other) { + if (!marked) + clause.push_back (other); + changed = true; + lrat_chain.push_back (eager_representative_id (lit)); + } else if (!marked) + clause.push_back (lit); + } + + for (auto lit : *c) { + internal->unmark (lit); + } + + for (auto lit : clause) { + internal->unmark (lit); + } + + lrat_chain.push_back (c->id); + if (tautology) { + LOG ("generated clause is a tautology"); + lrat_chain.clear (); + clause.clear (); + } else if (changed && clause.size () == 1) { + LOG (lrat_chain, "LRAT chain"); + } else { + LOG (c, "oops this should not happen"); + CADICAL_assert (false); + } +} + +Clause *Closure::new_tmp_clause (std::vector<int> &clause) { + CADICAL_assert (internal->lrat); + CADICAL_assert (!clause.empty ()); + CADICAL_assert (!lrat_chain.empty ()); + bool clear = false; + + LOG (clause, "learn new tmp clause"); + CADICAL_assert (clause.size () >= 2); + internal->external->check_learned_clause (); + + CADICAL_assert (internal->clause.size () <= (size_t) INT_MAX); + const int size = (int) clause.size (); + CADICAL_assert (size >= 2); + + size_t bytes = Clause::bytes (size); + Clause *c = (Clause *) new char[bytes]; + DeferDeleteArray<char> clause_delete ((char *) c); + + c->id = ++internal->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 = false; + c->transred = false; + c->subsume = false; + c->swept = false; + c->flushed = false; + c->vivified = false; + c->vivify = false; + c->used = 0; + + c->glue = size; + 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); + + clause_delete.release (); + LOG (c, "new pointer %p", (void *) c); + + if (clear) + clause.clear (); + + if (internal->proof) { + internal->proof->add_derived_clause (c, lrat_chain); + } + extra_clauses.push_back (c); + CADICAL_assert (internal->lrat_chain.empty ()); + return c; +} + +Clause *Closure::new_clause () { + CADICAL_assert (internal->clause.empty () || clause.empty ()); + bool clear = false; + if (internal->clause.empty ()) { + swap (internal->clause, clause); + clear = true; + } + + Clause *c = internal->new_clause (false); + + if (clear) + internal->clause.clear (); + + if (internal->proof) { + internal->proof->add_derived_clause (c, lrat_chain); + } + + return c; +} + +// TODO we here duplicate the arguments of push_id_and_rewriting_lrat but we +// probably do not need that. +void Closure::produce_rewritten_clause_lrat ( + std::vector<LitClausePair> &litIds, int except_lhs, bool remove_units) { + CADICAL_assert (internal->lrat_chain.empty ()); + for (auto &litId : litIds) { + if (!litId.clause) + continue; + litId.clause = produce_rewritten_clause_lrat (litId.clause, except_lhs, + remove_units); + litId.current_lit = find_eager_representative (litId.current_lit); + } +} + +Clause *Closure::produce_rewritten_clause_lrat (Clause *c, int except_lhs, + bool remove_units, bool fail_on_unit) { + CADICAL_assert (internal->clause.empty ()); + CADICAL_assert (internal->lrat_chain.empty ()); + auto tmp_lrat (std::move (lrat_chain)); + lrat_chain.clear (); + LOG (c, "rewriting clause for LRAT proof, except for rewriting %d", + except_lhs); + CADICAL_assert (internal->clause.empty ()); + CADICAL_assert (lrat_chain.empty ()); + bool changed = false; + bool tautology = false; + for (auto lit : *c) { + LOG ("checking if %d is required", lit); + if (internal->marked2 (lit)) { + continue; + } + if (internal->marked2 (-lit)) { + tautology = true; + break; + } + if (lit == except_lhs || lit == -except_lhs) { + internal->mark2 (lit); + clause.push_back (lit); + continue; + } + if (internal->val (lit) < 0) { + if (remove_units || lazy_propagated (lit)) { + LOG ("found unit %d, removing it", -lit); + LRAT_ID id = internal->unit_id (-lit); + lrat_chain.push_back (id); + changed = true; + continue; + } else + LOG ("found unit %d, but ignoring it", -lit); + } + if (internal->val (lit) > 0) { + LOG ("found positive unit %d, so clause is subsumed by unit", lit); + if (remove_units || lazy_propagated (lit)) + tautology = true; + } + const int other = find_eager_representative_and_compress (lit); + const bool marked = internal->marked2 (other); + const bool neg_marked = internal->marked2 (-other); + if (!marked) + internal->mark2 (other); + if (neg_marked) { + tautology = true; + LOG ("tautology due to %d -> %d", lit, other); + } else if (lit == other && marked) { + changed = true; + LOG ("%d -> %d already in", lit, other); + } else if (lit != other) { + if (!marked) + clause.push_back (other); + changed = true; + lrat_chain.push_back (eager_representative_id (lit)); + } else if (!marked) + clause.push_back (lit); + } + + for (auto lit : *c) { + internal->unmark (lit); + } + + for (auto lit : clause) { + internal->unmark (lit); + } + + lrat_chain.push_back (c->id); + Clause *d; + CADICAL_assert (internal->clause.empty ()); + if (tautology) { + LOG ("generated clause is a tautology"); + d = nullptr; + lrat_chain.clear (); + } else if (changed && clause.size () == 1) { + LOG (lrat_chain, "LRAT chain"); + if (fail_on_unit) { + d = nullptr; + CADICAL_assert (false && "rewriting produced a unit clause"); + } else { + d = c; + } + } else if (changed) { + LOG (lrat_chain, "LRAT Chain"); + d = new_tmp_clause (clause); + LOG (d, "rewritten clause to"); + } else { + LOG (c, "clause is unchanged, so giving up"); + lrat_chain.clear (); + d = c; + } + clause.clear (); + lrat_chain = std::move (tmp_lrat); + CADICAL_assert (internal->clause.empty ()); + return d; +} + +void Closure::push_id_on_chain (std::vector<LRAT_ID> &chain, + Rewrite rewrite, int lit) { + LOG ("adding reason %zd for rewriting %d marked", + lit == rewrite.src ? rewrite.id1 : rewrite.id2, lit); + chain.push_back (lit == rewrite.src ? rewrite.id1 : rewrite.id2); +} + +void Closure::push_id_and_rewriting_lrat_unit (Clause *c, Rewrite rewrite1, + std::vector<LRAT_ID> &chain, + bool insert_id_after, + Rewrite rewrite2, + int except_lhs, + int except_lhs2) { + LOG (c, + "computing normalized LRAT chain for clause to produce unit, " + "rewriting except for %d (%" PRIu64 ", %" PRIu64 ") and %d (%" PRIu64 + ", %" PRIu64 ") and skipping %d and %d", + rewrite1.src, rewrite1.id1, rewrite1.id2, rewrite2.src, rewrite2.id1, + rewrite2.id2, except_lhs, except_lhs2); + CADICAL_assert (c); + std::vector<LRAT_ID> units, rewriting; + for (auto other : *c) { + // unclear how to achieve this in the simplify context where other == + // g->lhs might be set CADICAL_assert (internal->val (other) <= 0 || other == + // except); + if (other == except_lhs || other == -except_lhs) { + // do nothing; + } else if (other == except_lhs2 || other == -except_lhs2) { + // do nothing; + } else if (internal->val (other) < 0) { + LOG ("found unit %d", -other); + LRAT_ID id = internal->unit_id (-other); + units.push_back (id); + } else if (other == rewrite1.src && rewrite1.id1) { + push_id_on_chain (rewriting, rewrite1, other); + } else if (other == -rewrite1.src && rewrite1.id2) { + push_id_on_chain (rewriting, rewrite1, other); + } else if (other == rewrite2.src && rewrite2.id1) { + push_id_on_chain (rewriting, rewrite2, other); + } else if (other == -rewrite2.src && rewrite2.id2) { + push_id_on_chain (rewriting, rewrite2, other); + } else if (other != find_eager_representative_and_compress (other)) { +#if defined(LOGGING) || !defined(CADICAL_NDEBUG) + const int rewritten_other = eager_representative (other); + CADICAL_assert (other != rewritten_other); + LOG ("reason for representative of %d %d is %" PRIu64 "", other, + rewritten_other, find_eager_representative_lrat (other)); +#endif + rewriting.push_back (find_eager_representative_lrat (other)); + } else { + LOG ("no rewriting needed for %d", other); + } + } + for (auto id : units) + chain.push_back (id); + + if (!insert_id_after) + chain.push_back (c->id); + for (auto id : rewriting) + chain.push_back (id); + + if (insert_id_after) + chain.push_back (c->id); +} + +// Note: it is important that the Rewrite takes over the normal rewriting, +// because we can force rewriting that way that have not been done eagerly +// yet. +void Closure::push_id_and_rewriting_lrat_full (Clause *c, Rewrite rewrite1, + std::vector<LRAT_ID> &chain, + bool insert_id_after, + Rewrite rewrite2, + int except_lhs, + int except_lhs2) { + LOG (c, + "computing normalized LRAT chain for clause, rewriting except for " + "%d (%" PRIu64 ", %" PRIu64 ") and %d (%" PRIu64 ", %" PRIu64 + ") and skipping %d and %d", + rewrite1.src, rewrite1.id1, rewrite1.id2, rewrite2.src, rewrite2.id1, + rewrite2.id2, except_lhs, except_lhs2); + CADICAL_assert (c); + if (!insert_id_after) + chain.push_back (c->id); + for (auto other : *c) { + // unclear how to achieve this in the simplify context where other == + // g->lhs might be set CADICAL_assert (internal->val (other) <= 0 || other == + // except); + if (other == except_lhs) { + // do nothing; + } else if (other == except_lhs2) { + // do nothing; + } else if (internal->val (other) < 0) { + LOG ("found unit %d", -other); + LRAT_ID id = internal->unit_id (-other); + CADICAL_assert (id); + chain.push_back (id); + } else if (other == rewrite1.src && rewrite1.id1) { + push_id_on_chain (chain, rewrite1, other); + } else if (other == -rewrite1.src && rewrite1.id2) { + push_id_on_chain (chain, rewrite1, other); + } else if (other == rewrite2.src && rewrite2.id1) { + push_id_on_chain (chain, rewrite2, other); + } else if (other == -rewrite2.src && rewrite2.id2) { + push_id_on_chain (chain, rewrite2, other); + } else { + CADICAL_assert (other == find_eager_representative (other)); + LOG ("no rewriting needed for %d", other); + } + } + if (insert_id_after) + chain.push_back (c->id); +} + +// Note: it is important that the Rewrite takes over the normal rewriting, +// because we can force rewriting that way that have not been done eagerly +// yet. +void Closure::push_id_on_chain (std::vector<LRAT_ID> &chain, Clause *c) { + CADICAL_assert (c); + chain.push_back (c->id); + LOG (lrat_chain, "chain"); +} + +void Closure::push_id_on_chain (std::vector<LRAT_ID> &chain, + const std::vector<LitClausePair> &reasons) { + for (auto litId : reasons) { + LOG (litId.clause, "found lrat in gate %d from %zd", litId.current_lit, + litId.clause->id); + push_id_on_chain (chain, litId.clause); + } + LOG (lrat_chain, "chain from %zd reasons", reasons.size ()); +} + +void Closure::learn_congruence_unit_when_lhs_set (Gate *g, int src, + LRAT_ID id1, LRAT_ID id2, + int dst) { + if (!internal->lrat) + return; + LOG ("calculating LRAT chain learn_congruence_unit_when_lhs_set"); + CADICAL_assert (!g->pos_lhs_ids.empty ()); + CADICAL_assert (internal->analyzed.empty ()); + CADICAL_assert (internal->val (g->lhs) < 0); + switch (g->tag) { + case Gate_Type::And_Gate: + LOG (lrat_chain, "lrat"); + LOG (lrat_chain, "lrat"); + for (auto litId : g->neg_lhs_ids) + push_id_and_rewriting_lrat_unit ( + litId.clause, Rewrite (src, dst, id1, id2), lrat_chain); + LOG (lrat_chain, "lrat"); + break; + default: + CADICAL_assert (false); + } +} + +// Something very important here: as we are producing a unit, we cannot +// simplify or rewrite the clauses as this will produce units. +void Closure::learn_congruence_unit_falsifies_lrat_chain ( + Gate *g, int src, int dst, int clashing, int falsified, int unit) { + if (!internal->lrat) + return; + CADICAL_assert (!g->pos_lhs_ids.empty ()); + CADICAL_assert (internal->analyzed.empty ()); + CADICAL_assert (lrat_chain.empty ()); + std::vector<LRAT_ID> proof_chain; + switch (g->tag) { + case Gate_Type::And_Gate: + if (clashing) { + LOG ("clashing %d where -lhs=%d", clashing, -g->lhs); + // Example: -2 = 1&3 and 3=2 + // The proof consists in taking the binary clause of the clashing + // literal + if (clashing == -g->lhs) { + for (auto litId : g->pos_lhs_ids) { + LOG (litId.clause, + "found lrat in gate %d from %zd (looking for %d)", + litId.current_lit, litId.clause->id, falsified); + if (litId.current_lit == clashing) { + push_id_and_rewriting_lrat_unit ( + litId.clause, Rewrite (), proof_chain, true, Rewrite (), + g->degenerated_and_neg || g->degenerated_and_pos ? 0 : -g->lhs); + } + } + } else { + // Example: 3 = (-1&2) and 2=1 + // The proof consists in taking the binary clause with the rewrites + // Example where the rewrite must be before: + // 2: 3v2 + // 9: -2v1 + // 6: 3v1 + // The chain cannot start by 9 + if (g->degenerated_and_neg || g->degenerated_and_pos) { + LOG ("%d %d %d", src, dst, g->lhs); + if (src == g->lhs || dst == g->lhs) { + LOG ("degenerated AND gate with dst=lhs"); + for (const auto &litId : g->pos_lhs_ids) { + LOG (litId.clause, "definition clause %d ->", + litId.current_lit); + if (litId.current_lit == clashing) { + push_id_and_rewriting_lrat_unit (litId.clause, Rewrite (), + proof_chain, true, + Rewrite (), 0); + LOG (proof_chain, "produced lrat chain so far"); + } + } + CADICAL_assert (!proof_chain.empty ()); + } else { + LOG ("degenerated AND gate with conflict without LHS"); + for (const auto &litId : g->pos_lhs_ids) { + LOG (litId.clause, "definition clause %d ->", + litId.current_lit); + push_id_and_rewriting_lrat_unit (litId.clause, Rewrite (), + proof_chain, false, + Rewrite (), -g->lhs); + LOG (proof_chain, "produced lrat chain so far"); + } + } + } else { + LOG ("normal AND gate"); + for (const auto &litId : g->pos_lhs_ids) { + push_id_and_rewriting_lrat_unit (litId.clause, Rewrite (), + proof_chain, false, Rewrite (), + g->degenerated_and_neg || g->degenerated_and_pos ? 0 : -g->lhs); + LOG (proof_chain, "produced lrat chain so far"); + } + } + } + LOG (proof_chain, "produced lrat chain"); + } else if (falsified) { + LOG ("falsifies %d", falsified); + // Example is 3=(1&2) with 2=false or 3=(1&4) with 4=2 and 2=false + // (can happen when the unit was derived in the middle of the + // rewriting) + for (auto litId : g->pos_lhs_ids) { + LOG (litId.clause, + "found lrat in gate %d from %zd (looking for %d)", + litId.current_lit, litId.clause->id, falsified); + if (litId.current_lit == falsified || + (litId.current_lit == src && dst == falsified)) { + push_id_and_rewriting_lrat_unit (litId.clause, Rewrite (), + proof_chain, true, Rewrite (), + -dst, -g->lhs); + } + } + } else { + CADICAL_assert (unit); + // Example is 1 = 2&3 where 2 and 3 are false + for (auto litId : g->neg_lhs_ids) { + push_id_and_rewriting_lrat_unit (litId.clause, Rewrite (), + proof_chain); + } + LOG (proof_chain, "produced lrat chain"); + break; + } + lrat_chain = std::move (proof_chain); + break; + default: + CADICAL_assert (false); + } + (void) unit; +} + +bool Closure::fully_propagate () { + if (internal->unsat) + return false; + LOG ("fully propagating"); + CADICAL_assert (internal->watching ()); + CADICAL_assert (full_watching); + bool no_conflict = internal->propagate (); + + if (no_conflict) + return true; + internal->learn_empty_clause (); + if (internal->lrat) + lrat_chain.clear (); + + return false; +} +bool Closure::learn_congruence_unit (int lit, bool delay_propagation, bool force_propagation) { + if (internal->unsat) + return false; + const signed char val_lit = internal->val (lit); + if (val_lit > 0) { + LOG ("already set lit %d", lit); + if (internal->lrat) + lrat_chain.clear (); + if (force_propagation) + return fully_propagate(); + return true; + } + LOG ("adding unit %s", LOGLIT (lit)); + ++internal->stats.congruence.units; + CADICAL_assert (!internal->lrat || !lrat_chain.empty ()); + if (val_lit < 0) { + if (internal->lrat) { + CADICAL_assert (internal->lrat_chain.empty ()); + LRAT_ID id = internal->unit_id (-lit); + internal->lrat_chain.push_back (id); + for (auto id : lrat_chain) + internal->lrat_chain.push_back (id); + lrat_chain.clear (); + } + internal->learn_empty_clause (); + return false; + } + + LOG (lrat_chain, "assigning due to LRAT chain"); + swap (lrat_chain, internal->lrat_chain); + internal->assign_unit (lit); + CADICAL_assert (lrat_chain.empty ()); + CADICAL_assert (internal->lrat_chain.empty ()); + if (delay_propagation) + return false; + else return fully_propagate (); +} + +// for merging the literals there are many cases +// TODO: LRAT does not work if the LHS is not in normal form and if the +// representative is also in the gate. +bool Closure::merge_literals_lrat ( + Gate *g, Gate *h, int lit, int other, + const std::vector<LRAT_ID> &extra_reasons_lit, + const std::vector<LRAT_ID> &extra_reasons_ulit) { + CADICAL_assert (!internal->unsat); + CADICAL_assert (g->lhs == lit); + CADICAL_assert (g == h || h->lhs == other); + (void) g, (void) h; + LOG ("merging literals %s and %s", LOGLIT(lit), LOGLIT(other)); + // TODO: this should not update_eager but still calculate the LRAT chain + // below! + const int repr_lit = find_representative_and_compress (lit, false); + const int repr_other = find_representative_and_compress (other, false); + find_representative_and_compress (-lit, false); + find_representative_and_compress (-other, false); + LOG ("merging literals %d [=%d] and %d [=%d]", lit, repr_lit, other, + repr_other); + + if (repr_lit == repr_other) { + LOG ("already merged %d and %d", lit, other); + if (internal->lrat) + lrat_chain.clear (); + return false; + } + + const int val_lit = internal->val (lit); + const int val_other = internal->val (other); + if (val_lit) { + if (val_lit == val_other) { + LOG ("not merging lits %d and %d assigned to same value", lit, other); + if (internal->lrat) + lrat_chain.clear (); + return false; + } + } + + // For LRAT we need to distinguish more cases for a more regular + // reconstruction. + // + // 1. if lit = -other, then we learn lit and -lit to derive false + // + // 2. otherwise, we learn the new clauses lit = -other (which are two real + // clauses). + // + // 2a. if repr_lit = -repr_other, we learn the units repr_lit and + // -repr_lit to derive false + // + // 2b. otherwise, we learn the equivalences repr_lit = -repr_other + // (which are two real clauses) + // + // Without LRAT this is easier, as we directly learn the conclusion + // (either false or the equivalence). The steps can also not be merged + // because repr_lit can appear in the gate and hence in the resolution + // chain. + int smaller_repr = repr_lit; + int larger_repr = repr_other; + int smaller = lit; + int larger = other; + const std::vector<LRAT_ID> *smaller_chain = &extra_reasons_ulit; + const std::vector<LRAT_ID> *larger_chain = &extra_reasons_lit; + + if (abs (smaller_repr) > abs (larger_repr)) { + swap (smaller_repr, larger_repr); + swap (smaller, larger); + swap (smaller_chain, larger_chain); + } + + CADICAL_assert (find_representative (smaller_repr) == smaller_repr); + CADICAL_assert (find_representative (larger_repr) == larger_repr); + if (lit == -other) { + CADICAL_assert (chain.empty ()); + LOG ("merging clashing %d and %d", lit, other); + if (internal->proof) { + if (internal->lrat) { + for (auto id : *smaller_chain) + lrat_chain.push_back (id); + } + unsimplified.push_back (smaller); + LRAT_ID id = simplify_and_add_to_proof_chain (unsimplified); + + if (internal->lrat) { + internal->lrat_chain.push_back (id); + for (auto id : *larger_chain) + internal->lrat_chain.push_back (id); + LOG (internal->lrat_chain, "lrat chain"); + } + } + internal->learn_empty_clause (); + delete_proof_chain (); + return false; + } + + LOG ("merging %d and %d first and then the equivalences of %d and %d " + "with LRAT", + lit, other, repr_lit, repr_other); + Clause *eq1_tmp = nullptr; + if (internal->lrat) { + lrat_chain = *smaller_chain; + eq1_tmp = add_tmp_binary_clause (-larger, smaller); + } + CADICAL_assert (!internal->lrat || eq1_tmp); + + Clause *eq2_tmp = nullptr; + if (internal->lrat) { + lrat_chain = *larger_chain; + LOG (lrat_chain, "lrat chain"); + + eq2_tmp = add_tmp_binary_clause (larger, -smaller); + // the order in the clause is important for the + // repr_lit == -repr_other to get the right chain + } + CADICAL_assert (!internal->lrat || eq2_tmp); + if (internal->lrat) + lrat_chain.clear (); + + if (repr_lit == -repr_other) { + // now derive empty clause + Rewrite rew1, rew2; + if (internal->lrat) { + // no need to calculate push_id_and_rewriting_lrat here because all + // the job is done by the arguments already + rew1 = Rewrite (lit == repr_lit ? 0 : lit, repr_lit, + lit == repr_lit ? 0 : representative_id (lit), + lit == repr_lit ? 0 : representative_id (-lit)); + rew2 = Rewrite (other == repr_other ? 0 : other, repr_other, + other == repr_other ? 0 : representative_id (other), + other == repr_other ? 0 : representative_id (-other)); + push_id_and_rewriting_lrat_unit (eq1_tmp, rew1, lrat_chain, true, + rew2); + swap (lrat_chain, internal->lrat_chain); + } + internal->assign_unit (-larger_repr); + if (internal->lrat) { + internal->lrat_chain.clear (); + + if (larger != larger_repr) + push_lrat_unit (-larger_repr); + push_id_and_rewriting_lrat_unit ( + eq2_tmp, rew1, lrat_chain, true, rew2, + larger != larger_repr ? larger_repr : 0); + LOG (lrat_chain, "lrat chain"); + swap (lrat_chain, internal->lrat_chain); + } + internal->learn_empty_clause (); + if (internal->lrat) + internal->lrat_chain.clear (); + return false; + } + + if (val_lit) { + if (val_lit == val_other) { + LOG ("not merging lits %d and %d assigned to same value", lit, other); + if (internal->lrat) + lrat_chain.clear (); + return false; + } + if (val_lit == -val_other) { + LOG ("merging lits %d and %d assigned to inconsistent value", lit, + other); + CADICAL_assert (lrat_chain.empty ()); + if (internal->lrat) { + Clause *c = val_lit ? eq2_tmp : eq1_tmp; + int pos = val_lit ? other : lit; + int neg = val_lit ? -lit : -other; + push_lrat_unit (pos); + push_lrat_unit (neg); + push_id_on_chain (lrat_chain, c); + } + internal->learn_empty_clause (); + if (internal->lrat) + lrat_chain.clear (); + return false; + } + + CADICAL_assert (!val_other); + LOG ("merging assigned %d and unassigned %d", lit, other); + CADICAL_assert (lrat_chain.empty ()); + const int unit = (val_lit < 0) ? -other : other; + if (internal->lrat) { + Clause *c; + if (lit == smaller) { + if (val_lit < 0) + c = eq1_tmp; + else + c = eq2_tmp; + } else { + if (val_lit < 0) + c = eq2_tmp; + else + c = eq1_tmp; + } + push_id_and_rewriting_lrat_unit (c, Rewrite (), lrat_chain, true, + Rewrite (), unit); + } + learn_congruence_unit (unit); + if (internal->lrat) + lrat_chain.clear (); + return false; + } + + if (!val_lit && val_other) { + LOG ("merging assigned %d and unassigned %d", lit, other); + CADICAL_assert (lrat_chain.empty ()); + const int unit = (val_other < 0) ? -lit : lit; + if (internal->lrat) { + Clause *c; + if (lit == smaller) { + if (val_lit < 0) + c = eq1_tmp; + else + c = eq2_tmp; + } else { + if (val_lit < 0) + c = eq2_tmp; + else + c = eq1_tmp; + } + push_id_and_rewriting_lrat_unit (c, Rewrite (), lrat_chain, true, + Rewrite (), lit, unit); + } + learn_congruence_unit (unit); + if (internal->lrat) + lrat_chain.clear (); + return false; + } + + Clause *eq1_repr, *eq2_repr; + if (smaller_repr != smaller || larger != larger_repr) { + if (internal->lrat) { + lrat_chain.clear (); + Rewrite rew1 = Rewrite ( + smaller_repr != smaller ? smaller : 0, + smaller_repr != smaller ? smaller_repr : 0, + smaller_repr != smaller ? representative_id (smaller) : 0, + smaller_repr != smaller ? representative_id (-smaller) : 0); + Rewrite rew2 = + Rewrite (larger_repr != larger ? larger : 0, + larger_repr != larger ? larger_repr : 0, + larger_repr != larger ? representative_id (larger) : 0, + larger_repr != larger ? representative_id (-larger) : 0); + push_id_and_rewriting_lrat_full (eq1_tmp, rew1, lrat_chain, true, + rew2); + } + eq1_repr = learn_binary_tmp_or_full_clause (-larger_repr, smaller_repr); + } else { + if (internal->lrat) + eq1_repr = maybe_promote_tmp_binary_clause (eq1_tmp); + else + eq1_repr = maybe_add_binary_clause (-larger_repr, smaller_repr); + } + + if (internal->lrat) { + lrat_chain.clear (); + } + + if (smaller_repr != smaller || larger != larger_repr) { + + if (internal->lrat) { + lrat_chain.clear (); + // eq2 = larger, -smaller + Rewrite rew1 = Rewrite ( + smaller_repr != smaller ? smaller : 0, + smaller_repr != smaller ? smaller_repr : 0, + smaller_repr != smaller ? representative_id (smaller) : 0, + smaller_repr != smaller ? representative_id (-smaller) : 0); + Rewrite rew2 = + Rewrite (larger_repr != larger ? larger : 0, + larger_repr != larger ? larger_repr : 0, + larger_repr != larger ? representative_id (larger) : 0, + larger_repr != larger ? representative_id (-larger) : 0); + push_id_and_rewriting_lrat_full (eq2_tmp, rew1, lrat_chain, true, + rew2); + } + + eq2_repr = learn_binary_tmp_or_full_clause (larger_repr, -smaller_repr); + + } else { + if (internal->lrat) + eq2_repr = maybe_promote_tmp_binary_clause (eq2_tmp); + else + eq2_repr = maybe_add_binary_clause (larger_repr, -smaller_repr); + } + lrat_chain.clear (); + + if (internal->lrat) { + representative_id (larger_repr) = eq1_repr->id; + CADICAL_assert (std::find (begin (*eq1_repr), end (*eq1_repr), -larger_repr) != + end (*eq1_repr)); + representative_id (-larger_repr) = eq2_repr->id; + CADICAL_assert (std::find (begin (*eq2_repr), end (*eq2_repr), larger_repr) != + end (*eq2_repr)); + } + LOG ("updating %d -> %d", larger_repr, smaller_repr); + representative (larger_repr) = smaller_repr; + representative (-larger_repr) = -smaller_repr; + schedule_literal (larger_repr); + ++internal->stats.congruence.congruent; + CADICAL_assert (lrat_chain.empty ()); + return true; +} + +// Variant when there are no gates +// TODO: this is exactly the same as the other function without the gates. +// Kill the arguments! +bool Closure::merge_literals_lrat ( + int lit, int other, const std::vector<LRAT_ID> &extra_reasons_lit, + const std::vector<LRAT_ID> &extra_reasons_ulit) { + CADICAL_assert (!internal->unsat); + LOG ("merging literals %s and %s", LOGLIT (lit), LOGLIT (other)); + // TODO: this should not update_eager but still calculate the LRAT chain + // below! + const int repr_lit = find_representative_and_compress (lit, false); + const int repr_other = find_representative_and_compress (other, false); + find_representative_and_compress (-lit, false); + find_representative_and_compress (-other, false); + LOG ("merging literals %s [=%d] and %s [=%d]", LOGLIT (lit), repr_lit, + LOGLIT (other), repr_other); + LOG (lrat_chain, "lrat chain beginning of merge"); + + if (repr_lit == repr_other) { + LOG ("already merged %s and %s", LOGLIT (lit), LOGLIT (other)); + if (internal->lrat) + lrat_chain.clear (); + return false; + } + + const int val_lit = internal->val (lit); + const int val_other = internal->val (other); + + // For LRAT we need to distinguish more cases for a more regular + // reconstruction. + // + // 1. if lit = -other, then we learn lit and -lit to derive false + // + // 2. otherwise, we learn the new clauses lit = -other (which are two real + // clauses). + // + // 2a. if repr_lit = -repr_other, we learn the units repr_lit and + // -repr_lit to derive false + // + // 2b. otherwise, we learn the equivalences repr_lit = -repr_other + // (which are two real clauses) + // + // Without LRAT this is easier, as we directly learn the conclusion + // (either false or the equivalence). The steps can also not be merged + // because repr_lit can appear in the gate and hence in the resolution + // chain. + int smaller_repr = repr_lit; + int larger_repr = repr_other; + int val_smaller = val_lit; + int val_larger = val_other; + int smaller = lit; + int larger = other; + const std::vector<LRAT_ID> *smaller_chain = &extra_reasons_ulit; + const std::vector<LRAT_ID> *larger_chain = &extra_reasons_lit; + + if (abs (smaller_repr) > abs (larger_repr)) { + swap (smaller_repr, larger_repr); + swap (smaller, larger); + swap (smaller_chain, larger_chain); + swap (val_smaller, val_larger); + } + + CADICAL_assert (find_representative (smaller_repr) == smaller_repr); + CADICAL_assert (find_representative (larger_repr) == larger_repr); + if (lit == -other) { + LOG ("merging clashing %d and %d", lit, other); + if (!val_smaller) { + if (internal->lrat) + internal->lrat_chain = *smaller_chain; + internal->assign_unit (smaller); + if (internal->lrat) + internal->lrat_chain.clear (); + + push_lrat_unit (smaller); + if (internal->lrat) { + CADICAL_assert (internal->lrat_chain.empty ()); + swap (internal->lrat_chain, lrat_chain); + for (auto id : *larger_chain) + internal->lrat_chain.push_back (id); + LOG (internal->lrat_chain, "lrat chain"); + } + internal->learn_empty_clause (); + return false; + } else { + if (internal->lrat) + internal->lrat_chain = + (val_smaller < 0 ? *smaller_chain : *larger_chain); + if (internal->lrat) + internal->lrat_chain.push_back ( + internal->unit_id (val_smaller > 0 ? smaller : -smaller)); + internal->learn_empty_clause (); + return false; + } + } + + if (val_lit && val_lit == val_other) { + LOG ("not merging lits %d and %d assigned to same value", lit, other); + return false; + } + + if (val_lit && val_lit == -val_other) { + if (internal->lrat) { + internal->lrat_chain.push_back ( + internal->unit_id (val_smaller < 0 ? -smaller : smaller)); + internal->lrat_chain.push_back ( + internal->unit_id (val_larger < 0 ? -larger : larger)); + for (auto id : (val_smaller < 0 ? *smaller_chain : *larger_chain)) { + internal->lrat_chain.push_back(id); + } + } + internal->learn_empty_clause (); + return false; + } + + LOG ("merging %d and %d first and then the equivalences of %d and %d " + "with LRAT", + lit, other, repr_lit, repr_other); + Clause *eq1_tmp = nullptr; + if (internal->lrat) { + lrat_chain = *smaller_chain; + eq1_tmp = add_tmp_binary_clause (-larger, smaller); + CADICAL_assert (eq1_tmp); + } + CADICAL_assert (!internal->lrat || eq1_tmp); + + Clause *eq2_tmp = nullptr; + if (internal->lrat) { + lrat_chain = *larger_chain; + LOG (lrat_chain, "lrat chain"); + // the order in the clause is important for the + // repr_lit == -repr_other to get the right chain + eq2_tmp = add_tmp_binary_clause (larger, -smaller); + CADICAL_assert (eq2_tmp); + } + + CADICAL_assert (!internal->lrat || eq2_tmp); + if (internal->lrat) + lrat_chain.clear (); + + if (repr_lit == -repr_other) { + // now derive empty clause + Rewrite rew1, rew2; + if (internal->lrat) { + // no need to calculate push_id_and_rewriting_lrat here because all + // the job is done by the arguments already + rew1 = Rewrite (lit == repr_lit ? 0 : lit, repr_lit, + lit == repr_lit ? 0 : representative_id (lit), + lit == repr_lit ? 0 : representative_id (-lit)); + rew2 = Rewrite (other == repr_other ? 0 : other, repr_other, + other == repr_other ? 0 : representative_id (other), + other == repr_other ? 0 : representative_id (-other)); + push_id_and_rewriting_lrat_unit (eq1_tmp, rew1, lrat_chain, true, + rew2); + swap (lrat_chain, internal->lrat_chain); + } + CADICAL_assert (val_larger == internal->val (larger_repr)); + if (!val_larger) { + // not assigned, first assign one + internal->assign_unit (-larger_repr); + if (internal->lrat) { + internal->lrat_chain.clear (); + + if (larger != larger_repr) + push_lrat_unit (-larger_repr); + // no need to calculate push_id_and_rewriting_lrat here because all + // the job is done by the arguments already + push_id_and_rewriting_lrat_unit ( + eq2_tmp, rew1, lrat_chain, true, rew2, + larger != larger_repr ? larger_repr : 0); + LOG (lrat_chain, "lrat chain"); + swap (lrat_chain, internal->lrat_chain); + } + } else { + // otherwise, no need to + if (internal->lrat) + lrat_chain.push_back (internal->unit_id (larger_repr)); + } + internal->learn_empty_clause (); + if (internal->lrat) + internal->lrat_chain.clear (); + return false; + } + + if (val_lit) { + if (val_lit == val_other) { + LOG ("not merging lits %d and %d assigned to same value", lit, other); + if (internal->lrat) + lrat_chain.clear (); + return false; + } + if (val_lit == -val_other) { + LOG ("merging lits %d and %d assigned to inconsistent value", lit, + other); + CADICAL_assert (lrat_chain.empty ()); + if (internal->lrat) { + Clause *c = val_lit ? eq2_tmp : eq1_tmp; + int pos = val_lit ? other : lit; + int neg = val_lit ? -lit : -other; + push_lrat_unit (pos); + push_lrat_unit (neg); + push_id_on_chain (lrat_chain, c); + } + internal->learn_empty_clause (); + if (internal->lrat) + lrat_chain.clear (); + return false; + } + + CADICAL_assert (!val_other); + LOG ("merging assigned %d and unassigned %d", lit, other); + CADICAL_assert (lrat_chain.empty ()); + const int unit = (val_lit < 0) ? -other : other; + if (internal->lrat) { + Clause *c; + if (lit == smaller) { + if (val_lit < 0) + c = eq1_tmp; + else + c = eq2_tmp; + } else { + if (val_lit < 0) + c = eq2_tmp; + else + c = eq1_tmp; + } + push_id_and_rewriting_lrat_unit (c, Rewrite (), lrat_chain, true, + Rewrite (), unit); + } + learn_congruence_unit (unit); + if (internal->lrat) + lrat_chain.clear (); + return false; + } + + if (!val_lit && val_other) { + LOG ("merging assigned %d and unassigned %d", lit, other); + CADICAL_assert (lrat_chain.empty ()); + const int unit = (val_other < 0) ? -lit : lit; + if (internal->lrat) { + Clause *c; + if (lit == smaller) { + CADICAL_assert (other == larger); + if (val_other > 0) + c = eq1_tmp; + else + c = eq2_tmp; + } else { + if (val_other > 0) + c = eq2_tmp; + else + c = eq1_tmp; + } + push_id_and_rewriting_lrat_unit (c, Rewrite (), lrat_chain, true, + Rewrite (), lit, unit); + } + learn_congruence_unit (unit); + if (internal->lrat) + lrat_chain.clear (); + return false; + } + + Clause *eq1_repr, *eq2_repr; + if (smaller_repr != smaller || larger != larger_repr) { + if (internal->lrat) { + lrat_chain.clear (); + Rewrite rew1 = Rewrite ( + smaller_repr != smaller ? smaller : 0, + smaller_repr != smaller ? smaller_repr : 0, + smaller_repr != smaller ? representative_id (smaller) : 0, + smaller_repr != smaller ? representative_id (-smaller) : 0); + Rewrite rew2 = + Rewrite (larger_repr != larger ? larger : 0, + larger_repr != larger ? larger_repr : 0, + larger_repr != larger ? representative_id (larger) : 0, + larger_repr != larger ? representative_id (-larger) : 0); + push_id_and_rewriting_lrat_full (eq1_tmp, rew1, lrat_chain, true, + rew2); + } + eq1_repr = learn_binary_tmp_or_full_clause (-larger_repr, smaller_repr); + } else { + if (internal->lrat) + eq1_repr = maybe_promote_tmp_binary_clause (eq1_tmp); + else + eq1_repr = maybe_add_binary_clause (-larger_repr, smaller_repr); + } + + if (internal->lrat) { + lrat_chain.clear (); + } + + if (smaller_repr != smaller || larger != larger_repr) { + + if (internal->lrat) { + lrat_chain.clear (); + // eq2 = larger, -smaller + Rewrite rew1 = Rewrite ( + smaller_repr != smaller ? smaller : 0, + smaller_repr != smaller ? smaller_repr : 0, + smaller_repr != smaller ? representative_id (smaller) : 0, + smaller_repr != smaller ? representative_id (-smaller) : 0); + Rewrite rew2 = + Rewrite (larger_repr != larger ? larger : 0, + larger_repr != larger ? larger_repr : 0, + larger_repr != larger ? representative_id (larger) : 0, + larger_repr != larger ? representative_id (-larger) : 0); + push_id_and_rewriting_lrat_full (eq2_tmp, rew1, lrat_chain, true, + rew2); + } + eq2_repr = learn_binary_tmp_or_full_clause (larger_repr, -smaller_repr); + } else { + if (internal->lrat) + eq2_repr = maybe_promote_tmp_binary_clause (eq2_tmp); + else + eq2_repr = maybe_add_binary_clause (larger_repr, -smaller_repr); + } + lrat_chain.clear (); + + if (internal->lrat) { + representative_id (larger_repr) = eq1_repr->id; + CADICAL_assert (std::find (begin (*eq1_repr), end (*eq1_repr), -larger_repr) != + end (*eq1_repr)); + representative_id (-larger_repr) = eq2_repr->id; + check_not_tmp_binary_clause (eq1_repr); + check_not_tmp_binary_clause (eq2_repr); + CADICAL_assert (std::find (begin (*eq2_repr), end (*eq2_repr), larger_repr) != + end (*eq2_repr)); + } + LOG ("updating %d -> %d", larger_repr, smaller_repr); + representative (larger_repr) = smaller_repr; + representative (-larger_repr) = -smaller_repr; + schedule_literal (larger_repr); + ++internal->stats.congruence.congruent; + CADICAL_assert (lrat_chain.empty ()); + return true; +} + +inline void Closure::promote_clause (Clause *c) { + if (internal->lrat) + check_not_tmp_binary_clause (c); + if (!c) + return; + if (!c->redundant) + return; + LOG (c, "turning redundant subsuming clause into irredundant clause"); + c->redundant = false; + if (internal->proof) + internal->proof->strengthen (c->id); + internal->stats.current.irredundant++; + internal->stats.added.irredundant++; + internal->stats.irrlits += c->size; + CADICAL_assert (internal->stats.current.redundant > 0); + internal->stats.current.redundant--; + CADICAL_assert (internal->stats.added.redundant > 0); + internal->stats.added.redundant--; + // ... and keep 'stats.added.total'. +} + +// This function is rather tricky for LRAT. If you have 2 = 1 and 3=4 you +// cannot add 2=3. You really to connect the representatives directly +// therefore you actually need to learn the clauses 2->3->4 and -2->1 and +// vice-versa +bool Closure::merge_literals_equivalence (int lit, int other, Clause *c1, + Clause *c2) { + CADICAL_assert (!internal->unsat); + LRAT_ID id1 = c1 ? c1->id : 0; + LRAT_ID id2 = c2 ? c2->id : 0; + if (internal->lrat) { + CADICAL_assert (c1); + CADICAL_assert (c2); + CADICAL_assert (c1->size == 2); + CADICAL_assert (c2->size == 2); + CADICAL_assert (c1->literals[0] == lit || c1->literals[1] == lit); + CADICAL_assert (c2->literals[0] == other || c2->literals[1] == other); + CADICAL_assert (c1->literals[0] == -other || c1->literals[1] == -other); + CADICAL_assert (c2->literals[0] == -lit || c2->literals[1] == -lit); + check_not_tmp_binary_clause (c1); + check_not_tmp_binary_clause (c2); + } + int repr_lit = find_representative (lit); + int repr_other = find_representative (other); + find_representative_and_compress_both (lit); + find_representative_and_compress_both (other); + LOG ("merging literals %d [=%d] and %d [=%d] lrat", lit, repr_lit, other, + repr_other); + + if (repr_lit == repr_other) { + LOG ("already merged %d and %d", lit, other); + return false; + } + const int val_lit = internal->val (lit); + const int val_other = internal->val (other); + + if (val_lit) { + if (val_lit == val_other) { + LOG ("not merging lits %d and %d assigned to same value", lit, other); + return false; + } + if (val_lit == -val_other) { + if (internal->lrat) + lrat_chain.push_back (internal->unit_id (lit)), + lrat_chain.push_back (internal->unit_id (other)); + LOG ("merging lits %d and %d assigned to inconsistent value", lit, + other); + internal->learn_empty_clause (); + return false; + } + + CADICAL_assert (!val_other); + LOG ("merging assigned %d and unassigned %d", lit, other); + const int unit = (val_lit < 0) ? -other : other; + if (internal->lrat) + lrat_chain.push_back (internal->unit_id (unit)); + if (val_lit < 0) + lrat_chain.push_back (c2->id); + else + lrat_chain.push_back (c1->id); + learn_congruence_unit (unit); + return false; + } + + if (!val_lit && val_other) { + LOG ("merging assigned %d and unassigned %d", other, lit); + const int unit = (val_other < 0) ? -lit : lit; + if (internal->lrat) + lrat_chain.push_back ( + internal->unit_id (val_other < 0 ? -other : other)); + if (val_lit < 0) + lrat_chain.push_back (c1->id); + else + lrat_chain.push_back (c2->id); + learn_congruence_unit (unit); + return false; + } + + int smaller_repr = repr_lit; + int larger_repr = repr_other; + int smaller = lit; + int larger = other; + + if (abs (smaller_repr) > abs (larger_repr)) { + swap (smaller_repr, larger_repr); + swap (smaller, larger); + } + + CADICAL_assert (find_representative (smaller_repr) == smaller_repr); + CADICAL_assert (find_representative (larger_repr) == larger_repr); + + if (repr_lit == -repr_other) { + LOG ("merging clashing %d [=%d] and %d[=%d], smaller: %d", lit, + repr_lit, other, repr_other, smaller); + if (internal->lrat) { + Rewrite rew1 = + Rewrite (lit, lit == repr_lit ? 0 : repr_lit, + lit == repr_lit ? 0 : find_representative_lrat (lit), + lit == repr_lit ? 0 : find_representative_lrat (-lit)); + Rewrite rew2 = Rewrite ( + other, other == repr_other ? 0 : repr_other, + other == repr_other ? 0 : find_representative_lrat (other), + other == repr_other ? 0 : find_representative_lrat (-other)); + push_id_and_rewriting_lrat_unit (c1, rew1, lrat_chain, true, rew2); + LOG (lrat_chain, "lrat chain"); + swap (internal->lrat_chain, lrat_chain); + } + internal->assign_unit (repr_lit); + if (internal->lrat) { + lrat_chain.clear (); + LRAT_ID id = internal->unit_id (repr_lit); + CADICAL_assert (id); + lrat_chain.push_back (id); + if (lit != repr_lit) { + const LRAT_ID repr_id2 = find_representative_lrat (-lit); + lrat_chain.push_back (repr_id2); + } + lrat_chain.push_back (id2); + if (other != repr_other) { + const LRAT_ID repr_larger_id2 = find_representative_lrat (other); + lrat_chain.push_back (repr_larger_id2); + } + LOG (lrat_chain, "lrat chain"); + swap (internal->lrat_chain, lrat_chain); + } + internal->learn_empty_clause (); + return false; + } + + LOG ("merging %d [=%d] and %d [=%d]", lit, repr_lit, other, repr_other); + promote_clause (c1), promote_clause (c2); + bool learn_clause = (lit != repr_lit) || (other != repr_other); + if (learn_clause) { + if (internal->lrat) { + if (lit != repr_lit) { + LOG ("adding chain for lit %d -> %d", lit, repr_lit); + lrat_chain.push_back (find_representative_lrat (lit)); + } + if (other != repr_other) { + LOG ("adding chain for lit %d -> %d", -other, -repr_other); + lrat_chain.push_back (find_representative_lrat (-other)); + } + lrat_chain.push_back (id1); + } + Clause *eq1 = learn_binary_tmp_or_full_clause (repr_lit, -repr_other); + + if (internal->lrat) { + lrat_chain.clear (); + if (lit != repr_lit) + lrat_chain.push_back (find_representative_lrat (-lit)); + if (other != repr_other) + lrat_chain.push_back (find_representative_lrat (other)); + lrat_chain.push_back (id2); + } + Clause *eq2 = learn_binary_tmp_or_full_clause (-repr_lit, repr_other); + if (internal->lrat) { + lrat_chain.clear (); + if (smaller_repr == repr_lit) { + CADICAL_assert (larger_repr == repr_other); + representative_id (-larger_repr) = eq2->id; + CADICAL_assert (std::find (eq2->begin (), eq2->end (), larger_repr) != + eq2->end ()); + representative_id (larger_repr) = eq1->id; + CADICAL_assert (std::find (eq1->begin (), eq1->end (), -larger_repr) != + eq1->end ()); + } else { + CADICAL_assert (larger_repr == repr_lit); + representative_id (-larger_repr) = eq1->id; + CADICAL_assert (std::find (eq1->begin (), eq1->end (), larger_repr) != + eq1->end ()); + representative_id (larger_repr) = eq2->id; + CADICAL_assert (std::find (eq2->begin (), eq2->end (), -larger_repr) != + eq2->end ()); + } + } + + } else if (internal->lrat) { + LOG ("not learning new clause, using already existing one"); + if (smaller_repr == repr_lit) { + LOG ("setting ids of %d: %" PRIu64 "; %d: %" PRIu64 " (case 1)", + larger, id1, -larger, id2); + representative_id (-larger_repr) = id2; + representative_id (larger_repr) = id1; + } else { + LOG ("setting ids of %d: %" PRIu64 "; %d: %" PRIu64 " (case 2)", + larger, id2, -larger, id1); + representative_id (-larger_repr) = id1; + representative_id (larger_repr) = id2; + } + } + + LOG ("updating %d -> %d", larger_repr, smaller_repr); + representative (larger_repr) = smaller_repr; + representative (-larger_repr) = -smaller_repr; + schedule_literal (larger_repr); + ++internal->stats.congruence.congruent; + return true; +} + +/*------------------------------------------------------------------------*/ +GOccs &Closure::goccs (int lit) { return gtab[internal->vlit (lit)]; } + +void Closure::connect_goccs (Gate *g, int lit) { + LOG (g, "connect %d to", lit); + // incorrect for ITE + // CADICAL_assert (std::find(begin (goccs (lit)), end (goccs (lit)), g) == + // std::end (goccs (lit))); + goccs (lit).push_back (g); +} + +uint64_t &Closure::largecount (int lit) { + CADICAL_assert (internal->vlit (lit) < glargecounts.size ()); + return glargecounts[internal->vlit (lit)]; +} + +/*------------------------------------------------------------------------*/ +// Initialization + +void Closure::init_closure () { + representant.resize (2 * internal->max_var + 3); + eager_representant.resize (2 * internal->max_var + 3); + if (internal->lrat) { + eager_representant_id.resize (2 * internal->max_var + 3); + representant_id.resize (2 * internal->max_var + 3); + lazy_propagated_idx.resize (internal->max_var + 1, false); + for (auto lit : internal->trail) + lazy_propagated (lit) = true; + } + marks.resize (2 * internal->max_var + 3); + mu1_ids.resize (2 * internal->max_var + 3); + if (internal->lrat) { + mu2_ids.resize (2 * internal->max_var + 3); + mu4_ids.resize (2 * internal->max_var + 3); + } +#ifndef CADICAL_NDEBUG + for (auto &it : mu1_ids) + it.current_lit = 0, it.clause = nullptr; + for (auto &it : mu2_ids) + it.current_lit = 0, it.clause = nullptr; + for (auto &it : mu4_ids) + it.current_lit = 0, it.clause = nullptr; +#endif + scheduled.resize (internal->max_var + 1); + gtab.resize (2 * internal->max_var + 3); + for (auto v : internal->vars) { + representative (v) = v; + representative (-v) = -v; + eager_representative (v) = v; + eager_representative (-v) = -v; + } + units = internal->propagated; + Random rand (internal->stats.congruence.rounds); + for (auto &n : nonces) { + n = 1 | rand.next (); + } +#ifdef LOGGING + fresh_id = internal->clause_id; +#endif + internal->init_noccs (); + internal->init_occs (); +} + +void Closure::init_and_gate_extraction () { + LOG ("[gate-extraction]"); + for (Clause *c : internal->clauses) { + if (c->garbage) + continue; + if (c->redundant && c->size != 2) + continue; + if (c->size > 2) + continue; + CADICAL_assert (c->size == 2); + const int lit = c->literals[0]; + const int other = c->literals[1]; + internal->noccs (lit)++; + internal->noccs (other)++; + internal->watch_clause (c); + } +} + +/*------------------------------------------------------------------------*/ +void Closure::check_and_gate_implied (Gate *g) { + CADICAL_assert (internal->clause.empty ()); + CADICAL_assert (g->tag == Gate_Type::And_Gate); + if (internal->lrat) + return; + LOG (g, "checking implied"); + const int lhs = g->lhs; + const int not_lhs = -lhs; + for (auto other : g->rhs) + check_binary_implied (not_lhs, other); + internal->clause = g->rhs; + check_implied (); + internal->clause.clear (); +} + +void Closure::delete_proof_chain () { + if (!internal->proof) { + CADICAL_assert (chain.empty ()); + return; + } + if (chain.empty ()) + return; + LOG ("starting deletion of proof chain"); + auto &clause = internal->clause; + CADICAL_assert (clause.empty ()); + uint32_t id1 = UINT32_MAX, id2 = UINT32_MAX; + LRAT_ID id = 0; + + LOG (chain, "chain:"); + for (auto lit : chain) { + LOG ("reading %d from chain", lit); + if (id1 == UINT32_MAX) { + id1 = lit; + id = (LRAT_ID) id1; + continue; + } + if (id2 == UINT32_MAX) { + id2 = lit; + id = ((LRAT_ID) id1 << 32) + id2; + continue; + } + if (lit) { // parsing the id first + LOG ("found %d as literal in chain", lit); + clause.push_back (lit); + } else { + CADICAL_assert (id); + internal->proof->delete_clause (id, false, clause); + clause.clear (); + id = 0, id1 = UINT32_MAX, id2 = UINT32_MAX; + } + } + CADICAL_assert (clause.empty ()); + chain.clear (); + LOG ("finished deletion of proof chain"); +} + +/*------------------------------------------------------------------------*/ +// Simplification +bool Closure::skip_and_gate (Gate *g) { + CADICAL_assert (g->tag == Gate_Type::And_Gate); + if (g->garbage) + return true; + const int lhs = g->lhs; + if (internal->val (lhs) > 0) { + mark_garbage (g); + return true; + } + + CADICAL_assert (g->arity () > 1); + return false; +} + +bool Closure::skip_xor_gate (Gate *g) { + CADICAL_assert (g->tag == Gate_Type::XOr_Gate); + if (g->garbage) + return true; + CADICAL_assert (g->arity () > 1); + return false; +} + +void Closure::shrink_and_gate (Gate *g, int falsifies, int clashing) { + if (falsifies) { + g->rhs.resize (1); + g->rhs[0] = falsifies; + g->hash = hash_lits (nonces, g->rhs); + } else if (clashing) { + LOG (g, "gate before clashing on %d", clashing); + g->rhs.resize (2); + g->rhs[0] = clashing; + g->rhs[1] = -clashing; + g->hash = hash_lits (nonces, g->rhs); + LOG (g, "gate after clashing on %d", clashing); + } + g->shrunken = true; +} + +void Closure::update_and_gate_unit_build_lrat_chain ( + Gate *g, int src, LRAT_ID id1, LRAT_ID id2, int dst, + std::vector<LRAT_ID> &extra_reasons_lit, + std::vector<LRAT_ID> &extra_reasons_ulit) { + LOG ("generate chain for gate boiling down to unit"); + if (g->neg_lhs_ids.size () != 1) { + + if (g->degenerated_and_neg || g->degenerated_and_pos) { + // can happen for 4 = AND 3 4 (degenerated with only the clause -4 3) + // with a rewriting 4 -> 1 (unchanged clause) + // and later 1 -> 3 (unchanged clause) + // but you do not know anymore from the gate that it is degenerated + CADICAL_assert (g->pos_lhs_ids.size () == 1); + push_id_and_rewriting_lrat_unit (g->pos_lhs_ids[0].clause, Rewrite (), + extra_reasons_ulit, true, Rewrite ()); + return; + } + CADICAL_assert (g->lhs == g->rhs[0] || (g->lhs == src && g->rhs[0] == dst)); + CADICAL_assert (g->pos_lhs_ids.size () <= 1); // either degenerated or empty A = A + return; + } + CADICAL_assert (g->neg_lhs_ids.size () == 1); + CADICAL_assert (!g->pos_lhs_ids.empty ()); + + const int repr_lit = find_representative (g->lhs); + const int repr_other = find_representative (g->rhs[0]); + if (repr_lit == repr_other) { + LOG ("skipping already merged"); + return; + } + + push_id_and_rewriting_lrat_unit (g->neg_lhs_ids[0].clause, Rewrite (), + extra_reasons_ulit, true, Rewrite (), + g->lhs, -dst); + LOG (extra_reasons_ulit, "lrat chain for negative side"); + + lrat_chain.clear (); + + CADICAL_assert (!g->pos_lhs_ids.empty ()); + for (auto litId : g->pos_lhs_ids) + push_id_and_rewriting_lrat_unit ( + litId.clause, Rewrite (src, dst, id1, id2), extra_reasons_lit, true, + Rewrite (), -g->lhs, dst); + LOG (extra_reasons_lit, "lrat chain for positive side"); +} + +void Closure::update_and_gate_build_lrat_chain ( + Gate *g, Gate *h, std::vector<LRAT_ID> &extra_reasons_lit, + std::vector<LRAT_ID> &extra_reasons_ulit, bool remove_units) { + CADICAL_assert (g != h); + LOG (g, "merging"); + LOG (h, "with"); + // If the LHS are identical, do not even attempt to build the LRAT chain + if (find_representative (g->lhs) == find_representative (h->lhs)) + return; + // set to same value, don't do anything + if (internal->val (g->lhs) && internal->val (g->lhs) == internal->val (h->lhs)) + return; + const bool g_tautology = gate_contains (g, g->lhs); + const bool h_tautology = gate_contains (h, h->lhs); + if (g_tautology && h_tautology) { + LOG ("both gates are a tautology"); + // special case: actually we have an equivalence due to binary clauses + // and all gate clauses (except one binary) are actually tautologies + for (auto &litId : g->pos_lhs_ids) { + if (litId.current_lit == h->lhs) { + CADICAL_assert (extra_reasons_lit.empty ()); + LOG (litId.clause, "binary clause to push into the reason"); + litId.clause = produce_rewritten_clause_lrat (litId.clause, g->lhs, + remove_units); + CADICAL_assert (litId.clause); + extra_reasons_lit.push_back (litId.clause->id); + } + } + CADICAL_assert (!extra_reasons_lit.empty ()); + CADICAL_assert (extra_reasons_lit.size () == 1); + + for (auto &litId : h->pos_lhs_ids) { + if (litId.current_lit == g->lhs) { + CADICAL_assert (extra_reasons_ulit.empty ()); + CADICAL_assert (litId.clause); + LOG (litId.clause, "binary clause to push into the reason"); + litId.clause = produce_rewritten_clause_lrat (litId.clause, h->lhs, + remove_units); + CADICAL_assert (litId.clause); + extra_reasons_ulit.push_back (litId.clause->id); + } + } + CADICAL_assert (!extra_reasons_ulit.empty ()); + CADICAL_assert (extra_reasons_ulit.size () == 1); + return; + } + if (g_tautology || h_tautology) { + // special case: actually we have an equivalence due to binary clauses + // and some of the clauses from the gate are actually tautologies + CADICAL_assert (g_tautology != h_tautology); + Gate *tauto = (g_tautology ? g : h); + Gate *other = (g_tautology ? h : g); + LOG (tauto, "one gate is a tautology"); + CADICAL_assert (tauto != other); + CADICAL_assert (tauto == h || tauto == g); + + auto &extra_reasons_tauto = + (!g_tautology ? extra_reasons_lit : extra_reasons_ulit); + auto &extra_reasons_other = + (!g_tautology ? extra_reasons_ulit : extra_reasons_lit); + + // one direction: the binary clause already exists + for (auto &litId : other->pos_lhs_ids) { + if (litId.current_lit == tauto->lhs) { + CADICAL_assert (litId.clause); + CADICAL_assert (extra_reasons_tauto.empty ()); + LOG (litId.clause, "binary clause to push into the reason"); + litId.clause = produce_rewritten_clause_lrat ( + litId.clause, other->lhs, remove_units); + CADICAL_assert (litId.clause); + extra_reasons_tauto.push_back (litId.clause->id); + } + } + CADICAL_assert (!extra_reasons_tauto.empty ()); + + // other direction, we have to resolve + LOG (tauto, "now the other direction"); + for (auto &litId : tauto->pos_lhs_ids) { + CADICAL_assert (litId.clause); + LOG (litId.clause, + "binary clause from %d to push into the reason [avoiding %d]", + litId.current_lit, tauto->lhs); + if (litId.current_lit != tauto->lhs) { + LOG (litId.clause, "binary clause to push into the reason"); + CADICAL_assert (litId.clause); + litId.clause = produce_rewritten_clause_lrat ( + litId.clause, tauto->lhs, remove_units); + if (!litId.clause) // degenerated but does not know yet + continue; + CADICAL_assert (litId.clause); + extra_reasons_other.push_back (litId.clause->id); + } + tauto->pos_lhs_ids.erase (std::remove_if (begin (tauto->pos_lhs_ids), + end (tauto->pos_lhs_ids), + [] (const LitClausePair &p) { + return !p.clause; + }), + end (tauto->pos_lhs_ids)); + } + CADICAL_assert (!extra_reasons_other.empty ()); + produce_rewritten_clause_lrat_and_clean (other->neg_lhs_ids, other->lhs, + remove_units); + push_id_on_chain (extra_reasons_other, other->neg_lhs_ids); + CADICAL_assert (!extra_reasons_tauto.empty ()); + CADICAL_assert (!extra_reasons_other.empty ()); + return; + } + // default: resolve all clauses + // first rewrite + // TODO: do we really need dest as second exclusion? + produce_rewritten_clause_lrat_and_clean (h->pos_lhs_ids, -h->lhs, + remove_units); + CADICAL_assert (internal->clause.empty ()); + produce_rewritten_clause_lrat_and_clean (h->neg_lhs_ids, -h->lhs, + remove_units); + CADICAL_assert (internal->clause.empty ()); + produce_rewritten_clause_lrat_and_clean (g->pos_lhs_ids, -g->lhs, + remove_units); + CADICAL_assert (internal->clause.empty ()); + produce_rewritten_clause_lrat_and_clean (g->neg_lhs_ids, -g->lhs, + remove_units); + CADICAL_assert (internal->clause.empty ()); + + push_id_on_chain (extra_reasons_ulit, h->pos_lhs_ids); + push_id_on_chain (extra_reasons_ulit, g->neg_lhs_ids[0].clause); + lrat_chain.clear (); + LOG (extra_reasons_ulit, "lrat chain for negative side"); + + push_id_on_chain (extra_reasons_lit, g->pos_lhs_ids); + push_id_on_chain (extra_reasons_lit, h->neg_lhs_ids); + + lrat_chain.clear (); + LOG (extra_reasons_lit, "lrat chain for positive side"); + CADICAL_assert (!extra_reasons_lit.empty ()); + CADICAL_assert (!extra_reasons_ulit.empty ()); +} + +void Closure::update_and_gate (Gate *g, GatesTable::iterator it, int src, + int dst, LRAT_ID id1, LRAT_ID id2, + int falsifies, int clashing) { + LOG (g, "update and gate of arity %ld", g->arity ()); + CADICAL_assert (lrat_chain.empty ()); + bool garbage = true; + if (falsifies || clashing) { + if (internal->lrat) + learn_congruence_unit_falsifies_lrat_chain (g, src, dst, clashing, + falsifies, 0); + int unit = -g->lhs; + if (unit == src) + unit = dst; + else if (unit == -src) + unit = -dst; + learn_congruence_unit (unit); + if (internal->lrat) + lrat_chain.clear (); + } else if (g->arity () == 1) { + const signed char v = internal->val (g->lhs); + if (v > 0) { + if (internal->lrat) + learn_congruence_unit_falsifies_lrat_chain (g, src, dst, 0, 0, + g->lhs); + learn_congruence_unit (g->rhs[0]); + if (internal->lrat) + lrat_chain.clear (); + } else if (v < 0) { + if (internal->lrat) + learn_congruence_unit_when_lhs_set (g, src, id1, id2, dst); + learn_congruence_unit (-g->rhs[0]); + if (internal->lrat) + lrat_chain.clear (); + } else { + std::vector<LRAT_ID> extra_reasons_lit; + std::vector<LRAT_ID> extra_reasons_ulit; + if (internal->lrat) + update_and_gate_unit_build_lrat_chain (g, src, id1, id2, g->rhs[0], + extra_reasons_lit, + extra_reasons_ulit); + if (merge_literals_lrat (g, g, g->lhs, g->rhs[0], extra_reasons_lit, + extra_reasons_ulit)) { + ++internal->stats.congruence.unaries; + ++internal->stats.congruence.unary_and; + } + } + } else { + CADICAL_assert (g->arity () > 1); + sort_literals_by_var (g->rhs); + Gate *h = find_and_lits (g->rhs, g); + CADICAL_assert (g != h); + if (h) { + CADICAL_assert (garbage); + std::vector<LRAT_ID> extra_reasons_lit2; + std::vector<LRAT_ID> extra_reasons_ulit2; + if (internal->lrat) + update_and_gate_build_lrat_chain (g, h, extra_reasons_lit2, + extra_reasons_ulit2); + if (merge_literals_lrat (g, h, g->lhs, h->lhs, extra_reasons_lit2, + extra_reasons_ulit2)) + ++internal->stats.congruence.ands; + } else { + if (g->indexed) { + LOG (g, "removing from table"); + (void) table.erase (it); + } + g->hash = hash_lits (nonces, g->rhs); + LOG (g, "inserting gate into table"); + CADICAL_assert (table.count (g) == 0); + table.insert (g); + g->indexed = true; + garbage = false; + if (internal->lrat) + lrat_chain.clear (); + } + } + + if (garbage && !internal->unsat) + mark_garbage (g); +} + +void Closure::update_xor_gate (Gate *g, GatesTable::iterator git) { + CADICAL_assert (g->tag == Gate_Type::XOr_Gate); + CADICAL_assert (!internal->unsat && chain.empty ()); + LOG (g, "updating"); + bool garbage = true; + // TODO Florian LRAT for learn_congruence_unit + CADICAL_assert (g->arity () == 0 || internal->clause.empty ()); + CADICAL_assert (clause.empty ()); + if (g->arity () == 0) { + if (internal->clause.size ()) { + CADICAL_assert (!internal->proof || (internal->clause.size () == 1 && + internal->clause.back () == -g->lhs)); + CADICAL_assert (!internal->lrat || lrat_chain.size ()); + internal->clause.clear (); + + } else if (internal->lrat) { + simplify_unit_xor_lrat_clauses (g->pos_lhs_ids, g->lhs); + CADICAL_assert (clause.size () && clause.back () == -g->lhs); + clause.clear (); + } + + if (internal->lrat && internal->val (-g->lhs) < 0) { + internal->lrat_chain.push_back (internal->unit_id (g->lhs)); + for (auto id : lrat_chain) + internal->lrat_chain.push_back (id); + lrat_chain.clear (); + internal->learn_empty_clause(); + } else + learn_congruence_unit (-g->lhs); + + CADICAL_assert (clause.empty ()); + } else if (g->arity () == 1) { + std::vector<LRAT_ID> reasons_implication, reasons_back; + if (internal->lrat) { + vector<LitClausePair> first; + simplify_and_sort_xor_lrat_clauses (g->pos_lhs_ids, first, g->lhs); + g->pos_lhs_ids = first; + CADICAL_assert (g->pos_lhs_ids.size () == 2); + reasons_implication.push_back (g->pos_lhs_ids[0].clause->id); + reasons_back.push_back (g->pos_lhs_ids[1].clause->id); + } + const signed char v = internal->val (g->lhs); + if (v > 0) { + if (internal->lrat) { + push_lrat_unit (g->lhs); + lrat_chain.push_back (g->pos_lhs_ids[0].clause->id); + } + learn_congruence_unit (g->rhs[0]); + } else if (v < 0) { + if (internal->lrat) { + push_lrat_unit (-g->lhs); + lrat_chain.push_back (g->pos_lhs_ids[1].clause->id); + } + learn_congruence_unit (-g->rhs[0]); + } else if (merge_literals_lrat ( + g->lhs, g->rhs[0], reasons_implication, + reasons_back)) { // TODO Florian merge with LRAT + ++internal->stats.congruence.unaries; + ++internal->stats.congruence.unary_and; + } + CADICAL_assert (clause.empty ()); + } else { + Gate *h = find_xor_gate (g); + if (h) { + CADICAL_assert (garbage); + std::vector<LRAT_ID> reasons_implication, reasons_back; + add_xor_matching_proof_chain (g, g->lhs, h->pos_lhs_ids, h->lhs, + reasons_implication, reasons_back); + if (merge_literals_lrat (g->lhs, h->lhs, reasons_implication, + reasons_back)) + ++internal->stats.congruence.xors; + delete_proof_chain (); + } else { + if (g->indexed) { + remove_gate (git); + } + g->hash = hash_lits (nonces, g->rhs); + LOG (g, "reinserting in table"); + table.insert (g); + g->indexed = true; + CADICAL_assert (table.find (g) != end (table)); + garbage = false; + } + CADICAL_assert (clause.empty ()); + } + if (garbage && !internal->unsat) + mark_garbage (g); + CADICAL_assert (clause.empty ()); +} + +void Closure::simplify_and_gate (Gate *g) { + if (skip_and_gate (g)) + return; + GatesTable::iterator git = (g->indexed ? table.find (g) : end (table)); + CADICAL_assert (!g->indexed || git != end (table)); + LOG (g, "simplifying"); + int falsifies = 0; + std::vector<int>::iterator it = begin (g->rhs); + bool ulhs_in_rhs = false; + for (auto lit : g->rhs) { + const signed char v = internal->val (lit); + if (v > 0) { + continue; + } + if (v < 0) { + falsifies = lit; + continue; + } + if (lit == -g->lhs) + ulhs_in_rhs = true; + *it++ = lit; + if (lit == g->lhs) + g->degenerated_and_pos = true; + if (lit == -g->lhs) + g->degenerated_and_neg = true; + } + + if (internal->lrat) { // updating reasons + size_t i = 0, size = g->pos_lhs_ids.size (); + for (size_t j = 0; j < size; ++j) { + LOG ("looking at %d [%ld %ld]", g->pos_lhs_ids[j].current_lit, i, j); + g->pos_lhs_ids[i] = g->pos_lhs_ids[j]; + if (!g->degenerated_and_pos && + internal->val (g->pos_lhs_ids[i].current_lit) && + g->pos_lhs_ids[i].current_lit != falsifies) + continue; + LOG ("keeping %d [%ld %ld]", g->pos_lhs_ids[i].current_lit, i, j); + ++i; + } + LOG ("resizing to %ld", i); + g->pos_lhs_ids.resize (i); + } + + CADICAL_assert (it <= end (g->rhs)); // can be equal when ITE are converted to + // ands leading to + CADICAL_assert (it >= begin (g->rhs)); + LOG (g, "shrunken"); + + g->shrunken = true; + g->rhs.resize (it - std::begin (g->rhs)); + g->hash = hash_lits (nonces, g->rhs); + + LOG (g, "shrunken"); + shrink_and_gate (g, falsifies); + std::vector<LRAT_ID> reasons_lrat_src, reasons_lrat_usrc; + + update_and_gate (g, git, 0, 0, 0, 0, falsifies, 0); + ++internal->stats.congruence.simplified_ands; + ++internal->stats.congruence.simplified; + + if (ulhs_in_rhs) { // missing in Kissat, TODO: port back + CADICAL_assert (gate_contains (g, -g->lhs)); + if (internal->lrat) { + for (auto litId : g->pos_lhs_ids) { + if (litId.current_lit == g->lhs) { + compute_rewritten_clause_lrat_simple (litId.clause, 0); + break; + } + } + } + learn_congruence_unit (-g->lhs); + } +} + +bool Closure::simplify_gate (Gate *g) { + switch (g->tag) { + case Gate_Type::And_Gate: + simplify_and_gate (g); + break; + case Gate_Type::XOr_Gate: + simplify_xor_gate (g); + break; + case Gate_Type::ITE_Gate: + simplify_ite_gate (g); + break; + default: + CADICAL_assert (false); + break; + } + CADICAL_assert (lrat_chain.empty ()); + return !internal->unsat; +} + +bool Closure::simplify_gates (int lit) { + const auto &occs = goccs (lit); + for (Gate *g : occs) { + CADICAL_assert (lrat_chain.empty ()); + CADICAL_assert (clause.empty ()); + if (!simplify_gate (g)) + return false; + } + return true; +} +/*------------------------------------------------------------------------*/ +// AND gates + +Gate *Closure::find_and_lits (const vector<int> &rhs, Gate *except) { + CADICAL_assert (is_sorted (begin (rhs), end (rhs), + sort_literals_by_var_smaller (internal))); + return find_gate_lits (rhs, Gate_Type::And_Gate, except); +} + +// search for the gate in the hash-table. We cannot use find, as we might +// be changing a gate, so there might be 2 gates with the same LHS (the one +// we are changing and the other we are looking for) +Gate *Closure::find_gate_lits (const vector<int> &rhs, Gate_Type typ, + Gate *except) { + Gate *g = new Gate; + g->tag = typ; + g->rhs = rhs; + g->hash = hash_lits (nonces, g->rhs); + g->lhs = 0; + g->garbage = false; +#ifdef LOGGING + g->id = 0; +#endif + const auto &its = table.equal_range (g); + Gate *h = nullptr; + for (auto it = its.first; it != its.second; ++it) { + LOG ((*it), "checking gate in the table"); + if (*it == except) + continue; + CADICAL_assert ((*it)->lhs != g->lhs); + if ((*it)->tag != g->tag) + continue; + if ((*it)->rhs != g->rhs) + continue; + h = *it; + break; + } + + if (h) { + LOG (g, "searching"); + LOG (h, "already existing"); + delete g; + return h; + } + + else { + LOG (g->rhs, "gate not found in table"); + delete g; + return nullptr; + } +} + +Gate *Closure::new_and_gate (Clause *base_clause, int lhs) { + rhs.clear (); + auto &lits = this->lits; + + for (auto lit : lits) { + if (lhs != lit) { + CADICAL_assert (lhs != -lit); + rhs.push_back (-lit); + } + } + + CADICAL_assert (rhs.size () + 1 == lits.size ()); + sort_literals_by_var (this->rhs); + + Gate *h = find_and_lits (this->rhs); + Gate *g = new Gate; + g->lhs = lhs; + g->tag = Gate_Type::And_Gate; + if (internal->lrat) { + g->neg_lhs_ids.push_back (LitClausePair (lhs, base_clause)); + for (auto i : lrat_chain_and_gate) + g->pos_lhs_ids.push_back (i); +#ifdef LOGGING + std::vector<LRAT_ID> result; + transform (begin (g->pos_lhs_ids), end (g->pos_lhs_ids), + back_inserter (result), + [] (const LitClausePair &x) { return x.clause->id; }); + LOG (result, "lrat chain positive (%d):", lhs); + result.clear (); + transform (begin (g->neg_lhs_ids), end (g->neg_lhs_ids), + back_inserter (result), + [] (const LitClausePair &x) { return x.clause->id; }); + LOG (result, "lrat chain negative (%d):", lhs); +#endif + } + + if (internal->lrat) + lrat_chain_and_gate.clear (); + + if (h) { + std::vector<LRAT_ID> reasons_lrat_src, reasons_lrat_usrc; + if (internal->lrat) + merge_and_gate_lrat_produce_lrat (g, h, reasons_lrat_src, + reasons_lrat_usrc); + if (merge_literals_lrat (g, h, lhs, h->lhs, reasons_lrat_src, + reasons_lrat_usrc)) { + LOG ("found merged literals"); + ++internal->stats.congruence.ands; + } + return nullptr; + } else { + g->rhs = {rhs}; + CADICAL_assert (!internal->lrat || + g->pos_lhs_ids.size () == + g->arity ()); // otherwise we need intermediate clauses + g->garbage = false; + g->indexed = true; + g->shrunken = false; + g->hash = hash_lits (nonces, g->rhs); + + table.insert (g); + ++internal->stats.congruence.gates; +#ifdef LOGGING + g->id = fresh_id++; +#endif + LOG (g, "creating new"); + for (auto lit : g->rhs) { + connect_goccs (g, lit); + } + } + return g; +} + +Gate *Closure::find_first_and_gate (Clause *base_clause, int lhs) { + CADICAL_assert (internal->analyzed.empty ()); + const int not_lhs = -lhs; + LOG ("trying to find AND gate with first LHS %d", (lhs)); + LOG ("negated LHS %d occurs in %zd binary clauses", (not_lhs), + internal->occs (not_lhs).size ()); + unsigned matched = 0; + + const size_t arity = lits.size () - 1; + + for (auto w : internal->watches (not_lhs)) { + LOG (w.clause, "checking clause for candidates"); + CADICAL_assert (w.binary ()); + CADICAL_assert (w.clause->size == 2); + CADICAL_assert (w.clause->literals[0] == -lhs || w.clause->literals[1] == -lhs); + const int other = w.blit; + signed char &mark = marked (other); + if (mark) { + LOG ("marking %d mu2", other); + ++matched; + CADICAL_assert (~(mark & 2)); + mark |= 2; + internal->analyzed.push_back (other); + set_mu2_reason (other, w.clause); + if (internal->lrat) + lrat_chain_and_gate.push_back (LitClausePair (other, w.clause)); + } + } + + LOG ("found %zd initial LHS candidates", internal->analyzed.size ()); + if (matched < arity) { + if (internal->lrat) + lrat_chain_and_gate.clear (); + return nullptr; + } + + Gate *g = new_and_gate (base_clause, lhs); + + if (internal->lrat) { + lrat_chain_and_gate.clear (); + } + return g; +} + +Clause *Closure::learn_binary_tmp_or_full_clause (int a, int b) { + Clause *eq1; + if (internal->lrat) { + eq1 = add_tmp_binary_clause (a, b); + eq1 = maybe_promote_tmp_binary_clause (eq1); + } else + eq1 = maybe_add_binary_clause (a, b); + return eq1; +} + +Clause *Closure::maybe_add_binary_clause (int a, int b) { + CADICAL_assert (internal->clause.empty ()); + CADICAL_assert (internal->lrat_chain.empty ()); + CADICAL_assert (!internal->lrat); + CADICAL_assert (lrat_chain.empty ()); + LOG ("learning binary clause %d %d", a, b); + if (internal->unsat) + return nullptr; + if (a == -b) + return nullptr; + if (!internal->lrat) { + const signed char a_value = internal->val (a); + if (a_value > 0) + return nullptr; + const signed char b_value = internal->val (b); + if (b_value > 0) + return nullptr; + int unit = 0; + if (a == b) + unit = a; + else if (a_value < 0 && !b_value) { + unit = b; + } else if (!a_value && b_value < 0) + unit = a; + if (unit) { + LOG ("clause reduced to unit %d", unit); + learn_congruence_unit (unit); + return nullptr; + } + CADICAL_assert (!a_value), CADICAL_assert (!b_value); + } + return add_binary_clause (a, b); +} + +Clause *Closure::add_binary_clause (int a, int b) { + CADICAL_assert (internal->clause.empty ()); + internal->clause.push_back (a); + internal->clause.push_back (b); + if (internal->lrat) { + CADICAL_assert (lrat_chain.size () >= 1); + CADICAL_assert (internal->lrat_chain.empty ()); + swap (internal->lrat_chain, lrat_chain); + } + LOG (internal->lrat_chain, "chain"); + Clause *res = internal->new_hyper_ternary_resolved_clause_and_watch ( + false, full_watching); + const bool already_sorted = internal->vlit (a) < internal->vlit (b); + binaries.push_back (CompactBinary (res, res->id, already_sorted ? a : b, + already_sorted ? b : a)); + if (!full_watching) + new_unwatched_binary_clauses.push_back (res); + LOG (res, "learning clause"); + internal->clause.clear (); + if (internal->lrat) { + internal->lrat_chain.clear (); + } + CADICAL_assert (internal->clause.empty ()); + CADICAL_assert (internal->lrat_chain.empty ()); + return res; +} + +void Closure::check_not_tmp_binary_clause (Clause *c) { +#ifndef CADICAL_NDEBUG + CADICAL_assert (internal->lrat); + CADICAL_assert (internal->lrat_chain.empty ()); + CADICAL_assert (c->size == 2); + if (internal->val (c->literals[0]) || internal->val (c->literals[1])) + return; + CADICAL_assert (std::find (begin (extra_clauses), end (extra_clauses), c) == + end (extra_clauses)); +#else + (void) c; +#endif +}; + +Clause *Closure::maybe_promote_tmp_binary_clause (Clause *c) { + CADICAL_assert (internal->lrat); + CADICAL_assert (internal->lrat_chain.empty ()); + CADICAL_assert (c->size == 2); + LOG (c, "promoting tmp"); +#ifndef CADICAL_NDEBUG + CADICAL_assert (std::find (begin (extra_clauses), end (extra_clauses), c) != + end (extra_clauses)); +#endif + if (internal->val (c->literals[0]) || internal->val (c->literals[1])) + return c; + lrat_chain.push_back (c->id); + Clause *res = add_binary_clause (c->literals[0], c->literals[1]); + LOG (res, "promoted to"); + return res; +}; + +Clause *Closure::add_tmp_binary_clause (int a, int b) { + CADICAL_assert (internal->clause.empty ()); + CADICAL_assert (internal->lrat_chain.empty ()); + CADICAL_assert (internal->lrat); + LOG ("learning tmp binary clause %d %d", a, b); + if (internal->unsat) + return nullptr; + if (a == -b) + return nullptr; + CADICAL_assert (internal->clause.empty ()); + internal->clause.push_back (a); + internal->clause.push_back (b); + if (internal->lrat) { + CADICAL_assert (lrat_chain.size () >= 1); + CADICAL_assert (internal->lrat_chain.empty ()); + } + LOG (lrat_chain, "chain"); + Clause *res = new_tmp_clause (internal->clause); + internal->clause.clear (); + if (internal->lrat) { + lrat_chain.clear (); + } + CADICAL_assert (internal->clause.empty ()); + CADICAL_assert (internal->lrat_chain.empty ()); + LOG (res, "promoted to"); + return res; +} + +Gate *Closure::find_remaining_and_gate (Clause *base_clause, int lhs) { + const int not_lhs = -lhs; + + if (marked (not_lhs) < 2) { + LOG ("skipping no-candidate LHS %d (%d)", lhs, marked (not_lhs)); + return nullptr; + } + + LOG ("trying to find AND gate with remaining LHS %d", (lhs)); + LOG ("negated LHS %d occurs times in %zd binary clauses", (not_lhs), + internal->noccs (-lhs)); + + const size_t arity = lits.size () - 1; + size_t matched = 0; + CADICAL_assert (1 < arity); + + for (auto w : internal->watches (not_lhs)) { + CADICAL_assert (w.binary ()); +#ifdef LOGGING + Clause *c = w.clause; + LOG (c, "checking"); + CADICAL_assert (c->size == 2); + CADICAL_assert (c->literals[0] == not_lhs || c->literals[1] == not_lhs); +#endif + const int other = w.blit; + signed char &mark = marked (other); + if (!mark) + continue; + ++matched; + if (!(mark & 2)) { + lrat_chain_and_gate.push_back (LitClausePair (other, w.clause)); + LOG ("pushing %d -> %zd", other, w.clause->id); + continue; + } + LOG ("marking %d mu4", other); + CADICAL_assert (!(mark & 4)); + mark |= 4; + lrat_chain_and_gate.push_back (LitClausePair (other, w.clause)); + if (internal->lrat) + set_mu4_reason (other, w.clause); + } + + { + auto q = begin (internal->analyzed); + CADICAL_assert (!internal->analyzed.empty ()); + CADICAL_assert (marked (not_lhs) == 3); + for (auto lit : internal->analyzed) { + signed char &mark = marked (lit); + if (lit == not_lhs) { + mark = 1; + continue; + } + + CADICAL_assert ((mark & 3) == 3); + if (mark & 4) { + mark = 3; + *q = lit; + ++q; + LOG ("keeping LHS candidate %d", -lit); + } else { + LOG ("dropping LHS candidate %d", -lit); + mark = 1; + } + } + CADICAL_assert (q != end (internal->analyzed)); + CADICAL_assert (marked (not_lhs) == 1); + internal->analyzed.resize (q - begin (internal->analyzed)); + LOG ("after filtering %zu LHS candidate remain", + internal->analyzed.size ()); + } + + if (matched < arity) { + if (internal->lrat) + lrat_chain_and_gate.clear (); + return nullptr; + } + + if (!internal->lrat) + lrat_chain_and_gate.clear (); + return new_and_gate (base_clause, lhs); +} + +struct congruence_occurrences_rank { + Internal *internal; + congruence_occurrences_rank (Internal *s) : internal (s) {} + typedef uint64_t Type; + Type operator() (int a) { + uint64_t res = internal->noccs (-a); + res <<= 32; + res |= a; + return res; + } +}; + +struct congruence_occurrences_larger { + Internal *internal; + congruence_occurrences_larger (Internal *s) : internal (s) {} + bool operator() (const int &a, const int &b) const { + return congruence_occurrences_rank (internal) (a) < + congruence_occurrences_rank (internal) (b); + } +}; + +void Closure::extract_and_gates_with_base_clause (Clause *c) { + CADICAL_assert (!c->garbage); + CADICAL_assert (lrat_chain.empty ()); + LOG (c, "extracting and gates with clause"); + unsigned size = 0; + const unsigned arity_limit = + min (internal->opts.congruenceandarity, MAX_ARITY); + const unsigned size_limit = arity_limit + 1; + size_t max_negbincount = 0; + lits.clear (); + + for (int lit : *c) { + signed char v = internal->val (lit); + if (v < 0) { + // push_lrat_unit (-lit); + continue; + } + if (v > 0) { + CADICAL_assert (!internal->level); + LOG (c, "found satisfied clause"); + internal->mark_garbage (c); + if (internal->lrat) + lrat_chain.clear (); + return; + } + if (++size > size_limit) { + LOG (c, "clause is actually too large, thus skipping"); + if (internal->lrat) + lrat_chain.clear (); + return; + } + const size_t count = internal->noccs (-lit); + if (!count) { + LOG (c, + "%d negated does not occur in any binary clause, thus skipping", + lit); + if (internal->lrat) + lrat_chain.clear (); + return; + } + + if (count > max_negbincount) + max_negbincount = count; + lits.push_back (lit); + } + + if (size < 3) { + LOG (c, "is actually too small, thus skipping"); + if (internal->lrat) + lrat_chain.clear (); + CADICAL_assert (lrat_chain.empty ()); + return; + } + + const size_t arity = size - 1; + if (max_negbincount < arity) { + LOG (c, + "all literals have less than %lu negated occurrences" + "thus skipping", + arity); + if (internal->lrat) + lrat_chain.clear (); + return; + } + + internal->analyzed.clear (); + size_t reduced = 0; + const size_t clause_size = lits.size (); + for (size_t i = 0; i < clause_size; ++i) { + const int lit = lits[i]; + const unsigned count = internal->noccs (-lit); + marked (-lit) = 1; + set_mu1_reason (-lit, c); + if (count < arity) { + if (reduced < i) { + lits[i] = lits[reduced]; + lits[reduced++] = lit; + } else if (reduced == i) + ++reduced; + } + } + const size_t reduced_size = clause_size - reduced; + CADICAL_assert (reduced_size); + LOG (c, "trying as base arity %lu AND gate", arity); + CADICAL_assert (begin (lits) + reduced_size <= end (lits)); + MSORT (internal->opts.radixsortlim, begin (lits), + begin (lits) + reduced_size, + congruence_occurrences_rank (internal), + congruence_occurrences_larger (internal)); + bool first = true; + unsigned extracted = 0; + + for (size_t i = 0; i < clause_size; ++i) { + CADICAL_assert (lrat_chain.empty ()); + if (internal->unsat) + break; + if (c->garbage) + break; + const int lhs = lits[i]; + LOG ("trying LHS candidate literal %d with %ld negated occurrences", + (lhs), internal->noccs (-lhs)); + + if (first) { + first = false; + CADICAL_assert (internal->analyzed.empty ()); + if (find_first_and_gate (c, lhs) != nullptr) { + CADICAL_assert (lrat_chain.empty ()); + ++extracted; + } + } else if (internal->analyzed.empty ()) { + LOG ("early abort AND gate search"); + break; + } else if (find_remaining_and_gate (c, lhs)) { + CADICAL_assert (lrat_chain.empty ()); + ++extracted; + } + } + + unmark_all (); + LOG (lits, "finish unmarking"); + for (auto lit : lits) { + marked (-lit) = 0; + } + lrat_chain_and_gate.clear (); + if (extracted) + LOG (c, "extracted %u with arity %lu AND base", extracted, arity); +} + +void Closure::reset_and_gate_extraction () { + internal->clear_noccs (); + internal->clear_watches (); +} + +void Closure::extract_and_gates () { + CADICAL_assert (!full_watching); + if (!internal->opts.congruenceand) + return; + START (extractands); + marks.resize (internal->max_var * 2 + 3); + init_and_gate_extraction (); + + const size_t size = internal->clauses.size (); + for (size_t i = 0; i < size && !internal->terminated_asynchronously (); + ++i) { // we can learn new binary clauses, but no for loop + CADICAL_assert (lrat_chain.empty ()); + Clause *c = internal->clauses[i]; + if (c->garbage) + continue; + if (c->size == 2) + continue; + if (c->hyper) + continue; + if (c->redundant) + continue; + extract_and_gates_with_base_clause (c); + CADICAL_assert (lrat_chain.empty ()); + } + + reset_and_gate_extraction (); + STOP (extractands); +} + +/*------------------------------------------------------------------------*/ +// XOR gates + +uint64_t &Closure::new_largecounts (int lit) { + CADICAL_assert (internal->vlit (lit) < gnew_largecounts.size ()); + return gnew_largecounts[internal->vlit (lit)]; +} + +uint64_t &Closure::largecounts (int lit) { + CADICAL_assert (internal->vlit (lit) < glargecounts.size ()); + return glargecounts[internal->vlit (lit)]; +} + +bool parity_lits (const vector<int> &lits) { + unsigned res = 0; + for (auto lit : lits) + res ^= (lit < 0); + return res; +} + +void inc_lits (vector<int> &lits) { + bool carry = true; + for (size_t i = 0; i < lits.size () && carry; ++i) { + int lit = lits[i]; + carry = (lit < 0); + lits[i] = -lit; + } +} + +void Closure::check_ternary (int a, int b, int c) { + CADICAL_assert (internal->clause.empty ()); + if (internal->lrat) + return; + auto &clause = internal->clause; + CADICAL_assert (clause.empty ()); + clause.push_back (a); + clause.push_back (b); + clause.push_back (c); + internal->external->check_learned_clause (); + if (internal->proof) { + const LRAT_ID id = internal->clause_id++; + internal->proof->add_derived_clause (id, false, clause, {}); + internal->proof->delete_clause (id, false, clause); + } + + clause.clear (); +} + +void Closure::check_binary_implied (int a, int b) { + CADICAL_assert (internal->clause.empty ()); + if (internal->lrat) + return; + auto &clause = internal->clause; + CADICAL_assert (clause.empty ()); + clause.push_back (a); + clause.push_back (b); + check_implied (); + clause.clear (); +} + +void Closure::check_implied () { + if (internal->lrat) + return; + internal->external->check_learned_clause (); +} + +void Closure::add_xor_shrinking_proof_chain (Gate *g, int pivot) { + CADICAL_assert (internal->clause.empty ()); + CADICAL_assert (clause.empty ()); + if (!internal->proof) + return; + LOG (g, "starting XOR shrinking proof chain"); + vector<LitClausePair> first; + vector<LitClausePair> newclauses; + if (internal->lrat) { + simplify_and_sort_xor_lrat_clauses (g->pos_lhs_ids, first, g->lhs, + pivot); + gate_sort_lrat_reasons (first, pivot, g->lhs); + } + + auto &clause = internal->clause; + + const int lhs = g->lhs; + clause.push_back (-lhs); + for (auto lit : g->rhs) + clause.push_back (lit); + + const bool parity = (lhs > 0); + CADICAL_assert (parity == parity_lits (clause)); + const size_t size = clause.size (); + const unsigned end = 1u << (size - 1); + CADICAL_assert (!internal->lrat || first.size () == 2 * end); +#ifdef LOGGING + for (auto pair : first) { + LOG (pair.clause, "key %d", pair.current_lit); + } +#endif + // TODO Florian adjust indices of first depending on order... + // + for (unsigned i = 0; i != end; ++i) { + while (i && parity != parity_lits (clause)) + inc_lits (clause); + LOG (clause, "xor shrinking clause"); + if (!internal->lrat) { + clause.push_back (pivot); + check_and_add_to_proof_chain (clause); + clause.pop_back (); + clause.push_back (-pivot); + check_and_add_to_proof_chain (clause); + clause.pop_back (); + } + if (internal->lrat) { + CADICAL_assert (lrat_chain.empty ()); + lrat_chain.push_back (first[2 * i].clause->id); + lrat_chain.push_back (first[2 * i + 1].clause->id); + } + if (clause.size () > 1) { + if (internal->lrat) { + Clause *c = new_tmp_clause (clause); + newclauses.push_back (LitClausePair (0, c)); + lrat_chain.clear (); + } else { + check_and_add_to_proof_chain (clause); + } + } + if (clause.size () == 1) + return; + inc_lits (clause); + } + g->pos_lhs_ids.swap (newclauses); + + clause.clear (); +} + +void Closure::check_xor_gate_implied (Gate const *const g) { + CADICAL_assert (internal->clause.empty ()); + CADICAL_assert (g->tag == Gate_Type::XOr_Gate); + if (internal->lrat) { + return; + } + const int lhs = g->lhs; + LOG (g, "checking implied"); + auto &clause = internal->clause; + CADICAL_assert (clause.empty ()); + for (auto other : g->rhs) { + CADICAL_assert (other > 0); + clause.push_back (other); + } + clause.push_back (-lhs); + const unsigned arity = g->arity (); + const unsigned end = 1u << arity; + const bool parity = (lhs > 0); + + for (unsigned i = 0; i != end; ++i) { + while (i && parity_lits (clause) != parity) + inc_lits (clause); + internal->external->check_learned_clause (); + if (internal->proof) { + internal->proof->add_derived_clause (internal->clause_id, false, + clause, {}); + internal->proof->delete_clause (internal->clause_id, false, clause); + } + inc_lits (clause); + } + clause.clear (); +} + +Gate *Closure::find_xor_lits (const vector<int> &rhs) { + CADICAL_assert (is_sorted (begin (rhs), end (rhs), + sort_literals_by_var_smaller (internal))); + return find_gate_lits (rhs, Gate_Type::XOr_Gate); +} + +Gate *Closure::find_xor_gate (Gate *g) { + CADICAL_assert (g->tag == Gate_Type::XOr_Gate); + CADICAL_assert (is_sorted (begin (g->rhs), end (g->rhs), + sort_literals_by_var_smaller (internal))); + return find_gate_lits (g->rhs, Gate_Type::XOr_Gate); +} + +void Closure::reset_xor_gate_extraction () { internal->clear_occs (); } + +bool Closure::normalize_ite_lits_gate (Gate *g) { + auto &rhs = g->rhs; + CADICAL_assert (rhs.size () == 3); + if (internal->lrat) + check_correct_ite_flags (g); + LOG (rhs, "RHS = "); + if (rhs[0] < 0) { + rhs[0] = -rhs[0]; + std::swap (rhs[1], rhs[2]); + if (internal->lrat) { + CADICAL_assert (g->pos_lhs_ids.size () == 4); + std::swap (g->pos_lhs_ids[0], g->pos_lhs_ids[2]); + std::swap (g->pos_lhs_ids[1], g->pos_lhs_ids[3]); + const int8_t flag = g->degenerated_ite; + const int8_t plus_then = flag & NO_PLUS_THEN; + const int8_t neg_then = flag & NO_NEG_THEN; + const int8_t plus_else = flag & NO_PLUS_ELSE; + const int8_t neg_else = flag & NO_NEG_ELSE; + g->degenerated_ite = (plus_then ? Special_ITE_GATE::NO_PLUS_ELSE + : Special_ITE_GATE::NORMAL) | + (neg_then ? Special_ITE_GATE::NO_NEG_ELSE + : Special_ITE_GATE::NORMAL) | + (plus_else ? Special_ITE_GATE::NO_PLUS_THEN + : Special_ITE_GATE::NORMAL) | + (neg_else ? Special_ITE_GATE::NO_NEG_THEN + : Special_ITE_GATE::NORMAL); + CADICAL_assert (g->pos_lhs_ids[0].current_lit == rhs[1]); + CADICAL_assert (g->pos_lhs_ids[2].current_lit == rhs[2]); + if (internal->lrat) + check_correct_ite_flags (g); + } + } + if (rhs[1] > 0) + return false; + if (internal->lrat) + check_correct_ite_flags (g); + rhs[1] = -rhs[1]; + rhs[2] = -rhs[2]; + LOG (rhs, "RHS = "); + if (internal->lrat) { + CADICAL_assert (g->pos_lhs_ids.size () == 4); + std::swap (g->pos_lhs_ids[0], g->pos_lhs_ids[1]); + std::swap (g->pos_lhs_ids[2], g->pos_lhs_ids[3]); + const int8_t flag = g->degenerated_ite; + const int8_t plus_then = flag & NO_PLUS_THEN; + const int8_t neg_then = flag & NO_NEG_THEN; + const int8_t plus_else = flag & NO_PLUS_ELSE; + const int8_t neg_else = flag & NO_NEG_ELSE; + g->degenerated_ite = (plus_then ? Special_ITE_GATE::NO_NEG_THEN + : Special_ITE_GATE::NORMAL) | + (neg_then ? Special_ITE_GATE::NO_PLUS_THEN + : Special_ITE_GATE::NORMAL) | + (plus_else ? Special_ITE_GATE::NO_NEG_ELSE + : Special_ITE_GATE::NORMAL) | + (neg_else ? Special_ITE_GATE::NO_PLUS_ELSE + : Special_ITE_GATE::NORMAL); + CADICAL_assert (g->pos_lhs_ids[0].current_lit == rhs[1]); + CADICAL_assert (g->pos_lhs_ids[2].current_lit == rhs[2]); + // incorrect as we have not negated the LHS yet! + // check_correct_ite_flags (g); + } + + LOG (g->rhs, "g/RHS = "); + return true; +} + +#ifndef CADICAL_NDEBUG +bool is_tautological_ite_gate (Gate *g) { + CADICAL_assert (g->tag == Gate_Type::ITE_Gate); + CADICAL_assert (g->rhs.size () == 3); + const int cond_lit = g->rhs[0]; + const int then_lit = g->rhs[1]; + const int else_lit = g->rhs[2]; + return cond_lit == then_lit || cond_lit == else_lit; +} +#endif + +Gate *Closure::find_ite_gate (Gate *g, bool &negate_lhs) { + negate_lhs = normalize_ite_lits_gate (g); + LOG (g, "post normalize"); + return find_gate_lits (g->rhs, Gate_Type::ITE_Gate, g); +} + +LRAT_ID Closure::check_and_add_to_proof_chain (vector<int> &clause) { + internal->external->check_learned_clause (); + const LRAT_ID id = ++internal->clause_id; + if (internal->proof) { + if (internal->lrat) { + CADICAL_assert (internal->lrat_chain.empty ()); + CADICAL_assert (lrat_chain.size () >= 1); + } + internal->proof->add_derived_clause (id, true, clause, lrat_chain); + lrat_chain.clear (); + } + return id; +} + +void Closure::add_clause_to_chain (std::vector<int> unsimplified, + LRAT_ID id) { + const uint32_t id2_higher = (id >> 32); + const uint32_t id2_lower = (uint32_t) (id & (LRAT_ID) (uint32_t) (-1)); + CADICAL_assert (id == ((LRAT_ID) id2_higher << 32) + (LRAT_ID) id2_lower); + chain.push_back (id2_higher); + chain.push_back (id2_lower); + LOG (unsimplified, "pushing to chain"); + chain.insert (end (chain), begin (unsimplified), end (unsimplified)); + chain.push_back (0); +} + +LRAT_ID Closure::simplify_and_add_to_proof_chain (vector<int> &unsimplified, + LRAT_ID delete_id) { + vector<int> &clause = internal->clause; + CADICAL_assert (clause.empty ()); +#ifndef CADICAL_NDEBUG + for (auto lit : unsimplified) { + CADICAL_assert (!(marked (lit) & 4)); + } +#endif + + bool trivial = false; + for (auto lit : unsimplified) { + signed char &lit_mark = marked (lit); + if (lit_mark & 4) + continue; + signed char ¬_lit_mark = marked (-lit); + if (not_lit_mark & 4) { + trivial = true; + break; + } + lit_mark |= 4; + clause.push_back (lit); + } + for (auto lit : clause) { + signed char &mark = marked (lit); + CADICAL_assert (mark & 4); + mark &= ~4u; + } + + LRAT_ID id = 0; + if (!trivial) { + if (delete_id) { + if (internal->proof) { + internal->proof->delete_clause (delete_id, true, clause); + lrat_chain.clear (); + } + } else { + id = check_and_add_to_proof_chain (clause); + add_clause_to_chain (clause, id); + } + } else { + LOG ("skipping trivial proof"); + lrat_chain.clear (); + } + clause.clear (); + return id; +} +/*------------------------------------------------------------------------*/ +void Closure::add_ite_turned_and_binary_clauses (Gate *g) { + if (!internal->proof) + return; + if (internal->lrat) + return; + LOG ("starting ITE turned AND supporting binary clauses"); + CADICAL_assert (unsimplified.empty ()); + CADICAL_assert (chain.empty ()); + int not_lhs = -g->lhs; + unsimplified.push_back (not_lhs); + unsimplified.push_back (g->rhs[0]); + simplify_and_add_to_proof_chain (unsimplified); + unsimplified.pop_back (); + unsimplified.push_back (g->rhs[1]); + simplify_and_add_to_proof_chain (unsimplified); + unsimplified.clear (); +} +void Closure::simplify_unit_xor_lrat_clauses ( + const vector<LitClausePair> &source, int lhs) { + CADICAL_assert (internal->lrat); + for (auto pair : source) { + compute_rewritten_clause_lrat_simple (pair.clause, lhs); + if (lrat_chain.size ()) + break; + } + CADICAL_assert (clause.size () == 1); +} +void Closure::simplify_and_sort_xor_lrat_clauses ( + const vector<LitClausePair> &source, vector<LitClausePair> &target, + int lhs, int except2, bool flip) { + CADICAL_assert (internal->lrat); + for (auto pair : source) { + Clause *c = produce_rewritten_clause_lrat (pair.clause, lhs); + if (c) { + target.push_back (LitClausePair (0, c)); + } + } + gate_sort_lrat_reasons (target, lhs, except2, flip); +} +void Closure::add_xor_matching_proof_chain ( + Gate *g, int lhs1, const vector<LitClausePair> &clauses2, int lhs2, + vector<LRAT_ID> &to_lrat, vector<LRAT_ID> &back_lrat) { + if (lhs1 == lhs2) + return; + if (!internal->proof) + return; + CADICAL_assert (unsimplified.empty ()); + unsimplified = g->rhs; + vector<LitClausePair> first; + vector<LitClausePair> second; + if (internal->lrat) { + simplify_and_sort_xor_lrat_clauses (g->pos_lhs_ids, first, lhs1); + simplify_and_sort_xor_lrat_clauses (clauses2, second, lhs2, 0, 1); + g->pos_lhs_ids = first; + } + LOG ("starting XOR matching proof"); + // for lrat + vector<LitIdPair> first_ids; + vector<LitIdPair> second_ids; + for (auto pair : first) { + bool first = pair.current_lit & 1; + int rest = pair.current_lit >> 1; + rest &= ~(1 << (g->rhs.size () - 1)); + if (first == (lhs1 > 0)) { + first_ids.push_back (LitIdPair (rest, pair.clause->id)); + } else { + second_ids.push_back (LitIdPair (rest, pair.clause->id)); + } + LOG (pair.clause, "key %d", pair.current_lit); + } + for (auto pair : second) { + bool first = pair.current_lit & 1; + int rest = pair.current_lit >> 1; + rest &= ~(1 << (g->rhs.size () - 1)); + if (first == (lhs2 < 0)) { + first_ids.push_back (LitIdPair (rest, pair.clause->id)); + } else { + second_ids.push_back (LitIdPair (rest, pair.clause->id)); + } + LOG (pair.clause, "key %d", pair.current_lit); + } + // TODO Florian: resort and ids after every round + do { + vector<LitIdPair> first_tmp; + vector<LitIdPair> second_tmp; + CADICAL_assert (!unsimplified.empty ()); + unsimplified.pop_back (); + const size_t size = unsimplified.size (); + CADICAL_assert (size < 32); + const size_t off = 1u << size; + for (size_t i = 0; i != off; ++i) { + int32_t n = 0; + if (internal->lrat) { + n = number_from_xor_reason_reversed (unsimplified); + CADICAL_assert (lrat_chain.empty ()); + for (auto pair : first_ids) { + if (pair.lit == n) + lrat_chain.push_back (pair.id); + } + CADICAL_assert (lrat_chain.size () == 2); + } + unsimplified.push_back (-lhs1); + unsimplified.push_back (lhs2); + const LRAT_ID id1 = simplify_and_add_to_proof_chain (unsimplified); + unsimplified.resize (unsimplified.size () - 2); + if (internal->lrat) { + int32_t rest = n &= ~(1 << (unsimplified.size () - 1)); + first_tmp.push_back (LitIdPair (rest, id1)); + n = number_from_xor_reason_reversed (unsimplified); + lrat_chain.clear (); + for (auto pair : second_ids) { + if (pair.lit == n) + lrat_chain.push_back (pair.id); + } + CADICAL_assert (lrat_chain.size () == 2); + } + unsimplified.push_back (lhs1); + unsimplified.push_back (-lhs2); + const LRAT_ID id2 = simplify_and_add_to_proof_chain (unsimplified); + unsimplified.resize (unsimplified.size () - 2); + if (internal->lrat) { + lrat_chain.clear (); + int32_t rest = n &= ~(1 << (unsimplified.size () - 1)); + second_tmp.push_back (LitIdPair (rest, id2)); + } + inc_lits (unsimplified); + } + if (internal->lrat) { + first_ids.swap (first_tmp); + second_ids.swap (second_tmp); + } + } while (!unsimplified.empty ()); + if (internal->lrat) { + CADICAL_assert (first_ids.size () == 1); + CADICAL_assert (second_ids.size () == 1); + to_lrat.push_back (first_ids.back ().id); + back_lrat.push_back (second_ids.back ().id); + } + CADICAL_assert (!internal->lrat || to_lrat.size () == 1); + CADICAL_assert (!internal->lrat || back_lrat.size () == 1); + LOG ("finished XOR matching proof"); + CADICAL_assert (unsimplified.empty ()); +} + +// this function needs to either put the clauses from +// lrat_chain_and_gate into g->pos_neg_ids or clear it or do something with +// it if you merge gates. +Gate *Closure::new_xor_gate (const vector<LitClausePair> &glauses, + int lhs) { + rhs.clear (); + + for (auto lit : lits) { + if (lhs != lit && -lhs != lit) { + CADICAL_assert (lit > 0); + rhs.push_back (lit); + } + } + CADICAL_assert (rhs.size () + 1 == lits.size ()); + sort_literals_by_var (rhs); + Gate *g = find_xor_lits (this->rhs); + if (g) { + check_xor_gate_implied (g); + std::vector<LRAT_ID> reasons_implication, reasons_back; + add_xor_matching_proof_chain (g, g->lhs, glauses, lhs, + reasons_implication, reasons_back); + if (merge_literals_lrat (g->lhs, lhs, reasons_implication, + reasons_back)) { + ++internal->stats.congruence.xors; + } + delete_proof_chain (); + CADICAL_assert (internal->unsat || chain.empty ()); + } else { + g = new Gate; + g->lhs = lhs; + g->tag = Gate_Type::XOr_Gate; + g->rhs = {rhs}; + g->garbage = false; + g->indexed = true; + g->shrunken = false; + g->hash = hash_lits (nonces, g->rhs); + for (auto pair : glauses) + g->pos_lhs_ids.push_back (pair); + table.insert (g); + ++internal->stats.congruence.gates; +#ifdef LOGGING + g->id = fresh_id++; +#endif + LOG (g, "creating new"); + check_xor_gate_implied (g); + for (auto lit : g->rhs) { + connect_goccs (g, lit); + } + } + return g; +} +uint32_t +Closure::number_from_xor_reason_reversed (const std::vector<int> &rhs) { + uint32_t n = 0; + CADICAL_assert (is_sorted (rhs.rbegin (), rhs.rend (), + sort_literals_by_var_smaller_except (internal, 0, 0))); + CADICAL_assert (rhs.size () <= 32); + for (auto r = rhs.rbegin (); r != rhs.rend (); r++) { + int lit = *r; + n *= 2; + n += !(lit > 0); + } + return n; +} + +uint32_t Closure::number_from_xor_reason (const std::vector<int> &rhs, + int lhs, int except, bool flip) { + uint32_t n = 0; + CADICAL_assert (is_sorted ( + begin (rhs), end (rhs), + sort_literals_by_var_smaller_except (internal, lhs, except))); + (void) lhs, (void) except; + CADICAL_assert (rhs.size () <= 32); + for (auto lit : rhs) { + n *= 2; + n += !(lit > 0) ^ flip; + flip = 0; + } + return n; +} + +// this is how I planned to sort it and produce the number +// Look at this first +void Closure::gate_sort_lrat_reasons (LitClausePair &litId, int lhs, + int except2, bool flip) { + CADICAL_assert (clause.empty ()); + std::copy (begin (*litId.clause), end (*litId.clause), + back_inserter (clause)); + sort_literals_by_var_except (clause, lhs, except2); + litId.current_lit = number_from_xor_reason (clause, lhs, except2, flip); + clause.clear (); +} + +struct smaller_pair_first_rank { + typedef size_t Type; + Type operator() (const LitClausePair &a) { return a.current_lit; } +}; + +// this is how I planned to sort it and produce the number +// Look at this first +void Closure::gate_sort_lrat_reasons (std::vector<LitClausePair> &xs, + int lhs, int except2, bool flip) { + CADICAL_assert (clause.empty ()); + CADICAL_assert (!xs.empty ()); + for (auto &litId : xs) { + gate_sort_lrat_reasons (litId, lhs, except2, flip); + } + + rsort (begin (xs), end (xs), smaller_pair_first_rank ()); + +#ifndef CADICAL_NDEBUG + std::for_each (begin (xs), end (xs), [&xs] (const LitClausePair &x) { + CADICAL_assert (x.clause->size == xs[1].clause->size); + }); +#endif +} + +void Closure::init_xor_gate_extraction (std::vector<Clause *> &candidates) { + const unsigned arity_limit = internal->opts.congruencexorarity; + CADICAL_assert (arity_limit < 32); // we use unsigned int. + const unsigned size_limit = arity_limit + 1; + glargecounts.resize (2 * internal->vsize, 0); + + for (auto c : internal->clauses) { + LOG (c, "considering clause for XOR"); + if (c->redundant) + continue; + if (c->garbage) + continue; + if (c->size < 3) + continue; + unsigned size = 0; + for (auto lit : *c) { + const signed char v = internal->val (lit); + if (v < 0) + continue; + if (v > 0) { + LOG (c, "satisfied by %d", lit); + internal->mark_garbage (c); + goto CONTINUE_COUNTING_NEXT_CLAUSE; + } + if (size == size_limit) + goto CONTINUE_COUNTING_NEXT_CLAUSE; + ++size; + } + + if (size < 3) + continue; + for (auto lit : *c) { + if (internal->val (lit)) + continue; + ++largecounts (lit); + } + + LOG (c, "considering clause for XOR as candidate"); + candidates.push_back (c); + CONTINUE_COUNTING_NEXT_CLAUSE:; + } + + LOG ("considering %zd out of %zd", candidates.size (), + internal->irredundant ()); + const unsigned rounds = internal->opts.congruencexorcounts; +#ifdef LOGGING + const size_t original_size = candidates.size (); +#endif + LOG ("resizing glargecounts to size %zd", glargecounts.size ()); + for (unsigned round = 0; round < rounds; ++round) { + LOG ("round %d of XOR extraction", round); + size_t removed = 0; + gnew_largecounts.resize (2 * internal->vsize); + unsigned cand_size = candidates.size (); + size_t j = 0; + for (size_t i = 0; i < cand_size; ++i) { + Clause *c = candidates[i]; + LOG (c, "considering"); + unsigned size = 0; + for (auto lit : *c) { + if (!internal->val (lit)) + ++size; + } + CADICAL_assert (3 <= size); + CADICAL_assert (size <= size_limit); + const unsigned arity = size - 1; + const unsigned needed_clauses = 1u << (arity - 1); + for (auto lit : *c) { + if (largecounts (lit) < needed_clauses) { + LOG (c, "not enough occurrences, so ignoring"); + removed++; + goto CONTINUE_WITH_NEXT_CANDIDATE_CLAUSE; + } + } + for (auto lit : *c) + if (!internal->val (lit)) + new_largecounts (lit)++; + candidates[j++] = candidates[i]; + + CONTINUE_WITH_NEXT_CANDIDATE_CLAUSE:; + } + candidates.resize (j); + glargecounts = std::move (gnew_largecounts); + gnew_largecounts.clear (); + LOG ("moving counts %zd", glargecounts.size ()); + if (!removed) + break; + + LOG ("after round %d, %zd (%ld %%) remain", round, candidates.size (), + candidates.size () / (1 + original_size) * 100); + } + + for (auto c : candidates) { + for (auto lit : *c) + internal->occs (lit).push_back (c); + } +} + +Clause *Closure::find_large_xor_side_clause (std::vector<int> &lits) { + unsigned least_occurring_literal = 0; + unsigned count_least_occurring = UINT_MAX; + const size_t size_lits = lits.size (); +#if defined(LOGGING) || !defined(CADICAL_NDEBUG) + const unsigned arity = size_lits - 1; +#endif +#ifndef CADICAL_NDEBUG + const unsigned count_limit = 1u << (arity - 1); +#endif + LOG (lits, "trying to find arity %u XOR side clause", arity); + for (auto lit : lits) { + CADICAL_assert (!internal->val (lit)); + marked (lit) = 1; + unsigned count = largecount (lit); + CADICAL_assert (count_limit <= count); + if (count >= count_least_occurring) + continue; + count_least_occurring = count; + least_occurring_literal = lit; + } + Clause *res = 0; + CADICAL_assert (least_occurring_literal); + LOG ("searching XOR side clause watched by %d#%u", + least_occurring_literal, count_least_occurring); + LOG ("searching for size %ld", size_lits); + for (auto c : internal->occs (least_occurring_literal)) { + LOG (c, "checking"); + CADICAL_assert (c->size != 2); // TODO kissat has break + if (c->garbage) + continue; + if ((size_t) c->size < size_lits) + continue; + size_t found = 0; + for (auto other : *c) { + const signed char value = internal->val (other); + if (value < 0) + continue; + if (value > 0) { + LOG (c, "found satisfied %d in", other); + internal->mark_garbage (c); + CADICAL_assert (c->garbage); + break; + } + if (marked (other)) + found++; + else { + LOG ("not marked %d", other); + found = 0; + break; + } + } + if (found == size_lits && !c->garbage) { + res = c; + break; + } else { + LOG ("too few literals"); + } + } + for (auto lit : lits) + marked (lit) = 0; + if (res) + LOG (res, "found matching XOR side"); + else + LOG ("no matching XOR side clause found"); + return res; +} + +void Closure::extract_xor_gates_with_base_clause (Clause *c) { + LOG (c, "checking clause"); + lits.clear (); + int smallest = 0; + int largest = 0; + const unsigned arity_limit = internal->opts.congruencexorarity; + const unsigned size_limit = arity_limit + 1; + unsigned negated = 0, size = 0; + bool first = true; + for (auto lit : *c) { + const signed char v = internal->val (lit); + if (v < 0) + continue; + if (v > 0) { + internal->mark_garbage (c); + return; + } + if (size == size_limit) { + LOG (c, "size limit reached"); + return; + } + + if (first) { + largest = smallest = lit; + first = false; + } else { + CADICAL_assert (smallest); + CADICAL_assert (largest); + if (internal->vlit (lit) < internal->vlit (smallest)) { + LOG ("new smallest %d", lit); + smallest = lit; + } + if (internal->vlit (lit) > internal->vlit (largest)) { + if (largest < 0) { + LOG (c, "not largest %d (largest: %d) occurs negated in XOR base", + lit, largest); + return; + } + largest = lit; + } + } + if (lit < 0 && internal->vlit (lit) < internal->vlit (largest)) { + LOG (c, "negated literal %d not largest in XOR base", lit); + return; + } + if (lit < 0 && negated++) { + LOG (c, "more than one negated literal in XOR base"); + return; + } + lits.push_back (lit); + ++size; + } + CADICAL_assert (size == lits.size ()); + if (size < 3) { + LOG (c, "short XOR base clause"); + return; + } + + LOG ("double checking if possible"); + const unsigned arity = size - 1; + const unsigned needed_clauses = 1u << (arity - 1); + for (auto lit : lits) { + for (int sign = 0; sign != 2; ++sign, lit = -lit) { + unsigned count = largecount (lit); + if (count >= needed_clauses) + continue; + LOG (c, + "literal %d in XOR base clause only occurs %u times in large " + "clause thus skipping", + lit, count); + return; + } + } + + LOG ("checking for XOR side clauses"); + CADICAL_assert (smallest && largest); + const unsigned end = 1u << arity; + CADICAL_assert (negated == parity_lits (lits)); + unsigned found = 0; + vector<LitClausePair> glauses; + glauses.push_back (LitClausePair (0, c)); + for (unsigned i = 0; i != end; ++i) { + while (i && parity_lits (lits) != negated) + inc_lits (lits); + if (i) { + // the clause must be stored + // you can use `lrat_chain_and_gate` for this + Clause *d = find_large_xor_side_clause (lits); + if (!d) + return; + CADICAL_assert (!d->redundant); + glauses.push_back (LitClausePair (i, d)); + } else + CADICAL_assert (!c->redundant); + inc_lits (lits); + ++found; + } + + while (parity_lits (lits) != negated) + inc_lits (lits); + LOG (lits, "found all needed %u matching clauses:", found); + CADICAL_assert (found == 1u << arity); + if (negated) { + auto p = begin (lits); + int lit; + while ((lit = *p) > 0) + p++; + LOG ("flipping RHS literal %d", (lit)); + *p = -lit; + } + LOG (lits, "normalized negations"); + unsigned extracted = 0; + for (auto lhs : lits) { + if (!negated) + lhs = -lhs; + Gate *g = new_xor_gate (glauses, lhs); + if (g) + extracted++; + if (internal->unsat) + break; + } + if (!extracted) + LOG ("no arity %u XOR gate extracted", arity); +} +void Closure::extract_xor_gates () { + CADICAL_assert (!full_watching); + if (!internal->opts.congruencexor) + return; + START (extractxors); + LOG ("starting extracting XOR"); + std::vector<Clause *> candidates = {}; + init_xor_gate_extraction (candidates); + for (auto c : candidates) { + if (internal->unsat) + break; + if (c->garbage) + continue; + extract_xor_gates_with_base_clause (c); + } + reset_xor_gate_extraction (); + STOP (extractxors); +} + +/*------------------------------------------------------------------------*/ +void Closure::find_units () { + size_t units = 0; + for (auto v : internal->vars) { + RESTART: + if (!internal->flags (v).active ()) + continue; + for (int sgn = -1; sgn < 1; sgn += 2) { + int lit = v * sgn; + for (auto w : internal->watches (lit)) { + if (!w.binary ()) + continue; // todo check that binaries first + const int other = w.blit; + if (marked (-other)) { + LOG (w.clause, "binary clause %d %d and %d %d give unit %d", lit, + other, lit, -other, lit); + ++units; + if (internal->lrat) { + lrat_chain.push_back (w.clause->id); + lrat_chain.push_back (marked_mu1 (-other).clause->id); + } + bool failed = !learn_congruence_unit (lit); + unmark_all (); + if (failed) + return; + else + goto RESTART; + } + if (marked (other)) + continue; + marked (other) = 1; + set_mu1_reason (other, w.clause); + internal->analyzed.push_back (other); + } + unmark_all (); + } + CADICAL_assert (internal->analyzed.empty ()); + } + LOG ("found %zd units", units); +} + +void Closure::find_equivalences () { + CADICAL_assert (!internal->unsat); + + for (auto v : internal->vars) { + RESTART: + if (!internal->flags (v).active ()) + continue; + int lit = v; + for (auto w : internal->watches (lit)) { + if (!w.binary ()) + break; + CADICAL_assert (w.size == 2); + const int other = w.blit; + if (internal->vlit (lit) > internal->vlit (other)) + continue; + if (marked (other)) + continue; + internal->analyzed.push_back (other); + marked (other) = true; + set_mu1_reason (other, w.clause); + } + + if (internal->analyzed.empty ()) + continue; + + for (auto w : internal->watches (-lit)) { + if (!w.binary ()) + break; // binary clauses are first + const int other = w.blit; + if (internal->vlit (-lit) > internal->vlit (other)) + continue; + CADICAL_assert (-lit != other); + LOG ("binary clause %d %d", -lit, other); + if (marked (-other)) { + int lit_repr = find_representative (lit); + int other_repr = find_representative (other); + LOG ("found equivalence %d %d with %d and %d as the representative", + lit, other, lit_repr, other_repr); + if (lit_repr != other_repr) { + // if (internal->lrat) { + // // This cannot work + // // if you have 2 = 1 and 3=4 + // // you cannot add 2=3. You really to connect the + // representatives directly + // // therefore you actually need to learn the clauses 2->3->4 + // and -2->1 and vice-versa eager_representative_id (other) = + // marked_mu1 (-other).clause->id; eager_representative_id + // (-other) = w.clause->id; CADICAL_assert (eager_representative_id + // (other) != -1); LOG ("lrat: %d (%zd) %d (%zd)", other, + // eager_representative_id (other), -other, + // eager_representative_id (-other)); + // } + promote_clause (marked_mu1 (-other).clause); + promote_clause (w.clause); + LOG (w.clause, "merging"); + LOG (marked_mu1 (-other).clause, "with"); + if (merge_literals_equivalence ( + lit, other, + internal->lrat ? marked_mu1 (-other).clause : nullptr, + w.clause)) { + ++internal->stats.congruence.congruent; + } + unmark_all (); + if (internal->unsat) + return; + else + goto RESTART; + } + } + } + unmark_all (); + } + CADICAL_assert (internal->analyzed.empty ()); + LOG ("found %zd equivalences", schedule.size ()); +} + +/*------------------------------------------------------------------------*/ +// Initialization + +void Closure::rewrite_and_gate (Gate *g, int dst, int src, LRAT_ID id1, + LRAT_ID id2) { + if (skip_and_gate (g)) + return; + if (!gate_contains (g, src)) + return; + if (internal->val (src)) { + // In essence the code below does the same thing as simplify_and_gate + // but the necessary LRAT chain are different. + simplify_and_gate (g); + return; + } + CADICAL_assert (src); + CADICAL_assert (dst); + CADICAL_assert (internal->val (src) == internal->val (dst)); + GatesTable::iterator git = (g->indexed ? table.find (g) : end (table)); + LOG (g, "rewriting %d into %d in", src, dst); + int clashing = 0, falsifies = 0; + unsigned dst_count = 0, not_dst_count = 0; + auto q = begin (g->rhs); + for (int &lit : g->rhs) { + if (lit == src) + lit = dst; + if (lit == -g->lhs) { + LOG ("found negated LHS literal %d", lit); + clashing = lit; + g->degenerated_and_neg = true; + break; + } + if (lit == g->lhs) + g->degenerated_and_pos = true; + const signed char val = internal->val (lit); + if (val > 0) { + continue; + } + if (val < 0) { + LOG ("found falsifying literal %d", (lit)); + falsifies = lit; + break; + } + if (lit == dst) { + if (not_dst_count) { + LOG ("clashing literals %d and %d", (-dst), (dst)); + clashing = -dst; + break; + } + if (dst_count++) + continue; + } + if (lit == -dst) { + if (dst_count) { + CADICAL_assert (!not_dst_count); + LOG ("clashing literals %d and %d", (dst), (-dst)); + clashing = -dst; + break; + } + CADICAL_assert (!not_dst_count); + ++not_dst_count; + } + *q++ = lit; + } + LOG (lrat_chain, "lrat chain after rewriting"); + + if (internal->lrat) { // updating reasons in the chain. +#ifdef LOGGING + for (auto litId : g->pos_lhs_ids) { + LOG (litId.clause, "%d ->", litId.current_lit); + } +#endif + // We remove all assigned literals except the falsified literal such + // that we can produce an LRAT chain + size_t i = 0, size = g->pos_lhs_ids.size (); + bool found = false; + CADICAL_assert (!falsifies || !clashing); + const int orig_falsifies = falsifies == dst ? src : falsifies; + const int orig_clashing = + clashing == -dst ? -src : (clashing == dst ? src : clashing); + int keep_clashing = clashing; + LOG ("keeping chain for falsifies: %d aka %d and clashing: %d aka %d", + falsifies, orig_falsifies, clashing, orig_clashing); + for (size_t j = 0; j < size; ++j) { + LOG (g->pos_lhs_ids[j].clause, "looking at %d [%zd %zd] with val %d", + g->pos_lhs_ids[j].current_lit, i, j, + internal->val (g->pos_lhs_ids[i].current_lit)); + g->pos_lhs_ids[i] = g->pos_lhs_ids[j]; + if (keep_clashing && g->pos_lhs_ids[i].current_lit != orig_clashing && + g->pos_lhs_ids[i].current_lit != -orig_clashing && + g->pos_lhs_ids[i].current_lit != keep_clashing && + g->pos_lhs_ids[i].current_lit != -keep_clashing) + continue; + if (internal->val (g->pos_lhs_ids[i].current_lit) && + g->pos_lhs_ids[i].current_lit != src && + g->pos_lhs_ids[i].current_lit != orig_falsifies) + continue; + if (g->pos_lhs_ids[i].current_lit == dst) { + if (!found) + found = true; + else + continue; // we have already one defining clause + } + + LOG ("maybe keeping %d [%zd %zd], src: %d, found: %d", + g->pos_lhs_ids[i].current_lit, i, j, src, found); + if (g->pos_lhs_ids[i].current_lit == src) { + if (!found) + g->pos_lhs_ids[i].current_lit = dst, found = true; + else + continue; // we have already one defining clause + } + LOG ("keeping %d [%zd %zd]", g->pos_lhs_ids[i].current_lit, i, j); + ++i; + } + LOG ("resizing to %zd", i); + CADICAL_assert (i); + g->pos_lhs_ids.resize (i); + } + + if (q != end (g->rhs)) { + g->rhs.resize (q - begin (g->rhs)); + g->shrunken = true; + } + CADICAL_assert (dst_count <= 2); + CADICAL_assert (not_dst_count <= 1); + + std::vector<LRAT_ID> reasons_lrat_src, reasons_lrat_usrc; + shrink_and_gate (g, falsifies, clashing); + LOG (g, "rewritten as"); + CADICAL_assert (!internal->lrat || !g->pos_lhs_ids.empty ()); + // check_and_gate_implied (g); + update_and_gate (g, git, src, dst, id1, id2, falsifies, clashing); + ++internal->stats.congruence.rewritten_ands; +} + +bool Closure::rewrite_gate (Gate *g, int dst, int src, LRAT_ID id1, + LRAT_ID id2) { + switch (g->tag) { + case Gate_Type::And_Gate: + rewrite_and_gate (g, dst, src, id1, id2); + break; + case Gate_Type::XOr_Gate: + rewrite_xor_gate (g, dst, src); + break; + case Gate_Type::ITE_Gate: + rewrite_ite_gate (g, dst, src); + break; + default: + CADICAL_assert (false); + break; + } + CADICAL_assert (internal->unsat || lrat_chain.empty ()); + return !internal->unsat; +} + +bool Closure::rewrite_gates (int dst, int src, LRAT_ID id1, LRAT_ID id2) { + const auto &occs = goccs (src); + for (auto g : occs) { + CADICAL_assert (lrat_chain.empty ()); + if (!rewrite_gate (g, dst, src, id1, id2)) + return false; + else if (!g->garbage && gate_contains (g, dst)) + goccs (dst).push_back (g); + } + goccs (src).clear (); + +#ifndef CADICAL_NDEBUG + for (const auto &occs : gtab) { + for (auto g : occs) { + CADICAL_assert (g); + CADICAL_assert (g->garbage || !gate_contains (g, src)); + } + } +#endif + CADICAL_assert (lrat_chain.empty ()); + return true; +} + +bool Closure::rewriting_lhs (Gate *g, int dst) { + if (dst != g->lhs && dst != -g->lhs) + return false; + mark_garbage (g); + return true; +} + +// update to produce proofs +void Closure::rewrite_xor_gate (Gate *g, int dst, int src) { + if (skip_xor_gate (g)) + return; + if (rewriting_lhs (g, dst)) + return; + if (!gate_contains (g, src)) + return; + LOG (g, "rewriting (%d -> %d)", src, dst); + check_xor_gate_implied (g); + GatesTable::iterator git = (g->indexed ? table.find (g) : end (table)); + size_t j = 0, dst_count = 0; + bool original_dst_negated = (dst < 0); + dst = abs (dst); + unsigned negate = original_dst_negated; + const size_t size = g->rhs.size (); + for (size_t i = 0; i < size; ++i) { + int lit = g->rhs[i]; + CADICAL_assert (lit > 0); + if (lit == src) + lit = dst; + const signed char v = internal->val (lit); + if (v > 0) { + negate ^= true; + } + if (v) + continue; + if (lit == dst) + dst_count++; + LOG ("keeping value %d", lit); + g->rhs[j++] = lit; + } + if (negate) { + LOG ("flipping LHS %d", g->lhs); + g->lhs = -g->lhs; + } + CADICAL_assert (dst_count <= 2); + if (dst_count == 2) { + LOG ("destination found twice, removing"); + size_t k = 0; + for (size_t i = 0; i < j; ++i) { + const int lit = g->rhs[i]; + if (lit != dst) + g->rhs[k++] = g->rhs[i]; + } + CADICAL_assert (k == j - 2); + g->rhs.resize (k); + g->shrunken = true; + CADICAL_assert (is_sorted (begin (g->rhs), end (g->rhs), + sort_literals_by_var_smaller (internal))); + g->hash = hash_lits (nonces, g->rhs); + } else if (j != size) { + g->shrunken = true; + g->rhs.resize (j); + sort_literals_by_var (g->rhs); + g->hash = hash_lits ( + nonces, + g->rhs); // all but one (the dst) is sorted correctly actually + } else { + CADICAL_assert (j == size); + sort_literals_by_var (g->rhs); + } + + CADICAL_assert (clause.empty ()); + // LRAT for add_xor_shrinking_proof_chain + // this should be unnecessary... + // TODO check if really unnecessary + if (dst_count > 1) + add_xor_shrinking_proof_chain (g, dst); + CADICAL_assert (internal->clause.size () <= 1); + update_xor_gate (g, git); + + if (!g->garbage && !internal->unsat && original_dst_negated && + dst_count == 1) { + connect_goccs (g, dst); + } + + check_xor_gate_implied (g); + // TODO stats +} + +// update to produce proofs +void Closure::simplify_xor_gate (Gate *g) { + LOG (g, "simplifying"); + if (skip_xor_gate (g)) + return; + check_xor_gate_implied (g); + unsigned negate = 0; + GatesTable::iterator git = (g->indexed ? table.find (g) : end (table)); + const size_t size = g->rhs.size (); + size_t j = 0; + for (size_t i = 0; i < size; ++i) { + int lit = g->rhs[i]; + CADICAL_assert (lit > 0); + const signed char v = internal->val (lit); + if (v > 0) + negate ^= 1; + if (!v) { + g->rhs[j++] = lit; + } + } + if (negate) { + LOG ("flipping LHS literal %d", (g->lhs)); + g->lhs = -(g->lhs); + } + if (j != size) { + LOG ("shrunken gate"); + g->shrunken = true; + g->rhs.resize (j); + CADICAL_assert (is_sorted (begin (g->rhs), end (g->rhs), + sort_literals_by_var_smaller (internal))); + g->hash = hash_lits (nonces, g->rhs); + } else { + CADICAL_assert (g->hash == hash_lits (nonces, g->rhs)); + } + + check_xor_gate_implied (g); + CADICAL_assert (clause.empty ()); + update_xor_gate (g, git); + LOG (g, "simplified"); + check_xor_gate_implied (g); + internal->stats.congruence.simplified++; + internal->stats.congruence.simplified_xors++; +} + +/*------------------------------------------------------------------------*/ +// propagation of clauses and simplification +void Closure::schedule_literal (int lit) { + const int idx = abs (lit); + if (scheduled[idx]) + return; + scheduled[idx] = true; + schedule.push (lit); + CADICAL_assert (lit != find_representative (lit)); + LOG ("scheduled literal %d", lit); +} + +bool Closure::propagate_unit (int lit) { + LOG ("propagation of congruence unit %d", lit); + if (internal->lrat) + lazy_propagated (lit) = true; + return simplify_gates (lit) && simplify_gates (-lit); +} + +bool Closure::propagate_units () { + while (units != + internal->trail + .size ()) { // units are added during propagation, so reloading + LOG ("propagating %d over gates", internal->trail[units]); + if (!propagate_unit (internal->trail[units++])) + return false; + } + return true; +} + +// The replacement has to be done eagerly, not lazily to make sure that the +// gates are in normalized form. Otherwise, some merges might be missed. +bool Closure::propagate_equivalence (int lit) { + if (internal->val (lit)) + return true; + LOG ("propagating literal %d", lit); + import_lazy_and_find_eager_representative_and_compress_both (lit); + const int repr = find_eager_representative_and_compress (lit); + const LRAT_ID id1 = find_eager_representative_lrat (lit); + const LRAT_ID id2 = find_eager_representative_lrat (-lit); + CADICAL_assert (lrat_chain.empty ()); + return rewrite_gates (repr, lit, id1, id2) && + rewrite_gates (-repr, -lit, id2, id1); +} + +size_t Closure::propagate_units_and_equivalences () { + START (congruencemerge); + size_t propagated = 0; + LOG ("propagating at least %zd units", schedule.size ()); + CADICAL_assert (lrat_chain.empty ()); + while (propagate_units () && !schedule.empty ()) { + CADICAL_assert (!internal->unsat); + CADICAL_assert (lrat_chain.empty ()); + ++propagated; + int lit = schedule.front (); + schedule.pop (); + scheduled[abs (lit)] = false; + if (!propagate_equivalence (lit)) + break; + } + + CADICAL_assert (internal->unsat || schedule.empty ()); + CADICAL_assert (internal->unsat || lrat_chain.empty ()); + + LOG ("propagated %zu congruence units", units); + LOG ("propagated %zu congruence equivalences", propagated); + +#ifndef CADICAL_NDEBUG + if (!internal->unsat) { + for (const auto &occs : gtab) { + for (auto g : occs) { + if (g->garbage) + continue; + CADICAL_assert (g->tag == Gate_Type::ITE_Gate || + g->tag == Gate_Type::XOr_Gate || + !gate_contains (g, -g->lhs)); + // TODO: this would be nice to have! + // CADICAL_assert (g->tag != Gate_Type::ITE_Gate || (g->rhs.size() == 3 + // && g->rhs[1] != -g->lhs && g->rhs[2] != -g->lhs)); + // CADICAL_assert (table.count(g) == 1); + for (auto lit : g->rhs) { + CADICAL_assert (!internal->val (lit)); + CADICAL_assert (representative (lit) == lit); + } + } + } + for (Gate *g : table) { + if (g->garbage) + continue; + if (g->tag == Gate_Type::And_Gate) { + // CADICAL_assert (find_and_lits(g->arity, g->rhs)); + } + } + } +#endif + STOP (congruencemerge); + return propagated; +} + +std::string string_of_gate (Gate_Type t) { + switch (t) { + case Gate_Type::And_Gate: + return "And"; + case Gate_Type::XOr_Gate: + return "XOr"; + case Gate_Type::ITE_Gate: + return "ITE"; + default: + return "buggy"; + } +} + +void Closure::reset_closure () { + scheduled.clear (); + for (Gate *g : table) { + CADICAL_assert (g->indexed); + LOG (g, "deleting"); + if (!g->garbage) + delete g; + } + table.clear (); + + for (auto &occ : gtab) { + occ.clear (); + } + gtab.clear (); + + for (auto gate : garbage) + delete gate; + garbage.clear (); + + if (internal->lrat) { + CADICAL_assert (internal->proof); + for (auto c : extra_clauses) { + CADICAL_assert (!c->garbage); + internal->proof->delete_clause (c); + delete c; + } + extra_clauses.clear (); + } else { + CADICAL_assert (extra_clauses.empty ()); + } +} + +void Closure::reset_extraction () { + full_watching = true; + if (!internal->unsat && !internal->propagate ()) { + internal->learn_empty_clause (); + } + +#if 0 + // remove delete watched clauses from the watch list + for (auto v : internal->vars) { + for (auto sgn = -1; sgn <= 1; sgn += 2) { + const int lit = v * sgn; + auto &watchers = internal->watches (lit); + const size_t size = watchers.size (); + size_t j = 0; + for (size_t i = 0; i != size; ++i) { + const auto w = watchers[i]; + watchers[j] = watchers[i]; + if (!w.clause->garbage) + ++j; + } + watchers.resize(j); + } + } + // watch the remaining non-watched clauses + for (auto c : new_unwatched_binary_clauses) + internal->watch_clause (c); + new_unwatched_binary_clauses.clear(); + for (auto c : internal->clauses) { + if (c->garbage) + continue; + if (c->size != 2) + internal->watch_clause (c); + } +#else // simpler implementation + new_unwatched_binary_clauses.clear (); + internal->clear_watches (); + internal->connect_watches (); +#endif +} + +void Closure::forward_subsume_matching_clauses () { + START (congruencematching); + reset_closure (); + std::vector<signed char> matchable; + matchable.resize (internal->max_var + 1); + size_t count_matchable = 0; + + for (auto idx : internal->vars) { + if (!internal->flags (idx).active ()) + continue; + const int lit = idx; + const int repr = find_representative (lit); + if (lit == repr) + continue; + const int repr_idx = abs (repr); + if (!matchable[idx]) { + LOG ("matchable %d", idx); + matchable[idx] = true; + count_matchable++; + } + + if (!matchable[repr_idx]) { + LOG ("matchable %d", repr_idx); + matchable[repr_idx] = true; + count_matchable++; + } + } + + LOG ("found %.0f%%", + (double) count_matchable / + (double) (internal->max_var ? internal->max_var : 1)); + std::vector<Clause *> candidates; + auto &analyzed = internal->analyzed; + + for (auto *c : internal->clauses) { + if (c->garbage) + continue; + if (c->redundant) + continue; + if (c->size == 2) + continue; + CADICAL_assert (analyzed.empty ()); + bool contains_matchable = false; + for (auto lit : *c) { + const signed char v = internal->val (lit); + if (v < 0) + continue; + if (v > 0) { + LOG (c, "mark satisfied"); + internal->mark_garbage (c); + break; + } + if (!contains_matchable) { + const int idx = abs (lit); + if (matchable[idx]) + contains_matchable = true; + } + + const int repr = find_representative (lit); + CADICAL_assert (!internal->val (repr)); + if (marked (repr)) + continue; + const int not_repr = -repr; + if (marked (not_repr)) { + LOG (c, "matches both %d and %d", (lit), (not_repr)); + internal->mark_garbage (c); + break; + } + marked (repr) = 1; + analyzed.push_back (repr); + } + + for (auto lit : analyzed) + marked (lit) = 0; + analyzed.clear (); + if (c->garbage) + continue; + if (!contains_matchable) { + LOG ("no matching variable"); + continue; + } + LOG (c, "candidate"); + candidates.push_back (c); + } + + rsort (begin (candidates), end (candidates), smaller_clause_size_rank ()); + size_t tried = 0, subsumed = 0; + internal->init_occs (); + for (auto c : candidates) { + CADICAL_assert (c->size != 2); + // TODO if terminated + ++tried; + if (find_subsuming_clause (c)) { + ++subsumed; + } + } + LOG ("[congruence] subsumed %.0f%%", + (double) subsumed / (double) (tried ? tried : 1)); + STOP (congruencematching); +} + +/*------------------------------------------------------------------------*/ +// Candidate clause 'subsumed' is subsumed by 'subsuming'. We need to copy +// the function because 'congruence' is too early to include the version +// from subsume + +void Closure::subsume_clause (Clause *subsuming, Clause *subsumed) { + // CADICAL_assert (!subsuming->redundant); + // CADICAL_assert (!subsumed->redundant); + auto &stats = internal->stats; + stats.subsumed++; + CADICAL_assert (subsuming->size <= subsumed->size); + LOG (subsumed, "subsumed"); + if (subsumed->redundant) + stats.subred++; + else + stats.subirr++; + if (subsumed->redundant || !subsuming->redundant) { + internal->mark_garbage (subsumed); + return; + } + LOG ("turning redundant subsuming clause into irredundant clause"); + subsuming->redundant = false; + if (internal->proof) + internal->proof->strengthen (subsuming->id); + internal->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'. +} + +bool Closure::find_subsuming_clause (Clause *subsumed) { + CADICAL_assert (!subsumed->garbage); + Clause *subsuming = nullptr; + for (auto lit : *subsumed) { + CADICAL_assert (internal->val (lit) <= 0); + const int repr_lit = find_representative (lit); + const signed char repr_val = internal->val (repr_lit); + CADICAL_assert (repr_val <= 0); + if (repr_val < 0) + continue; + if (marked (repr_lit)) + continue; + CADICAL_assert (!marked (-repr_lit)); + marked (repr_lit) = 1; + } + int least_occuring_lit = 0; + size_t count_least_occurring = INT_MAX; + LOG (subsumed, "trying to forward subsume"); + + for (auto lit : *subsumed) { + const int repr_lit = find_representative (lit); + const size_t count = internal->occs (lit).size (); + CADICAL_assert (count <= UINT_MAX); + if (count < count_least_occurring) { + count_least_occurring = count; + least_occuring_lit = repr_lit; + } + for (auto d : internal->occs (lit)) { + CADICAL_assert (!d->garbage); + CADICAL_assert (subsumed != d); + if (!subsumed->redundant && d->redundant) + continue; + for (auto other : *d) { + const signed char v = internal->val (other); + if (v < 0) + continue; + CADICAL_assert (!v); + const int repr_other = find_representative (other); + if (!marked (repr_other)) + goto CONTINUE_WITH_NEXT_CLAUSE; + LOG ("subsuming due to %d -> %d", other, repr_other); + } + subsuming = d; + goto FOUND_SUBSUMING; + + CONTINUE_WITH_NEXT_CLAUSE:; + } + } + CADICAL_assert (least_occuring_lit); + +FOUND_SUBSUMING: + for (auto lit : *subsumed) { + const int repr_lit = find_representative (lit); + const signed char v = internal->val (lit); + if (!v) + marked (repr_lit) = 0; + } + if (subsuming) { + LOG (subsumed, "subsumed"); + LOG (subsuming, "subsuming"); + subsume_clause (subsuming, subsumed); + ++internal->stats.congruence.subsumed; + return true; + } else { + internal->occs (least_occuring_lit).push_back (subsumed); + return false; + } +} + +/*------------------------------------------------------------------------*/ +static bool skip_ite_gate (Gate *g) { + CADICAL_assert (g->tag == Gate_Type::ITE_Gate); + if (g->garbage) + return true; + return false; +} + +void Closure::produce_ite_merge_then_else_reasons ( + Gate *g, int src, int dst, std::vector<LRAT_ID> &reasons_implication, + std::vector<LRAT_ID> &reasons_back) { + CADICAL_assert (!g->garbage); + if (!internal->lrat) + return; + check_correct_ite_flags (g); + // no merge is happening actually + CADICAL_assert (g->rhs[1] == find_eager_representative(g->rhs[1]) || g->rhs[2] == find_eager_representative(g->rhs[2])); + if (find_eager_representative (g->lhs) == g->rhs[1] || find_eager_representative (g->lhs) == g->rhs[2]) + return; + if ((g->rhs[1] == src && g->lhs == dst && g->rhs[2] == g->lhs) || + (g->rhs[2] == src && g->lhs == dst && g->rhs[1] == g->lhs) || + (g->rhs[1] == -src && g->lhs == -dst && g->rhs[2] == g->lhs) || + (g->rhs[2] == -src && g->lhs == -dst && g->rhs[1] == g->lhs)) + return; + check_ite_lrat_reasons (g, false); + CADICAL_assert (g->rhs.size () == 3); + CADICAL_assert (src == g->rhs[1] || src == g->rhs[2]); + CADICAL_assert (dst == g->rhs[1] || dst == g->rhs[2]); + const int8_t flag = g->degenerated_ite; + CADICAL_assert (!ite_flags_no_then_clauses (flag)); // e = lhs: already merged + CADICAL_assert (!ite_flags_no_else_clauses (flag)); // t = lhs: already merged + produce_rewritten_clause_lrat (g->pos_lhs_ids, g->lhs, false); + if (ite_flags_neg_cond_lhs (flag)) { + LOG ("degenerated case with lhs = -cond"); + LOG (g->pos_lhs_ids[0].clause, "1:"); + LOG (g->pos_lhs_ids[1].clause, "2:"); + reasons_back.push_back (g->pos_lhs_ids[0].clause->id); + reasons_implication.push_back (g->pos_lhs_ids[1].clause->id); + return; + } + if (ite_flags_cond_lhs (flag)) { + LOG ("degenerated case with lhs = cond"); + CADICAL_assert (g->pos_lhs_ids[0].clause); + CADICAL_assert (g->pos_lhs_ids[3].clause); + reasons_back.push_back (g->pos_lhs_ids[3].clause->id); + reasons_implication.push_back (g->pos_lhs_ids[0].clause->id); + return; + } + reasons_implication.push_back (g->pos_lhs_ids[0].clause->id); + reasons_implication.push_back (g->pos_lhs_ids[2].clause->id); + reasons_back.push_back (g->pos_lhs_ids[1].clause->id); + reasons_back.push_back (g->pos_lhs_ids[3].clause->id); +} + +void Closure::rewrite_ite_gate_update_lrat_reasons (Gate *g, int src, + int dst) { + if (!internal->lrat) + return; + LOG (g, "updating lrat from"); + for (auto &litId : g->pos_lhs_ids) { + CADICAL_assert (litId.clause); + if (litId.current_lit == src) + litId.current_lit = dst; + if (litId.current_lit == -src) + litId.current_lit = -dst; + } + check_ite_lrat_reasons (g, false); +} + +bool Closure::rewrite_ite_gate_to_and ( + Gate *g, int src, int dst, size_t idx1, size_t idx2, + int cond_lit_to_learn_if_degenerated) { + CADICAL_assert (internal->lrat_chain.empty ()); + CADICAL_assert (!g->garbage); + LOG (g, "rewriting to proper AND"); + if (internal->val (g->lhs) > 0) { + { + const int lit = g->rhs[0]; + const char v = internal->val (lit); + if (v > 0) { + } else if (!v) { + if (internal->lrat) { + push_id_and_rewriting_lrat_unit (g->pos_lhs_ids[idx1].clause, + Rewrite (), lrat_chain); + } + learn_congruence_unit (cond_lit_to_learn_if_degenerated); + } else { + if (internal->lrat) + push_id_and_rewriting_lrat_unit (g->pos_lhs_ids[idx1].clause, + Rewrite (), lrat_chain); + push_lrat_unit (-lit); + internal->learn_empty_clause (); + return true; + } + } + if (!internal->unsat) { + const int lit = g->rhs[1]; + const char v = internal->val (lit); + CADICAL_assert (dst == g->rhs[0] || dst == g->rhs[1] || -dst == g->rhs[0] || + -dst == g->rhs[1]); + const int other = (dst == g->rhs[0] || dst == g->rhs[1]) + ? dst ^ g->rhs[0] ^ g->rhs[1] + : (-dst) ^ g->rhs[0] ^ g->rhs[1]; + if (v > 0) { + // already set by propagation + return true; + } else if (!v) { + if (internal->lrat) { + push_id_and_rewriting_lrat_unit (g->pos_lhs_ids[idx2].clause, + Rewrite (), lrat_chain); + } + learn_congruence_unit (other); + } else { + if (internal->lrat) { + push_lrat_unit (cond_lit_to_learn_if_degenerated); + push_id_and_rewriting_lrat_unit (g->pos_lhs_ids[idx2].clause, + Rewrite (), lrat_chain); + } + internal->learn_empty_clause (); + return true; + } + } + return true; + } + if (!internal->lrat) + return false; + LOG ("updating flags"); + g->degenerated_and_neg = (g->rhs[1] == -g->lhs || g->rhs[0] == -g->lhs); + g->degenerated_and_pos = (g->rhs[0] == g->lhs || g->rhs[1] == g->lhs); + CADICAL_assert (g->rhs.size () == 3); + CADICAL_assert (g->pos_lhs_ids.size () == 4); + CADICAL_assert (idx1 < g->pos_lhs_ids.size ()); + CADICAL_assert (idx2 < g->pos_lhs_ids.size ()); + int lit = g->pos_lhs_ids[idx2].current_lit, other = g->lhs; + // TODO: remove argument + (void) src; + produce_rewritten_clause_lrat_and_clean (g->pos_lhs_ids, g->lhs, idx1, + idx2, false); + + if ((idx1 == (size_t) -1 || idx2 == (size_t) -1)) { + // degenerated and gate + return false; + } + + CADICAL_assert (idx1 != (size_t) -1); + CADICAL_assert (idx2 != (size_t) -1); + CADICAL_assert (idx1 < g->pos_lhs_ids.size ()); + CADICAL_assert (idx2 < g->pos_lhs_ids.size ()); + Clause *c = g->pos_lhs_ids[idx1].clause; + CADICAL_assert (c->size == 2); + Clause *d = g->pos_lhs_ids[idx2].clause; + CADICAL_assert (c != d); + CADICAL_assert (c); + CADICAL_assert (d); + g->pos_lhs_ids.erase (std::remove_if (begin (g->pos_lhs_ids), + end (g->pos_lhs_ids), + [d] (const LitClausePair &p) { + return p.clause == d || !p.clause; + }), + end (g->pos_lhs_ids)); + CADICAL_assert (g->pos_lhs_ids.size () == 2); + CADICAL_assert (lit); + CADICAL_assert (other); + CADICAL_assert (lit != dst); + CADICAL_assert (other != dst); + CADICAL_assert (lit != other); + lrat_chain.push_back (c->id); + lrat_chain.push_back (d->id); + Clause *e = learn_binary_tmp_or_full_clause (lit, -other); + CADICAL_assert (e); + + auto long_clause = + std::find_if (begin (g->pos_lhs_ids), end (g->pos_lhs_ids), + [] (LitClausePair l) { return l.clause->size == 3; }); + CADICAL_assert (long_clause != end (g->pos_lhs_ids)); + LOG (long_clause->clause, "new long clause"); + g->neg_lhs_ids.push_back (*long_clause); + g->pos_lhs_ids.erase (long_clause); + + CADICAL_assert (g->pos_lhs_ids.size () == 1); + + (void) maybe_promote_tmp_binary_clause (g->pos_lhs_ids[0].clause); + g->pos_lhs_ids.push_back ({lit, e}); +#ifndef CADICAL_NDEBUG + for (auto litId : g->pos_lhs_ids) { + bool found = false; + CADICAL_assert (litId.clause); + for (auto other : *litId.clause) { + found = (find_eager_representative (other) == litId.current_lit); + if (found) + break; + } + CADICAL_assert (found); + } + for (auto id : g->pos_lhs_ids) { + LOG (id.clause, "clause after rewriting:"); + CADICAL_assert (id.clause->size == 2); + } + +#endif + return false; +} + +void Closure::produce_ite_merge_lhs_then_else_reasons ( + Gate *g, std::vector<LRAT_ID> &reasons_implication, + std::vector<LRAT_ID> &reasons_back, std::vector<LRAT_ID> &reasons_unit, + bool rewritting_then, bool &learn_units) { + + const size_t idx1 = rewritting_then ? 0 : 2; + const size_t idx2 = idx1 + 1; + const size_t other_idx1 = rewritting_then ? 2 : 0; + const size_t other_idx2 = other_idx1 + 1; + const int cond_lit = g->rhs[0]; + const int lit_to_merge = g->rhs[rewritting_then ? 2 : 1]; + const int other_lit = g->rhs[rewritting_then ? 1 : 2]; + const int repr_cond_lit = find_eager_representative (g->rhs[0]); + const int repr_lit_to_merge = find_eager_representative (lit_to_merge); + const int repr_other_lit = find_eager_representative (other_lit); + const int repr_lhs = find_eager_representative(g->lhs); + if (!internal->proof) + return; + + + LOG ("cond: %d, merging %d and rewriting to %d", cond_lit, lit_to_merge, + other_lit); + if (internal->lrat) { + CADICAL_assert (internal->lrat); + CADICAL_assert (g->pos_lhs_ids.size () == 4); + + if (repr_lhs == -repr_other_lit) { + LOG ("special case: %s=%s, checking if other: %s %s", LOGLIT (g->lhs), + LOGLIT (-lit_to_merge), LOGLIT (cond_lit), LOGLIT (other_lit)); + CADICAL_assert (repr_lit_to_merge != -repr_lhs); // should have been rewritten before + + if (rewritting_then && repr_cond_lit == repr_lhs) { + LOG ("t=-lhs/c=lhs"); + learn_units = true; + // is a unit + push_id_and_rewriting_lrat_unit (g->pos_lhs_ids[0].clause, + Rewrite (), lrat_chain); + unsimplified.push_back (-cond_lit); + LRAT_ID id_unit = simplify_and_add_to_proof_chain (unsimplified); + reasons_unit = {id_unit}; + // don't bother finding out which one is used + reasons_implication.push_back (id_unit); + g->pos_lhs_ids[3].clause = produce_rewritten_clause_lrat ( + g->pos_lhs_ids[3].clause, g->lhs, false); + CADICAL_assert (g->pos_lhs_ids[3].clause); + reasons_implication.push_back (g->pos_lhs_ids[3].clause->id); + unsimplified.clear (); + return; + } + if (!rewritting_then && repr_cond_lit == repr_lhs) { + LOG ("e=-lhs/c=lhs"); + learn_units = true; + // is a unit + push_id_and_rewriting_lrat_unit (g->pos_lhs_ids[3].clause, + Rewrite (), lrat_chain); + unsimplified.push_back (cond_lit); + LRAT_ID id_unit = simplify_and_add_to_proof_chain (unsimplified); + reasons_unit = {id_unit}; + // don't bother finding out which one is used + reasons_implication.push_back (id_unit); + g->pos_lhs_ids[0].clause = produce_rewritten_clause_lrat ( + g->pos_lhs_ids[0].clause, g->lhs, false); + CADICAL_assert (g->pos_lhs_ids[0].clause); + reasons_implication.push_back (g->pos_lhs_ids[0].clause->id); + unsimplified.clear (); + return; + } + if (!rewritting_then && repr_cond_lit == -repr_lhs) { + LOG ("e=-lhs/c=-lhs"); + learn_units = true; + // TODO: this function does not work to produce units for this case + // c LOG 0 rewriting 4 by 3 in gate[42] (arity: 3) -3 := ITE 3 7 + // ... + // c LOG 0 clause[50] 4 -3 + // c LOG 0 clause[44] 5 3 + // c LOG 0 clause[2] -3 -4 -5 + // the first two are rewriting, but they are not ordered properly + // and we need the '5' clause to come after + push_id_and_rewriting_lrat_unit (g->pos_lhs_ids[2].clause, + Rewrite (), lrat_chain); + unsimplified.push_back (cond_lit); + LRAT_ID id_unit = simplify_and_add_to_proof_chain (unsimplified); + reasons_unit = {id_unit}; + g->pos_lhs_ids[1].clause = produce_rewritten_clause_lrat ( + g->pos_lhs_ids[1].clause, g->lhs, false); + CADICAL_assert (g->pos_lhs_ids[1].clause); + + // don't bother finding out which one is used + reasons_implication.push_back (id_unit); + reasons_implication.push_back (g->pos_lhs_ids[1].clause->id); + unsimplified.clear (); + return; + } + if (rewritting_then && repr_cond_lit == -repr_lhs) { + LOG ("t=-lhs/c=-lhs"); + learn_units = true; + push_id_and_rewriting_lrat_unit (g->pos_lhs_ids[1].clause, + Rewrite (), lrat_chain); + unsimplified.push_back (-cond_lit); + LRAT_ID id_unit = simplify_and_add_to_proof_chain (unsimplified); + reasons_unit = {id_unit}; + g->pos_lhs_ids[2].clause = produce_rewritten_clause_lrat ( + g->pos_lhs_ids[2].clause, g->lhs, false); + CADICAL_assert (g->pos_lhs_ids[2].clause); + + reasons_implication.push_back (id_unit); + reasons_implication.push_back (g->pos_lhs_ids[2].clause->id); + unsimplified.clear (); + return; + } + if (rewritting_then && repr_lit_to_merge == repr_lhs) { + LOG ("t=-lhs/e=lhs from rewriting then"); + learn_units = true; + g->pos_lhs_ids[idx1].clause = produce_rewritten_clause_lrat ( + g->pos_lhs_ids[idx1].clause, g->lhs, false); + g->pos_lhs_ids[idx2].clause = produce_rewritten_clause_lrat ( + g->pos_lhs_ids[idx2].clause, g->lhs, false); + CADICAL_assert (g->pos_lhs_ids[idx1].clause); + CADICAL_assert (g->pos_lhs_ids[idx2].clause); + lrat_chain.push_back (g->pos_lhs_ids[idx1].clause->id); + lrat_chain.push_back (g->pos_lhs_ids[idx2].clause->id); + unsimplified.push_back (-cond_lit); + LRAT_ID id_unit = simplify_and_add_to_proof_chain (unsimplified); + reasons_unit = {id_unit}; + unsimplified.clear (); + + return; + } + if (!rewritting_then && repr_lit_to_merge == repr_lhs) { + LOG ("t=-lhs/e=lhs from rewriting else"); + learn_units = true; + g->pos_lhs_ids[idx1].clause = produce_rewritten_clause_lrat ( + g->pos_lhs_ids[idx1].clause, g->lhs, false); + g->pos_lhs_ids[idx2].clause = produce_rewritten_clause_lrat ( + g->pos_lhs_ids[idx2].clause, g->lhs, false); + CADICAL_assert (g->pos_lhs_ids[idx1].clause); + CADICAL_assert (g->pos_lhs_ids[idx2].clause); + lrat_chain.push_back (g->pos_lhs_ids[idx1].clause->id); + lrat_chain.push_back (g->pos_lhs_ids[idx2].clause->id); + unsimplified.push_back (cond_lit); + LRAT_ID id_unit = simplify_and_add_to_proof_chain (unsimplified); + reasons_unit = {id_unit}; + unsimplified.clear (); + return; + } + + if (other_lit == repr_lhs) { + LOG ("TODO FIX ME t=-lhs/e=lhs"); + learn_units = true; + // in the other direction we are merging a literal with itself + g->pos_lhs_ids[idx1].clause = produce_rewritten_clause_lrat ( + g->pos_lhs_ids[idx1].clause, g->lhs, false); + g->pos_lhs_ids[idx2].clause = produce_rewritten_clause_lrat ( + g->pos_lhs_ids[idx2].clause, g->lhs, false); + CADICAL_assert (g->pos_lhs_ids[idx1].clause); + CADICAL_assert (g->pos_lhs_ids[idx2].clause); + reasons_unit.push_back (g->pos_lhs_ids[idx1].clause->id); + reasons_unit.push_back (g->pos_lhs_ids[idx2].clause->id); + return; + } + // if (other_lit == -g->lhs) { + // CADICAL_assert (false); + // } + // if (other_lit == g->lhs) { + // CADICAL_assert (false); + // } + } + + LOG ("normal path"); + produce_rewritten_clause_lrat (g->pos_lhs_ids, g->lhs, false); + CADICAL_assert (g->pos_lhs_ids.size () == 4); + + reasons_unit.push_back (g->pos_lhs_ids[idx1].clause->id); + reasons_unit.push_back (g->pos_lhs_ids[idx2].clause->id); + + // already merged: only unit is important + if (!rewritting_then && repr_lhs == repr_lit_to_merge) { + return; + } + + reasons_implication.push_back (g->pos_lhs_ids[other_idx1].clause->id); + reasons_implication.push_back (g->pos_lhs_ids[idx1].clause->id); + reasons_implication.push_back (g->pos_lhs_ids[idx2].clause->id); + + reasons_back.push_back (g->pos_lhs_ids[other_idx2].clause->id); + reasons_back.push_back (g->pos_lhs_ids[idx1].clause->id); + reasons_back.push_back (g->pos_lhs_ids[idx2].clause->id); + } else { + LOG ("learn extra clauses XXXXXXXXXXXXXXXXXXXXXXXXX"); + const int lhs = g->lhs; + const int cond = g->rhs[0]; + if (rewritting_then) { + unsimplified.push_back (-cond); + unsimplified.push_back (lhs); + simplify_and_add_to_proof_chain (unsimplified); + unsimplified.push_back (-cond); + unsimplified.push_back (-lhs); + simplify_and_add_to_proof_chain (unsimplified); + } else { + unsimplified.push_back (cond); + unsimplified.push_back (-lhs); + simplify_and_add_to_proof_chain (unsimplified); + unsimplified.push_back (cond); + unsimplified.push_back (lhs); + simplify_and_add_to_proof_chain (unsimplified); + } + unsimplified.clear (); + } +} + +void Closure::rewrite_ite_gate (Gate *g, int dst, int src) { + CADICAL_assert (unsimplified.empty ()); + if (skip_ite_gate (g)) + return; + if (!gate_contains (g, src)) + return; + LOG (g, "rewriting %d by %d in", src, dst); + CADICAL_assert (!g->shrunken); + CADICAL_assert (g->rhs.size () == 3); + CADICAL_assert (!internal->lrat || g->pos_lhs_ids.size () == 4); + auto &rhs = g->rhs; + const int lhs = g->lhs; + const int cond = g->rhs[0]; + const int then_lit = g->rhs[1]; + const int else_lit = g->rhs[2]; + const int not_lhs = -(lhs); + const int not_dst = -(dst); + const int not_cond = -(cond); + const int not_then_lit = -(then_lit); + const int not_else_lit = -(else_lit); + Gate_Type new_tag = Gate_Type::And_Gate; + + bool garbage = false; + bool shrink = true; + const auto git = g->indexed ? table.find (g) : end (table); + CADICAL_assert (!g->indexed || git != end (table)); + CADICAL_assert (*git == g); + if (internal->val (cond) && internal->val (then_lit) && + internal->val (else_lit)) { // propagation has set all value anyway + LOG (g, "all values are set"); + CADICAL_assert (internal->val (g->lhs)); + garbage = true; + } else if (internal->val (g->lhs) && internal->val (cond)) { + LOG (g, "all values are set 2"); + CADICAL_assert (internal->val (g->lhs)); + garbage = true; + } + // this code is taken one-to-one from kissat + else if (src == cond) { + if (dst == then_lit) { + // then_lit ? then_lit : else_lit + // then_lit & then_lit | !then_lit & else_lit + // then_lit | !then_lit & else_lit + // then_lit | else_lit + // !(!then_lit & !else_lit) + g->lhs = not_lhs; + rhs[0] = not_then_lit; + rhs[1] = not_else_lit; + if (then_lit == lhs || else_lit == lhs) + garbage = true; + else + garbage = rewrite_ite_gate_to_and (g, src, dst, 1, 3, -dst); + } else if (not_dst == then_lit) { + // !then_lit ? then_lit : else_lit + // !then_lit & then_lit | then_lit & else_lit + // then_lit & else_lit + rhs[0] = else_lit; + CADICAL_assert (rhs[1] == then_lit); + garbage = rewrite_ite_gate_to_and (g, src, dst, 0, 2, -dst); + } else if (dst == else_lit) { + // else_list ? then_lit : else_lit + // else_list & then_lit | !else_list & else_lit + // else_list & then_lit + rhs[0] = else_lit; + CADICAL_assert (rhs[1] == then_lit); + garbage = rewrite_ite_gate_to_and (g, src, dst, 2, 0, dst); + } else if (not_dst == else_lit) { + // !else_list ? then_lit : else_lit + // !else_list & then_lit | else_lit & else_lit + // !else_list & then_lit | else_lit + // then_lit | else_lit + // !(!then_lit & !else_lit) + g->lhs = not_lhs; + rhs[0] = not_then_lit; + rhs[1] = not_else_lit; + if (then_lit == lhs || else_lit == lhs) + garbage = true; + else + garbage = rewrite_ite_gate_to_and (g, src, dst, 3, 1, dst); + } else { + shrink = false; + rhs[0] = dst; + rewrite_ite_gate_update_lrat_reasons (g, src, dst); + } + } else if (src == then_lit) { + if (not_dst == g->lhs) { // TODO not in kissat + rhs[1] = dst; + check_ite_implied (g->lhs, cond, then_lit, else_lit); + std::vector<LRAT_ID> reasons_implication, reasons_back, reasons_unit; + LOG ("%d = %d ?", g->lhs, -g->rhs[0]); + bool learn_units_instead_of_equivalence = false; + produce_ite_merge_lhs_then_else_reasons ( + g, reasons_implication, reasons_back, reasons_unit, true, + learn_units_instead_of_equivalence); + if (learn_units_instead_of_equivalence) { // it is too hard to produce + // LRAT chains + // in this case + + if (internal->lrat) + lrat_chain = reasons_unit; + learn_congruence_unit (-cond, true); + if (-else_lit == lhs) { + if (internal->lrat) + lrat_chain = reasons_implication; + learn_congruence_unit (cond == -lhs ? -else_lit : else_lit, false, true); + } else fully_propagate (); + } else { + if (merge_literals_lrat (g->lhs, else_lit, reasons_implication, + reasons_back)) { + ++internal->stats.congruence.unaries; + ++internal->stats.congruence.unary_ites; + } + if (!internal->unsat) { + if (internal->lrat) + lrat_chain = reasons_unit; + learn_congruence_unit (-cond); + } + } + delete_proof_chain (); + garbage = true; + } else if (dst == cond) { + // cond ? cond : else_lit + // cond & cond | !cond & else_lit + // cond | !cond & else_lit + // cond | else_lit + // !(!cond & !else_lit) + g->lhs = not_lhs; + rhs[0] = not_cond; + rhs[1] = not_else_lit; + if (else_lit == lhs || cond == lhs) + garbage = true; + else + garbage = rewrite_ite_gate_to_and (g, src, dst, 1, 3, -cond); + } else if (not_dst == cond) { + // cond ? !cond : else_lit + // cond & !cond | !cond & else_lit + // !cond & else_lit + rhs[0] = not_cond; + rhs[1] = else_lit; + garbage = rewrite_ite_gate_to_and (g, src, dst, 0, 2, -cond); + } else if (dst == else_lit) { + // cond ? else_lit : else_lit + // else_lit + std::vector<LRAT_ID> reasons_implication, reasons_back; + produce_ite_merge_then_else_reasons (g, src, dst, reasons_implication, + reasons_back); + if (merge_literals_lrat (lhs, else_lit, reasons_implication, + reasons_back)) { + ++internal->stats.congruence.unaries; + ++internal->stats.congruence.unary_ites; + } + delete_proof_chain (); + garbage = true; + } else if (not_dst == else_lit) { + // cond ? !else_lit : else_lit + // cond & !else_lit | !cond & else_lit + // cond ^ else_lit + if (g->lhs == cond) { + produce_rewritten_clause_lrat_and_clean (g->pos_lhs_ids, g->lhs, + false); + if (internal->lrat) { + CADICAL_assert (g->pos_lhs_ids.size () == 2); + lrat_chain.push_back (g->pos_lhs_ids[0].clause->id); + lrat_chain.push_back (g->pos_lhs_ids[1].clause->id); + } + learn_congruence_unit (-else_lit); + garbage = true; + } else { + LOG ("changing to xor"); + new_tag = Gate_Type::XOr_Gate; + CADICAL_assert (rhs[0] == cond); + rhs[1] = else_lit; + CADICAL_assert (!internal->lrat || !g->pos_lhs_ids.empty ()); + { +#ifdef LOGGING + for (auto litId : g->pos_lhs_ids) { + LOG (litId.clause, "%d ->", litId.current_lit); + } +#endif + produce_rewritten_clause_lrat_and_clean (g->pos_lhs_ids, g->lhs, + true); +#ifdef LOGGING + for (auto litId : g->pos_lhs_ids) { + LOG (litId.clause, "%d ->", litId.current_lit); + } +#endif + } + } + } else { + shrink = false; + rhs[1] = dst; + rewrite_ite_gate_update_lrat_reasons (g, src, dst); + } + } else { + CADICAL_assert (src == else_lit); + if (not_dst == g->lhs) { // TODO not in kissat + rhs[2] = dst; + std::vector<LRAT_ID> reasons_implication, reasons_back, reasons_unit; + bool learn_units_instead_of_equivalence = false; + produce_ite_merge_lhs_then_else_reasons ( + g, reasons_implication, reasons_back, reasons_unit, false, + learn_units_instead_of_equivalence); + if (learn_units_instead_of_equivalence) { // Too hard to produce LRAT + if (internal->lrat) + lrat_chain = reasons_unit; + learn_congruence_unit (cond, true); + if (then_lit != lhs) { + LOG ("special case, learning %d",cond == -lhs ? -then_lit : then_lit); + if (internal->lrat) + lrat_chain = reasons_implication; + learn_congruence_unit (cond == -lhs ? -then_lit : then_lit, false, true); + } else fully_propagate (); + } else { + if (merge_literals_lrat (lhs, then_lit, reasons_implication, + reasons_back)) { + ++internal->stats.congruence.unaries; + ++internal->stats.congruence.unary_ites; + } + if (!internal->unsat) { + if (internal->lrat) + lrat_chain = reasons_unit; + learn_congruence_unit (cond); + } + } + delete_proof_chain (); + garbage = true; + } else if (dst == cond) { + // cond ? then_lit : cond + // cond & then_lit | !cond & cond + // cond & then_lit + CADICAL_assert (rhs[0] == cond); + CADICAL_assert (rhs[1] == then_lit); + garbage = rewrite_ite_gate_to_and (g, src, dst, 2, 0, cond); + } else if (not_dst == cond) { + // cond ? then_lit : !cond + // cond & then_lit | !cond & !cond + // cond & then_lit | !cond + // then_lit | !cond + // !(!then_lit & cond) + g->lhs = not_lhs; + CADICAL_assert (rhs[0] == cond); + rhs[1] = not_then_lit; + if (then_lit == lhs || cond == lhs) + garbage = true; + else + garbage = rewrite_ite_gate_to_and (g, src, dst, 3, 1, cond); + } else if (dst == then_lit) { + // cond ? then_lit : then_lit + // then_lit + std::vector<LRAT_ID> reasons_implication, reasons_back; + produce_ite_merge_then_else_reasons (g, src, dst, reasons_implication, + reasons_back); + if (merge_literals_lrat (lhs, then_lit, reasons_implication, + reasons_back)) { + ++internal->stats.congruence.unaries; + ++internal->stats.congruence.unary_ites; + } + garbage = true; + } else if (not_dst == then_lit) { + // cond ? then_lit : !then_lit + // cond & then_lit | !cond & !then_lit + // !(cond ^ then_lit) + if (lhs == cond) { + produce_rewritten_clause_lrat_and_clean (g->pos_lhs_ids, not_lhs, + false); + if (internal->lrat) { + CADICAL_assert (g->pos_lhs_ids.size () == 2); + lrat_chain.push_back (g->pos_lhs_ids[0].clause->id); + lrat_chain.push_back (g->pos_lhs_ids[1].clause->id); + } + learn_congruence_unit (then_lit); + garbage = true; + } else if (not_lhs == cond) { + produce_rewritten_clause_lrat_and_clean (g->pos_lhs_ids, not_lhs, + false); + if (internal->lrat) { + CADICAL_assert (g->pos_lhs_ids.size () == 2); + lrat_chain.push_back (g->pos_lhs_ids[0].clause->id); + lrat_chain.push_back (g->pos_lhs_ids[1].clause->id); + } + learn_congruence_unit (-then_lit); + garbage = true; + } else if (not_lhs == then_lit) { + produce_rewritten_clause_lrat_and_clean (g->pos_lhs_ids, not_lhs, + false); + if (internal->lrat) { + CADICAL_assert (g->pos_lhs_ids.size () == 2); + lrat_chain.push_back (g->pos_lhs_ids[0].clause->id); + lrat_chain.push_back (g->pos_lhs_ids[1].clause->id); + } + learn_congruence_unit (cond); + garbage = true; + } else { + new_tag = Gate_Type::XOr_Gate; + g->lhs = not_lhs; + CADICAL_assert (rhs[0] == cond); + CADICAL_assert (rhs[1] == then_lit); + CADICAL_assert (rhs[0] != g->lhs); + CADICAL_assert (rhs[1] != g->lhs); + produce_rewritten_clause_lrat_and_clean (g->pos_lhs_ids, not_lhs, + false); + } + } else { + shrink = false; + rhs[2] = dst; + rewrite_ite_gate_update_lrat_reasons (g, src, dst); + } + } + if (!garbage && !internal->unsat) { + CADICAL_assert (new_tag != Gate_Type::ITE_Gate || g->lhs != -rhs[1]); + CADICAL_assert (new_tag != Gate_Type::ITE_Gate || g->lhs != -rhs[2]); + if (shrink) { + if (new_tag == Gate_Type::XOr_Gate) { + bool negate_lhs = false; + if (rhs[0] < 0) { + rhs[0] = -rhs[0]; + negate_lhs = !negate_lhs; + } + if (rhs[1] < 0) { + rhs[1] = -rhs[1]; + negate_lhs = !negate_lhs; + } + if (negate_lhs) + g->lhs = -g->lhs; + } + if (internal->vlit (rhs[0]) > + internal->vlit ( + rhs[1])) { // unlike kissat, we need to do it after negating + std::swap (rhs[0], rhs[1]); + CADICAL_assert (new_tag != Gate_Type::ITE_Gate); + } + CADICAL_assert (internal->vlit (rhs[0]) < internal->vlit (rhs[1])); + CADICAL_assert (!g->shrunken); + g->shrunken = true; + rhs[2] = 0; + g->tag = new_tag; + rhs.resize (2); + CADICAL_assert (rhs[0] != -rhs[1]); + if (new_tag == Gate_Type::XOr_Gate) { + if (rhs[0] == -g->lhs || rhs[1] == -g->lhs) { + LOG (g, "special XOR:"); + const int unit = rhs[0] ^ -g->lhs ^ rhs[1]; + produce_rewritten_clause_lrat_and_clean (g->pos_lhs_ids, not_lhs, + false); + if (internal->lrat) { + CADICAL_assert (g->pos_lhs_ids.size () == 2); + lrat_chain.push_back (g->pos_lhs_ids[0].clause->id); + lrat_chain.push_back (g->pos_lhs_ids[1].clause->id); + } + learn_congruence_unit (unit); + garbage = true; + } else if (rhs[0] == g->lhs || rhs[1] == g->lhs) { + LOG (g, "special XOR:"); + const int unit = rhs[0] ^ g->lhs ^ rhs[1]; + produce_rewritten_clause_lrat_and_clean (g->pos_lhs_ids, not_lhs, + false); + if (internal->lrat) { + CADICAL_assert (g->pos_lhs_ids.size () == 2); + lrat_chain.push_back (g->pos_lhs_ids[0].clause->id); + lrat_chain.push_back (g->pos_lhs_ids[1].clause->id); + } + learn_congruence_unit (-unit); + garbage = true; + } else { + int i = 0; + bool negated = false; + for (int j = 0; j < 2; ++i, ++j) { + CADICAL_assert (i <= j); + const int lit = rhs[i] = rhs[j]; + const char v = internal->val (lit); + if (v > 0) { + --i; + negated = !negated; + } else if (v < 0) { + --i; + } + } + CADICAL_assert (i <= 2); + rhs.resize (i); + if (negated) { + g->lhs = -g->lhs; + } + if (i != 2) { + LOG (g, "removed units"); + } + if (!i) + garbage = true; + else if (i == 1) { +#ifdef LOGGING + for (auto litId : g->pos_lhs_ids) { + LOG (litId.clause, "%d ->", litId.current_lit); + } +#endif + produce_rewritten_clause_lrat_and_clean (g->pos_lhs_ids, g->lhs); + CADICAL_assert (!internal->lrat || g->pos_lhs_ids.size () == 2); + Clause *c1 = nullptr, *c2 = nullptr; + if (internal->lrat) { + CADICAL_assert (g->pos_lhs_ids[0].clause); + bool rhs_as_src_first = + g->pos_lhs_ids[0].clause->literals[0] == g->lhs || + g->pos_lhs_ids[0].clause->literals[1] == g->lhs; + c1 = (rhs_as_src_first ? g->pos_lhs_ids[0].clause + : g->pos_lhs_ids[1].clause); + c2 = (rhs_as_src_first ? g->pos_lhs_ids[1].clause + : g->pos_lhs_ids[0].clause); + c1 = maybe_promote_tmp_binary_clause (c1); + c2 = maybe_promote_tmp_binary_clause (c2); + } else { + maybe_add_binary_clause (-g->lhs, g->rhs[0]); + maybe_add_binary_clause (g->lhs, -g->rhs[0]); + } + merge_literals_equivalence (g->lhs, g->rhs[0], c1, c2); + garbage = true; + } + } + if (!garbage) { + CADICAL_assert (rhs[0] != g->lhs); + CADICAL_assert (rhs[1] != g->lhs); + CADICAL_assert (rhs[0] != -g->lhs); + CADICAL_assert (rhs[1] != -g->lhs); + } + } + + if (!garbage) { + g->hash = hash_lits (nonces, g->rhs); + LOG (g, "rewritten"); + + if (internal->lrat) { + if (new_tag == Gate_Type::XOr_Gate) { +#ifndef CADICAL_NDEBUG + std::for_each (begin (g->pos_lhs_ids), end (g->pos_lhs_ids), + [g] (LitClausePair l) { + CADICAL_assert ((size_t) l.clause->size == + 1 + g->arity ()); + }); +#endif + } else if (new_tag == Gate_Type::And_Gate) { + // we have to get rid of one clause, two become binaries, and + // becomes ternary + +#if defined(LOGGING) || !defined(CADICAL_NDEBUG) + for (auto id : g->pos_lhs_ids) { + LOG (id.clause, "clause after rewriting:"); + CADICAL_assert (id.clause->size == 2); + } +#endif + CADICAL_assert (g->pos_lhs_ids.size () == 2 || + gate_contains (g, g->lhs)); + CADICAL_assert (g->neg_lhs_ids.size () == 1 || + gate_contains (g, g->lhs)); + CADICAL_assert (g->arity () == 2); +#ifndef CADICAL_NDEBUG + std::for_each ( + begin (g->pos_lhs_ids), end (g->pos_lhs_ids), + [] (LitClausePair l) { CADICAL_assert (l.clause->size == 2); }); +#endif + } else { + CADICAL_assert (false); +#ifdef WIN32 + __assume(false); +#else + __builtin_unreachable (); +#endif + } + } + Gate *h; + if (new_tag == Gate_Type::And_Gate) { + check_and_gate_implied (g); + h = find_and_lits (rhs); + } else { + CADICAL_assert (new_tag == Gate_Type::XOr_Gate); + check_xor_gate_implied (g); + h = find_xor_gate (g); + } + if (h) { + garbage = true; + if (new_tag == Gate_Type::XOr_Gate) { + std::vector<LRAT_ID> reasons_implication, reasons_back; + add_xor_matching_proof_chain (g, g->lhs, h->pos_lhs_ids, h->lhs, + reasons_implication, + reasons_back); + if (merge_literals_lrat (g->lhs, h->lhs, reasons_implication, + reasons_back)) + ++internal->stats.congruence.xors; + } else { + add_ite_turned_and_binary_clauses (g); + std::vector<LRAT_ID> reasons_implication, reasons_back; + if (internal->lrat) + merge_and_gate_lrat_produce_lrat (g, h, reasons_implication, + reasons_back, false); + if (merge_literals_lrat (g->lhs, h->lhs, reasons_implication, + reasons_back)) + ++internal->stats.congruence.ands; + } + delete_proof_chain (); + } else { + garbage = false; + if (g->indexed) + remove_gate (git); + index_gate (g); + CADICAL_assert (g->arity () == 2); + for (auto lit : g->rhs) + if (lit != dst) + if (lit != cond && lit != then_lit && lit != else_lit) + connect_goccs (g, lit); + if (g->tag == Gate_Type::And_Gate && !internal->lrat) + for (auto lit : g->rhs) + maybe_add_binary_clause (-g->lhs, lit); + } + } + } else { + LOG (g, "rewritten"); + if (internal->lrat) + update_ite_flags (g), check_correct_ite_flags(g); + CADICAL_assert (rhs[0] != rhs[1]); + CADICAL_assert (rhs[0] != rhs[2]); + CADICAL_assert (rhs[1] != rhs[2]); + CADICAL_assert (rhs[0] != -(rhs[1])); + CADICAL_assert (rhs[0] != -(rhs[2])); + CADICAL_assert (rhs[1] != -(rhs[2])); + check_ite_gate_implied (g); + check_ite_lrat_reasons (g, false); + bool negate_lhs; + Gate *h = find_ite_gate (g, negate_lhs); + CADICAL_assert (lhs == g->lhs); + CADICAL_assert (not_lhs == -(g->lhs)); + if (negate_lhs) + g->lhs = -lhs; + check_ite_lrat_reasons (g); + if (internal->lrat) + check_correct_ite_flags (g); + if (h) { + garbage = true; + check_ite_gate_implied (g); + check_ite_lrat_reasons (g, false); + check_ite_gate_implied (h); + check_ite_lrat_reasons (h, false); + int normalized_lhs = negate_lhs ? not_lhs : lhs; + std::vector<LRAT_ID> extra_reasons_lit, extra_reasons_ulit; + add_ite_matching_proof_chain (g, h, normalized_lhs, h->lhs, + extra_reasons_lit, + extra_reasons_ulit); + if (merge_literals_lrat (normalized_lhs, h->lhs, extra_reasons_lit, + extra_reasons_ulit)) + ++internal->stats.congruence.ites; + delete_proof_chain (); + CADICAL_assert (internal->unsat || chain.empty ()); + } else { + garbage = false; + if (g->indexed) + remove_gate (git); + LOG (g, "normalized"); + g->hash = hash_lits (nonces, g->rhs); + index_gate (g); + CADICAL_assert (g->arity () == 3); + for (auto lit : g->rhs) + if (lit != dst) + if (lit != cond && lit != then_lit && lit != else_lit) + connect_goccs (g, lit); + } + } + } + if (garbage && !internal->unsat) + mark_garbage (g); + + CADICAL_assert (chain.empty ()); +} + +void Closure::simplify_ite_gate_produce_unit_lrat (Gate *g, int lit, + size_t idx1, + size_t idx2) { + if (!internal->lrat) + return; + // TODO + if (internal->val (lit) > 0) + return; + CADICAL_assert (internal->lrat); + CADICAL_assert (g); + CADICAL_assert (idx1 < g->pos_lhs_ids.size ()); + CADICAL_assert (idx2 < g->pos_lhs_ids.size ()); + CADICAL_assert (g->pos_lhs_ids.size () == 4); + + CADICAL_assert (idx1 != idx2); + Clause *c = g->pos_lhs_ids[idx1].clause; + Clause *d = g->pos_lhs_ids[idx2].clause; + + if (g->lhs == -g->rhs[0]) { + LOG ("special case of LHS=-cond where only one clause in LRAT is needed is needed"); + size_t idx = (internal->val (g->rhs[1]) < 0 ? idx2 : idx1); + c = produce_rewritten_clause_lrat (g->pos_lhs_ids[idx].clause, g->lhs, false, false); + CADICAL_assert (c); + // not possible to do this in a single lrat chain + push_id_and_rewriting_lrat_unit (c, Rewrite (), lrat_chain, true, + Rewrite (), g->lhs); + CADICAL_assert (d); + return; + } + if (g->lhs == g->rhs[0]) { + LOG ("special case of LHS=cond where only one clause in LRAT is needed is needed"); + size_t idx = (internal->val (g->rhs[1]) > 0 ? idx2 : idx1); + c = produce_rewritten_clause_lrat (g->pos_lhs_ids[idx].clause, g->lhs, false, false); + CADICAL_assert (c); + // not possible to do this in a single lrat chain + push_id_and_rewriting_lrat_unit (c, Rewrite (), lrat_chain, true, + Rewrite (), g->lhs); + CADICAL_assert (d); + return; + } + + c = produce_rewritten_clause_lrat (c, g->lhs, true); + if (c) { + lrat_chain.push_back (c->id); + d = produce_rewritten_clause_lrat (d, g->lhs, true); + if (d) + lrat_chain.push_back (d->id); + } else if (!c) { + push_id_and_rewriting_lrat_unit (d, Rewrite (), lrat_chain, true, + Rewrite (), g->lhs); + CADICAL_assert (d); + lrat_chain.push_back (d->id); + } +} + +// TODO merge +void Closure::merge_and_gate_lrat_produce_lrat ( + Gate *g, Gate *h, std::vector<LRAT_ID> &reasons_lrat_src, + std::vector<LRAT_ID> &reasons_lrat_usrc, bool remove_units) { + CADICAL_assert (internal->lrat); + CADICAL_assert (g->tag == Gate_Type::And_Gate); + CADICAL_assert (h->tag == Gate_Type::And_Gate); + CADICAL_assert (g->neg_lhs_ids.size () <= 1); + update_and_gate_build_lrat_chain (g, h, reasons_lrat_src, + reasons_lrat_usrc, remove_units); +} + +// odd copy of rewrite_ite_gate_lrat_and +bool Closure::simplify_ite_gate_to_and (Gate *g, size_t idx1, size_t idx2, + int removed_lit) { + CADICAL_assert (internal->lrat_chain.empty ()); + CADICAL_assert (g->rhs.size () == 3); +#ifdef LOGGING + for (auto litId : g->pos_lhs_ids) { + LOG (litId.clause, "%d ->", litId.current_lit); + } +#endif + if (g->lhs == -g->rhs[0] || g->lhs == -g->rhs[1]) { + if (internal->lrat) { + Clause *c = g->pos_lhs_ids[idx1].clause; + push_id_and_rewriting_lrat_unit (c, Rewrite (), lrat_chain); + CADICAL_assert (!lrat_chain.empty ()); + } + learn_congruence_unit (-g->lhs); + return true; + } + if (!internal->lrat) + return false; + g->degenerated_and_neg = (g->degenerated_and_neg || g->rhs[1] == -g->lhs || g->rhs[0] == -g->lhs); + g->degenerated_and_pos = (g->degenerated_and_pos || g->rhs[0] == g->lhs || g->rhs[1] == g->lhs); + + CADICAL_assert (g->pos_lhs_ids.size () == 4); + CADICAL_assert (idx1 < g->pos_lhs_ids.size ()); + CADICAL_assert (idx2 < g->pos_lhs_ids.size ()); + int lit = g->pos_lhs_ids[idx2].current_lit, other = g->lhs; + size_t new_idx1 = idx1; + size_t new_idx2 = idx2; + produce_rewritten_clause_lrat_and_clean (g->pos_lhs_ids, g->lhs, new_idx1, + new_idx2); + + if (g->pos_lhs_ids.size () == 1) { + LOG (g, "degenerated AND gate"); + const int replacement_lit = (g->rhs[1] == g->lhs ? g->rhs[0] : g->rhs[1]); + for (auto &litId : g->pos_lhs_ids) { + CADICAL_assert (litId.clause); + LOG (litId.clause, "%d ->", litId.current_lit); + if (litId.current_lit == removed_lit) + litId.current_lit = -replacement_lit; + if (litId.current_lit == -removed_lit) + litId.current_lit = replacement_lit; + LOG (litId.clause, "%d ->", litId.current_lit); + // TODO we need a replacement index + CADICAL_assert (std::find (begin (*litId.clause), end (*litId.clause), litId.current_lit) != + end (*litId.clause)); + CADICAL_assert (std::find (begin (g->rhs), end (g->rhs), litId.current_lit) != + end (g->rhs)); + } + return false; + } + CADICAL_assert (new_idx1 < g->pos_lhs_ids.size ()); + CADICAL_assert (new_idx2 < g->pos_lhs_ids.size ()); + Clause *c = g->pos_lhs_ids[new_idx1].clause; + CADICAL_assert (c->size == 2); + CADICAL_assert (new_idx1 != new_idx2); + Clause *d = g->pos_lhs_ids[new_idx2].clause; + CADICAL_assert (c != d); + CADICAL_assert (c); + CADICAL_assert (d); + g->pos_lhs_ids.erase (std::remove_if (begin (g->pos_lhs_ids), + end (g->pos_lhs_ids), + [d] (const LitClausePair &p) { + return p.clause == d; + }), + end (g->pos_lhs_ids)); + CADICAL_assert (g->pos_lhs_ids.size () == 2); + CADICAL_assert (lit); + CADICAL_assert (other); + CADICAL_assert (lit != other); + lrat_chain.push_back (c->id); + lrat_chain.push_back (d->id); + Clause *e = add_tmp_binary_clause (lit, -other); + + auto long_clause = + std::find_if (begin (g->pos_lhs_ids), end (g->pos_lhs_ids), + [] (LitClausePair l) { return l.clause->size == 3; }); + CADICAL_assert (long_clause != end (g->pos_lhs_ids)); + LOG (long_clause->clause, "new long clause"); + g->neg_lhs_ids.push_back (*long_clause); + g->pos_lhs_ids.erase (long_clause); + CADICAL_assert (g->pos_lhs_ids.size () == 1); + g->pos_lhs_ids.push_back ({lit, e}); + + const int first_lit = g->rhs[0]; + const int second_lit = g->rhs[1]; + for (auto &litId : g->pos_lhs_ids) { + CADICAL_assert (litId.clause); + LOG (litId.clause, "%s ->", LOGLIT (litId.current_lit)); + if (internal->val (litId.current_lit)) { + CADICAL_assert (litId.clause->size == 2); + int replacement_lit = 0; + for (int i = 0; i < 2; ++i) { + if (litId.clause->literals[i] == first_lit) { + replacement_lit = first_lit; + break; + } else if (litId.clause->literals[i] == second_lit) { + replacement_lit = second_lit; + break; + } + } + CADICAL_assert (replacement_lit); + + litId.current_lit = replacement_lit; + } else if (litId.current_lit == removed_lit) + litId.current_lit = -g->rhs[0]; + else if (litId.current_lit == -removed_lit) + litId.current_lit = g->rhs[0]; + LOG (litId.clause, "%d ->", litId.current_lit); + // TODO we need a replacement index + CADICAL_assert (std::find (begin (g->rhs), end (g->rhs), litId.current_lit) != + end (g->rhs)); + CADICAL_assert (std::find (begin (*litId.clause), end (*litId.clause), + litId.current_lit) != end (*litId.clause)); + } + return false; +} + +void Closure::merge_ite_gate_same_then_else_lrat ( + std::vector<LitClausePair> &clauses, + std::vector<LRAT_ID> &reasons_implication, + std::vector<LRAT_ID> &reasons_back) { + CADICAL_assert (clauses.size () == 4); + produce_rewritten_clause_lrat_and_clean (clauses); + CADICAL_assert (clauses.size () == 4); + auto then_imp = clauses[0]; + auto neg_then_imp = clauses[1]; + auto else_imp = clauses[2]; + auto neg_else_imp = clauses[3]; + reasons_implication.push_back (then_imp.clause->id); + reasons_implication.push_back (else_imp.clause->id); + reasons_back.push_back (neg_then_imp.clause->id); + reasons_back.push_back (neg_else_imp.clause->id); +} + +void Closure::simplify_ite_gate_then_else_set ( + Gate *g, std::vector<LRAT_ID> &reasons_implication, + std::vector<LRAT_ID> &reasons_back, size_t idx1, size_t idx2) { + CADICAL_assert (idx1 < g->pos_lhs_ids.size ()); + CADICAL_assert (idx2 < g->pos_lhs_ids.size ()); + Clause *c = g->pos_lhs_ids[idx1].clause; + Clause *d = g->pos_lhs_ids[idx2].clause; + push_id_and_rewriting_lrat_unit (c, Rewrite (), reasons_back, true, + Rewrite (), g->lhs); + push_id_and_rewriting_lrat_unit (d, Rewrite (), reasons_implication, true, + Rewrite (), g->lhs); + LOG (reasons_back, "LRAT"); + LOG (reasons_implication, "LRAT"); + // c = produce_rewritten_clause_lrat (c, g->lhs, false); + // CADICAL_assert (c); + // d = produce_rewritten_clause_lrat (d, g->lhs, false); + // CADICAL_assert (d); + // const int cond = g->rhs[0]; + // CADICAL_assert (internal->val (cond)); + // reasons_implication.push_back(internal->unit_id (internal->val (cond) > + // 0 ? cond : -cond)); reasons_implication.push_back(c->id); + // reasons_back.push_back(internal->unit_id (internal->val (cond) > 0 ? + // cond : -cond)); reasons_back.push_back(d->id); +} + +void Closure::simplify_ite_gate_condition_set ( + Gate *g, std::vector<LRAT_ID> &reasons_lrat, + std::vector<LRAT_ID> &reasons_back_lrat, size_t idx1, size_t idx2) { + CADICAL_assert (internal->lrat); + Clause *c = g->pos_lhs_ids[idx1].clause; + Clause *d = g->pos_lhs_ids[idx2].clause; +#if defined(LOGGING) || !defined(CADICAL_NDEBUG) + const int cond = g->rhs[0]; + CADICAL_assert (internal->val (cond)); + LOG ("cond = %d", cond); +#endif +#ifdef LOGGING + for (auto litid : g->pos_lhs_ids) + LOG (litid.clause, "clause in gate:"); +#endif + push_id_and_rewriting_lrat_unit (c, Rewrite (), reasons_lrat, true, + Rewrite (), -g->lhs); + push_id_and_rewriting_lrat_unit (d, Rewrite (), reasons_back_lrat, true, + Rewrite (), g->lhs); +} + +void Closure::simplify_ite_gate (Gate *g) { + if (skip_ite_gate (g)) + return; + LOG (g, "simplifying"); + CADICAL_assert (g->arity () == 3); + bool garbage = true; + int lhs = g->lhs; + auto &rhs = g->rhs; + const int cond = rhs[0]; + const int then_lit = rhs[1]; + const int else_lit = rhs[2]; + const signed char v_cond = internal->val (cond); + const signed char v_else = internal->val (else_lit); + const signed char v_then = internal->val (then_lit); + std::vector<LRAT_ID> reasons_lrat, reasons_back_lrat; + if (v_cond && v_else && v_then) { // propagation has set all value anyway + LOG (g, "all values are set"); + CADICAL_assert (internal->val (g->lhs)); + garbage = true; + } else if (v_cond > 0) { + if (internal->lrat) + simplify_ite_gate_condition_set (g, reasons_lrat, reasons_back_lrat, + 0, 1); + if (merge_literals_lrat (lhs, then_lit, reasons_lrat, + reasons_back_lrat)) { + ++internal->stats.congruence.unary_ites; + ++internal->stats.congruence.unaries; + } + } else if (v_cond < 0) { + if (internal->lrat) + simplify_ite_gate_condition_set (g, reasons_lrat, reasons_back_lrat, + 2, 3); + if (merge_literals_lrat (lhs, else_lit, reasons_lrat, + reasons_back_lrat)) { + ++internal->stats.congruence.unary_ites; + ++internal->stats.congruence.unaries; + } + } else { + LOG ("then %d: %d; else %d: %d", then_lit, v_then, else_lit, v_else); + std::vector<LRAT_ID> extra_reasons, extra_reasons_back; + CADICAL_assert (v_then || v_else); + if (v_then > 0 && v_else > 0) { + simplify_ite_gate_produce_unit_lrat (g, lhs, 1, 3); + learn_congruence_unit (lhs); + } else if (v_then < 0 && v_else < 0) { + simplify_ite_gate_produce_unit_lrat (g, -lhs, 0, 2); + learn_congruence_unit (-lhs); + } else if (v_then > 0 && v_else < 0) { + if (internal->lrat) + simplify_ite_gate_then_else_set (g, extra_reasons, + extra_reasons_back, 1, 2); + if (merge_literals_lrat (lhs, cond, extra_reasons, + extra_reasons_back)) { + ++internal->stats.congruence.unary_ites; + ++internal->stats.congruence.unaries; + } + } else if (v_then < 0 && v_else > 0) { + if (internal->lrat) + simplify_ite_gate_then_else_set (g, extra_reasons, + extra_reasons_back, 0, 3); + if (merge_literals_lrat (lhs, -cond, extra_reasons_back, + extra_reasons)) { + ++internal->stats.congruence.unary_ites; + ++internal->stats.congruence.unaries; + } + } else { + CADICAL_assert (!!v_then + !!v_else == 1); + auto git = g->indexed ? table.find (g) : end (table); + CADICAL_assert (!g->indexed || git != end (table)); + if (v_then > 0) { + g->lhs = -lhs; + rhs[0] = -cond; + rhs[1] = -else_lit; + simplify_ite_gate_to_and (g, 1, 3, then_lit); + } else if (v_then < 0) { + rhs[0] = -cond; + rhs[1] = else_lit; + simplify_ite_gate_to_and (g, 0, 2, -then_lit); + } else if (v_else > 0) { + g->lhs = -lhs; + rhs[0] = -then_lit; + rhs[1] = cond; + simplify_ite_gate_to_and (g, 3, 1, else_lit); + } else { + CADICAL_assert (v_else < 0); + rhs[0] = then_lit; + rhs[1] = cond; + simplify_ite_gate_to_and (g, 2, 0, -else_lit); + } + if (internal->unsat) + return; + if (internal->vlit (rhs[0]) > internal->vlit (rhs[1])) + std::swap (rhs[0], rhs[1]); + g->shrunken = true; + g->tag = Gate_Type::And_Gate; + rhs.resize (2); + CADICAL_assert (is_sorted (begin (rhs), end (rhs), + sort_literals_by_var_smaller (internal))); + g->hash = hash_lits (nonces, rhs); + check_and_gate_implied (g); + Gate *h = find_and_lits (rhs); + if (h) { + CADICAL_assert (garbage); + std::vector<LRAT_ID> reasons_lrat, reasons_lrat_back; + if (internal->lrat) + merge_and_gate_lrat_produce_lrat (g, h, reasons_lrat, + reasons_lrat_back, false); + if (merge_literals_lrat (g->lhs, h->lhs, reasons_lrat, + reasons_lrat_back)) { + ++internal->stats.congruence.ites; + } + } else { + remove_gate (git); + index_gate (g); + garbage = false; + g->hash = hash_lits (nonces, g->rhs); + for (auto lit : rhs) + if (lit != cond && lit != then_lit && lit != else_lit) { + connect_goccs (g, lit); + } + + if (rhs[0] == -g->lhs || rhs[1] == -g->lhs) + simplify_and_gate ( + g); // TODO Kissat does not do that, but it has also no + // checks to verify that it cannot happen... + } + } + } + if (garbage && !internal->unsat) + mark_garbage (g); + ++internal->stats.congruence.simplified; + ++internal->stats.congruence.simplified_ites; +} + +void Closure::add_ite_matching_proof_chain ( + Gate *g, Gate *h, int lhs1, int lhs2, std::vector<LRAT_ID> &reasons1, + std::vector<LRAT_ID> &reasons2) { + check_ite_lrat_reasons (g); + check_ite_lrat_reasons (h, false); + CADICAL_assert (g->lhs == lhs1); + CADICAL_assert (h->lhs == lhs2); + if (lhs1 == lhs2) + return; + if (!internal->proof) + return; + LOG (g, "starting ITE matching proof chain"); + LOG (h, "starting ITE matching proof chain with"); + CADICAL_assert (unsimplified.empty ()); + CADICAL_assert (chain.empty ()); + if (internal->lrat) + check_correct_ite_flags (g); + const auto &rhs = g->rhs; + const int8_t flags_g = g->degenerated_ite; + const int8_t flags_h = h->degenerated_ite; + const int cond = rhs[0]; + LRAT_ID g_then_id = 0, g_neg_then_id = 0, g_neg_else_id = 0; + LRAT_ID h_then_id = 0, h_neg_then_id = 0, h_else_id = 0; + LRAT_ID g_else_id = 0, h_neg_else_id = 0; + const bool degenerated_g_then = ite_flags_no_then_clauses (flags_g); + const bool degenerated_g_else = ite_flags_no_else_clauses (flags_g); + const bool degenerated_h_then = ite_flags_no_then_clauses (flags_h); + const bool degenerated_h_else = ite_flags_no_else_clauses (flags_h); + + const bool degenerated_g_cond = ite_flags_cond_lhs (flags_g); + const bool degenerated_h_cond = ite_flags_cond_lhs (flags_h); + CADICAL_assert (!(degenerated_g_cond && degenerated_h_cond)); + + const bool degenerated_g_not_cond = ite_flags_neg_cond_lhs (flags_g); + const bool degenerated_h_not_cond = ite_flags_neg_cond_lhs (flags_h); + CADICAL_assert (!(degenerated_g_not_cond && degenerated_h_not_cond)); + + if (internal->lrat) { + // the code can produce tautologies in the case that: + // a = (c ? a : e) + // b = (c ? a : e) + // (no clauses with (then) index a/-a) + // but also for + // a = (a ? t : e) + // b = (a ? t : e) + // (no clauses with (then) index -a and (else) index e) + // and same for + // a = (c ? t : a) + // b = (c ? t : a) + // (no clauses with (then) index a and (else) index -e) + LOG (g, "get ids from"); + CADICAL_assert (g->pos_lhs_ids.size () == 4); + auto &g_then_clause = g->pos_lhs_ids[0].clause; + g_then_clause = + g_then_clause ? produce_rewritten_clause_lrat (g_then_clause, g->lhs, false) : nullptr; + if (g_then_clause) + g_then_id = g_then_clause->id; + + auto &g_neg_then_clause = g->pos_lhs_ids[1].clause; + g_neg_then_clause = + g_neg_then_clause ? produce_rewritten_clause_lrat (g_neg_then_clause, g->lhs, false) : nullptr; + if (g_neg_then_clause) + g_neg_then_id = g_neg_then_clause->id; + + auto &g_else_clause = g->pos_lhs_ids[2].clause; + g_else_clause = + g_else_clause ? produce_rewritten_clause_lrat (g_else_clause, g->lhs, false) : g_else_clause; + if (g_else_clause) + g_else_id = g_else_clause->id; + + auto &g_neg_else_clause = g->pos_lhs_ids[3].clause; + g_neg_else_clause = + g_neg_else_clause ? produce_rewritten_clause_lrat (g_neg_else_clause, g->lhs, false) : nullptr; + if (g_neg_else_clause) + g_neg_else_id = g_neg_else_clause->id; + + LOG (h, "now clauses from"); + CADICAL_assert (h->pos_lhs_ids.size () == 4); + auto &h_then_clause = h->pos_lhs_ids[0].clause; + h_then_clause = + h_then_clause ? produce_rewritten_clause_lrat (h_then_clause, h->lhs, false) : nullptr; + if (h_then_clause) + h_then_id = h_then_clause->id; + + auto &h_neg_then_clause = h->pos_lhs_ids[1].clause; + h_neg_then_clause = + h_neg_then_clause ? produce_rewritten_clause_lrat (h_neg_then_clause, h->lhs, false) : nullptr; + if (h_neg_then_clause) + h_neg_then_id = h_neg_then_clause->id; + + auto &h_else_clause = h->pos_lhs_ids[2].clause; + h_else_clause = + h_else_clause ? produce_rewritten_clause_lrat (h_else_clause, h->lhs, false) : nullptr; + if (h_else_clause) + h_else_id = h_else_clause->id; + + auto &h_neg_else_clause = h->pos_lhs_ids[3].clause; + h_neg_else_clause = + h_neg_else_clause ? produce_rewritten_clause_lrat (h_neg_else_clause, h->lhs, false) : nullptr; + if (h_neg_else_clause) + h_neg_else_id = h_neg_else_clause->id; + + } + + if (degenerated_g_cond) { + LOG ("special case: cond = lhs, g degenerated"); + unsimplified.push_back (-lhs1); + unsimplified.push_back (lhs2); + LRAT_ID id1 = 0; + if (internal->lrat) { + lrat_chain.push_back (g_then_id); + lrat_chain.push_back (h_neg_then_id); + } + id1 = simplify_and_add_to_proof_chain (unsimplified); + + unsimplified.clear (); + unsimplified.push_back (lhs1); + unsimplified.push_back (-lhs2); + LRAT_ID id2 = 0; + if (internal->lrat) { + lrat_chain.push_back (g_neg_else_id); + lrat_chain.push_back (h_else_id); + } + id2 = simplify_and_add_to_proof_chain (unsimplified); + + CADICAL_assert (!internal->lrat || id1); + CADICAL_assert (!internal->lrat || id2); + reasons1.push_back (id1); + reasons2.push_back (id2); + unsimplified.clear (); + return; + } + + if (degenerated_h_cond) { + LOG ("special case: cond = lhs, h degenerated"); + unsimplified.push_back (lhs1); // potentially lhs1 == lhs2 + unsimplified.push_back (-lhs2); + LRAT_ID id1 = 0; + if (internal->lrat) { + lrat_chain.push_back (h_then_id); + lrat_chain.push_back (g_neg_then_id); + } + id1 = simplify_and_add_to_proof_chain (unsimplified); + + unsimplified.clear (); + unsimplified.push_back (-lhs1); + unsimplified.push_back (lhs2); + LRAT_ID id2 = 0; + if (internal->lrat) { + lrat_chain.push_back (h_neg_else_id); + lrat_chain.push_back (g_else_id); + } + id2 = simplify_and_add_to_proof_chain (unsimplified); + + CADICAL_assert (!internal->lrat || id1); + CADICAL_assert (!internal->lrat || id2); + reasons2.push_back (id1); + reasons1.push_back (id2); + unsimplified.clear (); + return; + } + + if (degenerated_g_not_cond) { + LOG ("special case: cond = -lhs, g degenerated"); + unsimplified.push_back (lhs1); + unsimplified.push_back (-lhs2); + LRAT_ID id1 = 0; + if (internal->lrat) { + CADICAL_assert (g_neg_then_id && h_then_id && g_else_id && h_neg_else_id); + lrat_chain.push_back (g_neg_then_id); + lrat_chain.push_back (h_then_id); + } + id1 = simplify_and_add_to_proof_chain (unsimplified); + + unsimplified.clear (); + unsimplified.push_back (-lhs1); + unsimplified.push_back (lhs2); + LRAT_ID id2 = -1; + + if (internal->lrat) { + lrat_chain.push_back (g_else_id); + lrat_chain.push_back (h_neg_else_id); + } + id2 = simplify_and_add_to_proof_chain (unsimplified); + CADICAL_assert (!internal->lrat || id1); + CADICAL_assert (!internal->lrat || id2); + reasons2.push_back (id1); + reasons1.push_back (id2); + unsimplified.clear (); + return; + } + + if (degenerated_h_not_cond) { + LOG ("special case: cond = -lhs, h degenerated"); + unsimplified.push_back (lhs1); + unsimplified.push_back (-lhs2); + LRAT_ID id1 = 0; + if (internal->lrat) { + CADICAL_assert (g_neg_then_id && h_then_id && g_else_id && h_neg_else_id); + lrat_chain.push_back (h_neg_then_id); + lrat_chain.push_back (g_then_id); + } + id1 = simplify_and_add_to_proof_chain (unsimplified); + + unsimplified.clear (); + unsimplified.push_back (-lhs1); + unsimplified.push_back (lhs2); + LRAT_ID id2 = -1; + + if (internal->lrat) { + lrat_chain.push_back (h_else_id); + lrat_chain.push_back (g_neg_else_id); + } + id2 = simplify_and_add_to_proof_chain (unsimplified); + CADICAL_assert (!internal->lrat || id1); + CADICAL_assert (!internal->lrat || id2); + reasons2.push_back (id1); + reasons1.push_back (id2); + unsimplified.clear (); + return; + } + + LOG ("normal path"); + CADICAL_assert (!internal->lrat || degenerated_g_then || + (g_then_id && g_neg_then_id)); + CADICAL_assert (!internal->lrat || degenerated_g_else || + (g_else_id && g_neg_else_id)); + CADICAL_assert (!internal->lrat || degenerated_h_then || + (h_then_id && h_neg_then_id)); + CADICAL_assert (!internal->lrat || degenerated_h_else || + (h_else_id && h_neg_else_id)); + CADICAL_assert (!internal->lrat || g_then_id || h_neg_then_id); + CADICAL_assert (!internal->lrat || g_neg_then_id || h_then_id); + unsimplified.push_back (-lhs1); + unsimplified.push_back (lhs2); + unsimplified.push_back (-cond); + LRAT_ID id1 = 0; + if (degenerated_g_then || degenerated_h_then) { + id1 = degenerated_g_then ? h_neg_then_id : g_then_id; + } else { + if (internal->lrat) { + lrat_chain.push_back (g_then_id); + lrat_chain.push_back (h_neg_then_id); + } + id1 = simplify_and_add_to_proof_chain (unsimplified); + } + unsimplified.pop_back (); + unsimplified.push_back (cond); + + LRAT_ID id2 = 0; + if (degenerated_g_else || degenerated_h_else) { + id2 = degenerated_g_else ? h_neg_else_id : g_else_id; + } else { + if (internal->lrat) { + lrat_chain.push_back (h_neg_else_id); + lrat_chain.push_back (g_else_id); + } + id2 = simplify_and_add_to_proof_chain (unsimplified); + } + unsimplified.pop_back (); + + unsimplified.clear (); + unsimplified.push_back (lhs1); + unsimplified.push_back (-lhs2); + unsimplified.push_back (-cond); + CADICAL_assert (lrat_chain.empty ()); + + LRAT_ID id3 = 0; + if (degenerated_g_then || degenerated_h_then) { + id3 = degenerated_g_then ? h_then_id : g_neg_then_id; + } else { + if (internal->lrat) { + // lrat_chain.push_back (g_then_id); + // lrat_chain.push_back (h_neg_then_id); + lrat_chain.push_back (g_neg_then_id); + lrat_chain.push_back (h_then_id); + } + id3 = simplify_and_add_to_proof_chain (unsimplified); + } + unsimplified.pop_back (); + unsimplified.push_back (cond); + CADICAL_assert (lrat_chain.empty ()); + + LRAT_ID id4 = 0; + if (degenerated_g_else || degenerated_h_else) { + id4 = degenerated_g_else ? h_else_id : g_neg_else_id; + } else { + if (internal->lrat) { + lrat_chain.push_back (g_neg_else_id); + lrat_chain.push_back (h_else_id); + } + id4 = simplify_and_add_to_proof_chain (unsimplified); + } + unsimplified.pop_back (); + + if (internal->lrat) { + CADICAL_assert (!internal->lrat || (id1 && id2 && id3 && id4)); + reasons1.push_back (id1), reasons1.push_back (id2); + reasons2.push_back (id3), reasons2.push_back (id4); + } + + unsimplified.clear (); + + LOG ("finished ITE matching proof chain"); +} + +Gate *Closure::new_ite_gate (int lhs, int cond, int then_lit, int else_lit, + std::vector<LitClausePair> &&clauses) { + CADICAL_assert (chain.empty ()); + if (else_lit == -then_lit) { + if (then_lit < 0) + LOG ("skipping ternary XOR %d := %d ^ %d", lhs, cond, -then_lit); + else + LOG ("skipping ternary XOR %d := %d ^ %d", -lhs, cond, then_lit); + return nullptr; + } + if (else_lit == then_lit) { + LOG ("found trivial ITE gate %d := %d ? %d : %d", (lhs), (cond), + (then_lit), (else_lit)); + std::vector<LRAT_ID> reasons_implication, reasons_back; + if (internal->lrat) { + merge_ite_gate_same_then_else_lrat (clauses, reasons_implication, + reasons_back); + } + if (merge_literals_lrat (lhs, then_lit, reasons_implication, + reasons_back)) + ++internal->stats.congruence.trivial_ite; + return 0; + } + + rhs.clear (); + rhs.push_back (cond); + rhs.push_back (then_lit); + rhs.push_back (else_lit); + LOG ("ITE gate %d = %d ? %d : %d", lhs, cond, then_lit, else_lit); + + bool negate_lhs = false; + Gate *g = new Gate; + g->lhs = lhs; + g->tag = Gate_Type::ITE_Gate; + g->rhs = {rhs}; + g->pos_lhs_ids = clauses; +#ifdef LOGGING + g->id = -1; +#endif + Gate *h = find_ite_gate (g, negate_lhs); + if (negate_lhs) + lhs = -lhs; + g->lhs = lhs; + check_ite_gate_implied (g); + check_ite_lrat_reasons ( + g, false); // due to merges done before during AND gate detection! + + if (h) { + check_ite_gate_implied (h); + std::vector<LRAT_ID> extra_reasons_lit, extra_reasons_ulit; + add_ite_matching_proof_chain (h, g, h->lhs, lhs, extra_reasons_lit, + extra_reasons_ulit); + if (merge_literals_lrat (h, g, h->lhs, lhs, extra_reasons_lit, + extra_reasons_ulit)) { + ++internal->stats.congruence.ites; + LOG ("found merged literals"); + } + delete_proof_chain (); + delete g; + return h; + } else { + // do not sort clauses here obviously! + // sort (begin (g->rhs), end (g->rhs)); + g->garbage = false; + g->indexed = true; + g->shrunken = false; + g->hash = hash_lits (nonces, g->rhs); + table.insert (g); + ++internal->stats.congruence.gates; +#ifdef LOGGING + g->id = fresh_id++; +#endif + LOG (g, "creating new"); + if (internal->lrat) + update_ite_flags (g); + check_ite_gate_implied (g); + for (auto lit : g->rhs) { + connect_goccs (g, lit); + } + } + check_ite_lrat_reasons (g); + return g; +} + +void check_ite_lits_normalized (const std::vector<int> &lits) { + CADICAL_assert (lits[0] > 0); + CADICAL_assert (lits[1] > 0); + CADICAL_assert (lits[0] != lits[1]); + CADICAL_assert (lits[0] != lits[2]); + CADICAL_assert (lits[1] != lits[2]); + CADICAL_assert (lits[0] != -lits[1]); + CADICAL_assert (lits[0] != -lits[2]); + CADICAL_assert (lits[1] != -lits[2]); +#ifdef CADICAL_NDEBUG + (void) lits; +#endif +} + +void Closure::check_ite_implied (int lhs, int cond, int then_lit, + int else_lit) { + if (internal->lrat) + return; + check_ternary (cond, -else_lit, lhs); + check_ternary (cond, else_lit, -lhs); + check_ternary (-cond, -then_lit, lhs); + check_ternary (-cond, then_lit, -lhs); +} + +void Closure::check_contained_module_rewriting (Clause *c, int lit, + bool normalized, + int except) { +#ifndef CADICAL_NDEBUG + if (lit == except) // happens for degenerated cases + except = 0; + const int normalize_lit = + (lit == except ? except : find_eager_representative (lit)); + CADICAL_assert (!normalized || lit == -except || normalize_lit == lit); + bool found = false; + LOG (c, "looking for (normalized) %d in ", lit); + for (auto other : *c) { + const int normalize_other = + (other == except ? except : find_eager_representative (other)); + LOG ("%d -> %d ", other, normalize_other); + CADICAL_assert (!normalized || other == -except || normalize_other == other); + if (normalize_other == normalize_lit) { + found = true; + break; + } + } + CADICAL_assert (found); +#else + (void) c, (void) lit, (void) normalized, (void) except; +#endif +} + +void Closure::check_ite_lrat_reasons (Gate *g, bool normalized) { +#ifndef CADICAL_NDEBUG + CADICAL_assert (g->tag == Gate_Type::ITE_Gate); + if (!internal->lrat) + return; + CADICAL_assert (g->rhs.size () == 3); + CADICAL_assert (is_tautological_ite_gate (g) || g->pos_lhs_ids.size () == 4); + CADICAL_assert (g->neg_lhs_ids.empty ()); + CADICAL_assert (g->pos_lhs_ids.size () == 4); +#else + (void) g, (void) normalized; +#endif +} + +void Closure::check_ite_gate_implied (Gate *g) { + CADICAL_assert (g->tag == Gate_Type::ITE_Gate); + if (internal->lrat) + return; + check_ite_implied (g->lhs, g->rhs[0], g->rhs[1], g->rhs[2]); +} + +void Closure::init_ite_gate_extraction ( + std::vector<ClauseSize> &candidates) { + std::vector<Clause *> ternary; + glargecounts.resize (2 * internal->vsize, 0); + for (auto c : internal->clauses) { + if (c->garbage) + continue; + if (c->redundant) + continue; + if (c->size < 3) + continue; + unsigned size = 0; + + CADICAL_assert (!c->garbage); + for (auto lit : *c) { + const signed char v = internal->val (lit); + if (v < 0) + continue; + if (v > 0) { + LOG (c, "deleting as satisfied due to %d", lit); + internal->mark_garbage (c); + goto CONTINUE_COUNTING_NEXT_CLAUSE; + } + if (size == 3) + goto CONTINUE_COUNTING_NEXT_CLAUSE; + size++; + } + if (size < 3) + continue; + CADICAL_assert (size == 3); + ternary.push_back (c); + LOG (c, "counting original ITE gate base"); + for (auto lit : *c) { + if (!internal->val (lit)) + ++largecount (lit); + } + CONTINUE_COUNTING_NEXT_CLAUSE:; + } + + for (auto c : ternary) { + CADICAL_assert (!c->garbage); + CADICAL_assert (!c->redundant); + unsigned positive = 0, negative = 0, twice = 0; + for (auto lit : *c) { + if (internal->val (lit)) + continue; + const int count_not_lit = largecount (-lit); + if (!count_not_lit) + goto CONTINUE_WITH_NEXT_TERNARY_CLAUSE; + const unsigned count_lit = largecount (lit); + CADICAL_assert (count_lit); + if (count_lit > 1 && count_not_lit > 1) + ++twice; + if (lit < 0) + ++negative; + else + ++positive; + } + if (twice < 2) + goto CONTINUE_WITH_NEXT_TERNARY_CLAUSE; + CADICAL_assert (c->size != 2); + for (auto lit : *c) + internal->occs (lit).push_back (c); + if (positive && negative) + candidates.push_back (c); + CONTINUE_WITH_NEXT_TERNARY_CLAUSE:; + } + + ternary.clear (); +} + +void Closure::reset_ite_gate_extraction () { + condbin[0].clear (); + condbin[1].clear (); + condeq[0].clear (); + condeq[1].clear (); + glargecounts.clear (); + internal->clear_occs (); +} + +void Closure::copy_conditional_equivalences (int lit, + lit_implications &condbin) { + CADICAL_assert (condbin.empty ()); + for (auto c : internal->occs (lit)) { + CADICAL_assert (c->size != 2); + int first = 0, second = 0; + for (auto other : *c) { + if (internal->val (other)) + continue; + if (other == lit) + continue; + if (!first) + first = other; + else { + CADICAL_assert (!second); + second = other; + } + } + CADICAL_assert (first), CADICAL_assert (second); + lit_implication p (first, second, c); + + if (internal->vlit (first) < internal->vlit (second)) { + CADICAL_assert (p.first == first); + CADICAL_assert (p.second == second); + } else { + CADICAL_assert (internal->vlit (second) < internal->vlit (first)); + p.swap (); + CADICAL_assert (p.first == second); + CADICAL_assert (p.second == first); + } + LOG (c, "literal %d condition binary clause %d %d", lit, first, second); + condbin.push_back (p); + } +} + +bool less_litpair (lit_equivalence p, lit_equivalence q) { + const int a = p.first; + const int b = q.first; + if (a < b) + return true; + if (b > a) + return false; + const int c = p.second; + const int d = q.second; + return (c < d); +} +struct litpair_rank { + CaDiCaL::Internal *internal; + litpair_rank (Internal *i) : internal (i) {} + typedef uint64_t Type; + Type operator() (const lit_implication &a) const { + uint64_t lita = internal->vlit (a.first); + uint64_t litb = internal->vlit (a.second); + return (lita << 32) + litb; + } +}; + +struct litpair_smaller { + CaDiCaL::Internal *internal; + litpair_smaller (Internal *i) : internal (i) {} + bool operator() (const lit_implication &a, + const lit_implication &b) const { + const auto s = litpair_rank (internal) (a); + const auto t = litpair_rank (internal) (b); + return s < t; + } +}; + +lit_implications::const_iterator +Closure::find_lit_implication_second_literal ( + int lit, lit_implications::const_iterator begin, + lit_implications::const_iterator end) { + LOG ("searching for %d in", lit); + for (auto it = begin; it != end; ++it) + LOG ("%d [%d]", it->first, it->second); + lit_implications::const_iterator found = std::lower_bound ( + begin, end, lit_implication{lit, lit}, + [] (const lit_implication &a, const lit_implication &b) { + return a.second < b.second; + }); +#ifndef CADICAL_NDEBUG + auto found2 = std::binary_search ( + begin, end, lit_implication{lit, lit}, + [] (const lit_implication &a, const lit_implication &b) { + return a.second < b.second; + }); +#endif + + if (found < end && found->second == lit) { + CADICAL_assert (found2 == (found < end)); + return found; + } + return end; +} + +void Closure::search_condeq (int lit, int pos_lit, + lit_implications::const_iterator pos_begin, + lit_implications::const_iterator pos_end, + int neg_lit, + lit_implications::const_iterator neg_begin, + lit_implications::const_iterator neg_end, + lit_equivalences &condeq) { + CADICAL_assert (neg_lit == -pos_lit); + CADICAL_assert (pos_begin < pos_end); + CADICAL_assert (neg_begin < neg_end); + CADICAL_assert (pos_begin->first == pos_lit); + CADICAL_assert (neg_begin->first == neg_lit); + CADICAL_assert (pos_end <= neg_begin || neg_end <= pos_begin); + for (lit_implications::const_iterator p = pos_begin; p != pos_end; p++) { + const int other = p->second; + const int not_other = -other; + const lit_implications::const_iterator other_clause = + find_lit_implication_second_literal (not_other, neg_begin, neg_end); + CADICAL_assert (std::find (begin (*p->clause), end (*p->clause), lit) != + end (*p->clause)); + if (other_clause != neg_end) { + CADICAL_assert (std::find (begin (*other_clause->clause), + end (*other_clause->clause), + neg_lit) != end (*other_clause->clause)); + CADICAL_assert (std::find (begin (*p->clause), end (*p->clause), other) != + end (*p->clause)); + lit_equivalence equivalence (neg_lit, other_clause->clause, other, + p->clause); + if (pos_lit > 0) { + equivalence.negate_both (); + } + if (internal->lrat) + equivalence.check_invariant (); + LOG ("found conditional %d equivalence %d = %d", lit, + equivalence.first, equivalence.second); + CADICAL_assert (equivalence.first > 0); + CADICAL_assert (internal->vlit (equivalence.first) < + internal->vlit (equivalence.second)); + check_ternary (lit, neg_lit, -other); + check_ternary (lit, -neg_lit, other); + condeq.push_back (equivalence); + if (equivalence.second < 0) { + lit_equivalence inverse_equivalence = + equivalence.swap ().negate_both (); + condeq.push_back (inverse_equivalence); + if (internal->lrat) + inverse_equivalence.check_invariant (); + } else { + lit_equivalence inverse_equivalence = equivalence.swap (); + condeq.push_back (inverse_equivalence); + if (internal->lrat) + inverse_equivalence.check_invariant (); + } + } + } +#ifndef LOGGING + (void) lit; +#endif +} + +void Closure::extract_condeq_pairs (int lit, lit_implications &condbin, + lit_equivalences &condeq) { + const lit_implications::const_iterator begin = condbin.cbegin (); + const lit_implications::const_iterator end = condbin.cend (); + lit_implications::const_iterator pos_begin = begin; + int next_lit = 0; + +#ifdef LOGGING + for (const auto &pair : condbin) + LOG ("unsorted conditional %d equivalence %d = %d", lit, pair.first, + pair.second); +#endif + LOG ("searching for first positive literal for lit %d", lit); + for (;;) { + if (pos_begin == end) + return; + next_lit = pos_begin->first; + LOG ("checking %d", next_lit); + if (next_lit > 0) + break; + pos_begin++; + } + + for (;;) { + CADICAL_assert (pos_begin != end); + CADICAL_assert (next_lit == pos_begin->first); + CADICAL_assert (next_lit > 0); + const int pos_lit = next_lit; + lit_implications::const_iterator pos_end = pos_begin + 1; + LOG ("searching for first other literal after finding lit %d", + next_lit); + for (;;) { + if (pos_end == end) + return; + next_lit = pos_end->first; + if (next_lit != pos_lit) + break; + pos_end++; + } + CADICAL_assert (pos_end != end); + CADICAL_assert (next_lit == pos_end->first); + const int neg_lit = -pos_lit; + if (next_lit != neg_lit) { + if (next_lit < 0) { + pos_begin = pos_end + 1; + LOG ("next_lit %d < 0", next_lit); + for (;;) { + if (pos_begin == end) + return; + next_lit = pos_begin->first; + if (next_lit > 0) + break; + pos_begin++; + } + } else + pos_begin = pos_end; + continue; + } + const lit_implications::const_iterator neg_begin = pos_end; + lit_implications::const_iterator neg_end = neg_begin + 1; + while (neg_end != end) { + next_lit = neg_end->first; + if (next_lit != neg_lit) + break; + neg_end++; + } +#ifdef LOGGING + for (lit_implications::const_iterator p = pos_begin; p != pos_end; p++) + LOG ("conditional %d binary clause %d %d with positive %d", (lit), + (p->first), (p->second), (pos_lit)); + for (lit_implications::const_iterator p = neg_begin; p != neg_end; p++) + LOG ("conditional %d binary clause %d %d with negative %d", (lit), + (p->first), (p->second), (neg_lit)); +#endif + const size_t pos_size = pos_end - pos_begin; + const size_t neg_size = neg_end - neg_begin; + if (pos_size <= neg_size) { + LOG ("searching negation of %zu conditional binary clauses " + "with positive %d in %zu conditional binary clauses with %d", + pos_size, (pos_lit), neg_size, (neg_lit)); + search_condeq (lit, pos_lit, pos_begin, pos_end, neg_lit, neg_begin, + neg_end, condeq); + } else { + LOG ("searching negation of %zu conditional binary clauses " + "with negative %d in %zu conditional binary clauses with %d", + neg_size, (neg_lit), pos_size, (pos_lit)); + search_condeq (lit, neg_lit, neg_begin, neg_end, pos_lit, pos_begin, + pos_end, condeq); + } + if (neg_end == end) + return; + CADICAL_assert (next_lit == neg_end->first); + if (next_lit < 0) { + pos_begin = neg_end + 1; + for (;;) { + if (pos_begin == end) + return; + next_lit = pos_begin->first; + if (next_lit > 0) + break; + pos_begin++; + } + } else + pos_begin = neg_end; + } +} + +void Closure::find_conditional_equivalences (int lit, + lit_implications &condbin, + lit_equivalences &condeq) { + CADICAL_assert (condbin.empty ()); + CADICAL_assert (condeq.empty ()); + CADICAL_assert (internal->occs (lit).size () > 1); + copy_conditional_equivalences (lit, condbin); + MSORT (internal->opts.radixsortlim, begin (condbin), end (condbin), + litpair_rank (this->internal), litpair_smaller (this->internal)); + + extract_condeq_pairs (lit, condbin, condeq); + MSORT (internal->opts.radixsortlim, begin (condbin), end (condbin), + litpair_rank (this->internal), litpair_smaller (this->internal)); + +#ifdef LOGGING + for (auto pair : condeq) + LOG ("sorted conditional %d equivalence %d = %d", lit, pair.first, + pair.second); + LOG ("found %zu conditional %d equivalences", condeq.size (), lit); + +#endif +} + +void Closure::merge_condeq (int cond, lit_equivalences &condeq, + lit_equivalences ¬_condeq) { + LOG ("merging cond for literal %d", cond); + auto q = begin (not_condeq); + const auto end_not_condeq = end (not_condeq); + for (auto p : condeq) { + const int lhs = p.first; + const int then_lit = p.second; + if (internal->lrat) + p.check_invariant (); + CADICAL_assert (lhs > 0); + while (q != end_not_condeq && q->first < lhs) + ++q; + while (q != end_not_condeq && q->first == lhs) { + LOG ("looking when %d at p= %d %d", cond, p.first, p.second); + LOG ("looking when %d at %d %d", -cond, q->first, q->second); + lit_equivalence not_cond_pair = *q++; + const int else_lit = not_cond_pair.second; + std::vector<LitClausePair> clauses; + if (internal->lrat) { + // The then/else literal is the second of the pair, hence the swap + // of the reasons + CADICAL_assert (p.first_clause && p.second_clause); + CADICAL_assert (not_cond_pair.first_clause && not_cond_pair.second_clause); + LOG (p.second_clause, "pairing %d", then_lit); + LOG (p.first_clause, "pairing %d", -then_lit); + LOG (not_cond_pair.second_clause, "pairing %d", else_lit); + LOG (not_cond_pair.first_clause, "pairing %d", -else_lit); + clauses.push_back (LitClausePair (then_lit, p.second_clause)); + clauses.push_back (LitClausePair (-then_lit, p.first_clause)); + clauses.push_back ( + LitClausePair (else_lit, not_cond_pair.second_clause)); + clauses.push_back ( + LitClausePair (-else_lit, not_cond_pair.first_clause)); + if (internal->lrat) + not_cond_pair.check_invariant (); + } + new_ite_gate (lhs, cond, then_lit, else_lit, std::move (clauses)); + if (internal->unsat) + return; + } + } +} + +void Closure::extract_ite_gates_of_literal (int lit) { + LOG ("search for ITE for literal %d ", lit); + find_conditional_equivalences (lit, condbin[0], condeq[0]); + if (!condeq[0].empty ()) { + find_conditional_equivalences (-lit, condbin[1], condeq[1]); + if (!condeq[1].empty ()) { + if (lit < 0) + merge_condeq (-lit, condeq[0], condeq[1]); + else + merge_condeq (lit, condeq[1], condeq[0]); + } + } + + condbin[0].clear (); + condbin[1].clear (); + condeq[0].clear (); + condeq[1].clear (); +} + +void Closure::extract_ite_gates_of_variable (int idx) { + const int lit = idx; + const int not_lit = -idx; + + auto lit_watches = internal->occs (lit); + auto not_lit_watches = internal->occs (not_lit); + const size_t size_lit_watches = lit_watches.size (); + const size_t size_not_lit_watches = not_lit_watches.size (); + if (size_lit_watches <= size_not_lit_watches) { + if (size_lit_watches > 1) + extract_ite_gates_of_literal (lit); + } else { + if (size_not_lit_watches > 1) + extract_ite_gates_of_literal (not_lit); + } +} + +void Closure::extract_ite_gates () { + CADICAL_assert (!full_watching); + if (!internal->opts.congruenceite) + return; + START (extractites); + std::vector<ClauseSize> candidates; + + init_ite_gate_extraction (candidates); + + for (auto idx : internal->vars) { + if (internal->flags (idx).active ()) { + extract_ite_gates_of_variable (idx); + if (internal->unsat) + break; + } + } + // Kissat has an alternative version MERGE_CONDITIONAL_EQUIVALENCES + reset_ite_gate_extraction (); + STOP (extractites); +} + +/*------------------------------------------------------------------------*/ +void Closure::extract_gates () { + START (extract); + extract_and_gates (); + CADICAL_assert (internal->unsat || lrat_chain.empty ()); + CADICAL_assert (internal->unsat || chain.empty ()); + if (internal->unsat || internal->terminated_asynchronously ()) { + STOP (extract); + return; + } + extract_xor_gates (); + CADICAL_assert (internal->unsat || lrat_chain.empty ()); + CADICAL_assert (internal->unsat || chain.empty ()); + + if (internal->unsat || internal->terminated_asynchronously ()) { + STOP (extract); + return; + } + extract_ite_gates (); + STOP (extract); +} + +/*------------------------------------------------------------------------*/ +// top level function to extract gate +bool Internal::extract_gates () { + if (unsat) + return false; + if (!opts.congruence) + return false; + if (level) + backtrack (); + if (!propagate ()) { + learn_empty_clause (); + return false; + } + if (congruence_delay.bumpreasons.limit) { + LOG ("delaying congruence %" PRId64 " more times", + congruence_delay.bumpreasons.limit); + congruence_delay.bumpreasons.limit--; + return false; + } + + // to remove false literals from clauses + // It makes the technique stronger as long clauses + // can become binary / ternary + // garbage_collection (); + + const int64_t old = stats.congruence.congruent; + const int old_merged = stats.congruence.congruent; + + // congruencebinary is already doing it (and more actually) + if (!internal->opts.congruencebinaries) { + const bool dedup = opts.deduplicate; + opts.deduplicate = true; + mark_duplicated_binary_clauses_as_garbage (); + opts.deduplicate = dedup; + } + ++stats.congruence.rounds; + clear_watches (); + // connect_binary_watches (); + + START_SIMPLIFIER (congruence, CONGRUENCE); + Closure closure (this); + + closure.init_closure (); + CADICAL_assert (unsat || closure.chain.empty ()); + CADICAL_assert (unsat || lrat_chain.empty ()); + closure.extract_binaries (); + CADICAL_assert (unsat || closure.chain.empty ()); + CADICAL_assert (unsat || lrat_chain.empty ()); + closure.extract_gates (); + CADICAL_assert (unsat || closure.chain.empty ()); + CADICAL_assert (unsat || lrat_chain.empty ()); + internal->clear_watches (); + internal->connect_watches (); + closure.reset_extraction (); + + if (!unsat) { + closure.find_units (); + CADICAL_assert (unsat || closure.chain.empty ()); + CADICAL_assert (unsat || lrat_chain.empty ()); + if (!internal->unsat) { + closure.find_equivalences (); + CADICAL_assert (unsat || closure.chain.empty ()); + CADICAL_assert (unsat || lrat_chain.empty ()); + + if (!unsat) { + const int propagated = closure.propagate_units_and_equivalences (); + CADICAL_assert (unsat || closure.chain.empty ()); + if (!unsat && propagated) + closure.forward_subsume_matching_clauses (); + } + } + } + + closure.reset_closure (); + internal->clear_watches (); + internal->connect_watches (); + if (!internal->unsat) { + propagated2 = propagated = 0; + propagate (); + } + CADICAL_assert (closure.new_unwatched_binary_clauses.empty ()); + internal->reset_occs (); + internal->reset_noccs (); + CADICAL_assert (!internal->occurring ()); + CADICAL_assert (lrat_chain.empty ()); + + const int64_t new_merged = stats.congruence.congruent; + +#ifndef CADICAL_QUIET + phase ("congruence-phase", stats.congruence.rounds, "merged %ld literals", + new_merged - old_merged); +#endif + if (!unsat && !internal->propagate ()) + unsat = true; + + STOP_SIMPLIFIER (congruence, CONGRUENCE); + report ('c', !opts.reportall && !(stats.congruence.congruent - old)); +#ifndef CADICAL_NDEBUG + size_t watched = 0; + for (auto v : vars) { + for (auto sgn = -1; sgn <= 1; sgn += 2) { + const int lit = v * sgn; + for (auto w : watches (lit)) { + if (w.binary ()) + CADICAL_assert (!w.clause->garbage); + if (w.clause->garbage) + continue; + ++watched; + LOG (w.clause, "watched"); + } + } + } + LOG ("and now the clauses:"); + size_t nb_clauses = 0; + for (auto c : clauses) { + if (c->garbage) + continue; + LOG (c, "watched"); + ++nb_clauses; + } + CADICAL_assert (watched == nb_clauses * 2); +#endif + CADICAL_assert (!internal->occurring ()); + + if (new_merged == old_merged) { + congruence_delay.bumpreasons.interval++; + } else { + congruence_delay.bumpreasons.interval /= 2; + } + + LOG ("delay congruence internal %" PRId64, + congruence_delay.bumpreasons.interval); + congruence_delay.bumpreasons.limit = + congruence_delay.bumpreasons.interval; + + return new_merged != old_merged; +} + +} // namespace CaDiCaL + +ABC_NAMESPACE_IMPL_END diff --git a/src/sat/cadical/cadical_constrain.cpp b/src/sat/cadical/cadical_constrain.cpp new file mode 100644 index 0000000000..47f14e3d97 --- /dev/null +++ b/src/sat/cadical/cadical_constrain.cpp @@ -0,0 +1,70 @@ +#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 diff --git a/src/sat/cadical/cadical_contract.cpp b/src/sat/cadical/cadical_contract.cpp new file mode 100644 index 0000000000..7485ff4aab --- /dev/null +++ b/src/sat/cadical/cadical_contract.cpp @@ -0,0 +1,33 @@ +#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 diff --git a/src/sat/cadical/cadical_cover.cpp b/src/sat/cadical/cadical_cover.cpp new file mode 100644 index 0000000000..0d3253e98e --- /dev/null +++ b/src/sat/cadical/cadical_cover.cpp @@ -0,0 +1,710 @@ +#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 diff --git a/src/sat/cadical/cadical_decide.cpp b/src/sat/cadical/cadical_decide.cpp new file mode 100644 index 0000000000..0874cffd74 --- /dev/null +++ b/src/sat/cadical/cadical_decide.cpp @@ -0,0 +1,258 @@ +#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 diff --git a/src/sat/cadical/cadical_decompose.cpp b/src/sat/cadical/cadical_decompose.cpp new file mode 100644 index 0000000000..9081321b4b --- /dev/null +++ b/src/sat/cadical/cadical_decompose.cpp @@ -0,0 +1,739 @@ +#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 diff --git a/src/sat/cadical/cadical_deduplicate.cpp b/src/sat/cadical/cadical_deduplicate.cpp new file mode 100644 index 0000000000..49d26a5717 --- /dev/null +++ b/src/sat/cadical/cadical_deduplicate.cpp @@ -0,0 +1,176 @@ +#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 diff --git a/src/sat/cadical/cadical_definition.cpp b/src/sat/cadical/cadical_definition.cpp new file mode 100644 index 0000000000..7c8fe4fbbb --- /dev/null +++ b/src/sat/cadical/cadical_definition.cpp @@ -0,0 +1,289 @@ +#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 diff --git a/src/sat/cadical/cadical_drattracer.cpp b/src/sat/cadical/cadical_drattracer.cpp new file mode 100644 index 0000000000..eb73b4c95a --- /dev/null +++ b/src/sat/cadical/cadical_drattracer.cpp @@ -0,0 +1,159 @@ +#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 diff --git a/src/sat/cadical/cadical_elim.cpp b/src/sat/cadical/cadical_elim.cpp new file mode 100644 index 0000000000..951dfee742 --- /dev/null +++ b/src/sat/cadical/cadical_elim.cpp @@ -0,0 +1,1178 @@ +#include "global.h" + +#include "internal.hpp" + +ABC_NAMESPACE_IMPL_START + +namespace CaDiCaL { + +/*------------------------------------------------------------------------*/ + +// Implements a variant of bounded variable elimination as originally +// described in our SAT'05 paper introducing 'SATeLite'. This is an +// inprocessing version, i.e., it is interleaved with search and triggers +// subsumption and strengthening, blocked and covered clause elimination +// during elimination rounds. It focuses only those variables which +// occurred in removed irredundant clauses since the last time an +// elimination round was run. By bounding the maximum resolvent size we can +// run each elimination round until completion. See the code of 'elim' for +// how elimination rounds are interleaved with blocked clause elimination +// and subsumption (which in turn also calls vivification and transitive +// reduction of the binary implication graph). + +/*------------------------------------------------------------------------*/ + +inline double Internal::compute_elim_score (unsigned lit) { + CADICAL_assert (1 <= lit), CADICAL_assert (lit <= (unsigned) max_var); + const unsigned uidx = 2 * lit; + const double pos = internal->ntab[uidx]; + const double neg = internal->ntab[uidx + 1]; + if (!pos) + return -neg; + if (!neg) + return -pos; + double sum = 0, prod = 0; + if (opts.elimsum) + sum = opts.elimsum * (pos + neg); + if (opts.elimprod) + prod = opts.elimprod * (pos * neg); + return prod + sum; +} + +/*------------------------------------------------------------------------*/ + +bool elim_more::operator() (unsigned a, unsigned b) { + const auto s = internal->compute_elim_score (a); + const auto t = internal->compute_elim_score (b); + if (s > t) + return true; + if (s < t) + return false; + return a > b; +} + +/*------------------------------------------------------------------------*/ + +// Note that the new fast subsumption algorithm implemented in 'subsume' +// does not distinguish between irredundant and redundant clauses and is +// also run during search to strengthen and remove 'sticky' redundant +// clauses but also irredundant ones. So beside learned units during search +// or as consequence of other preprocessors, these subsumption rounds during +// search can remove (irredundant) clauses (and literals), which in turn +// might make new bounded variable elimination possible. This is tested +// in the 'bool ineliminating ()' guard. + +bool Internal::ineliminating () { + + if (!opts.elim) + return false; + if (!preprocessing && !opts.inprocessing) + return false; + if (preprocessing) + CADICAL_assert (lim.preprocessing); + + // Respect (increasing) conflict limit. + // + if (lim.elim >= stats.conflicts) + return false; + + // Wait until there are new units or new removed variables + // (in removed or shrunken irredundant clauses and thus marked). + // + if (last.elim.fixed < stats.all.fixed) + return true; + if (last.elim.marked < stats.mark.elim) + return true; + + // VERBOSE (3, "elim not scheduled due to fixpoint"); + return false; +} + +/*------------------------------------------------------------------------*/ + +// Update the global elimination schedule after adding or removing a clause. + +void Internal::elim_update_added_clause (Eliminator &eliminator, + Clause *c) { + CADICAL_assert (!c->redundant); + ElimSchedule &schedule = eliminator.schedule; + for (const auto &lit : *c) { + if (!active (lit)) + continue; + occs (lit).push_back (c); + if (frozen (lit)) + continue; + noccs (lit)++; + const int idx = abs (lit); + if (schedule.contains (idx)) + schedule.update (idx); + } +} + +void Internal::elim_update_removed_lit (Eliminator &eliminator, int lit) { + if (!active (lit)) + return; + if (frozen (lit)) + return; + int64_t &score = noccs (lit); + CADICAL_assert (score > 0); + score--; + const int idx = abs (lit); + ElimSchedule &schedule = eliminator.schedule; + if (schedule.contains (idx)) + schedule.update (idx); + else { + LOG ("rescheduling %d for elimination after removing clause", idx); + schedule.push_back (idx); + } +} + +void Internal::elim_update_removed_clause (Eliminator &eliminator, + Clause *c, int except) { + CADICAL_assert (!c->redundant); + for (const auto &lit : *c) { + if (lit == except) + continue; + CADICAL_assert (lit != -except); + elim_update_removed_lit (eliminator, lit); + } +} + +/*------------------------------------------------------------------------*/ + +// Since we do not have watches we have to do our own unit propagation +// during elimination as soon we find a unit clause. This finds new units +// and also marks clauses satisfied by those units as garbage immediately. + +void Internal::elim_propagate (Eliminator &eliminator, int root) { + CADICAL_assert (val (root) > 0); + vector<int> work; + size_t i = 0; + work.push_back (root); + while (i < work.size ()) { + int lit = work[i++]; + LOG ("elimination propagation of %d", lit); + CADICAL_assert (val (lit) > 0); + const Occs &ns = occs (-lit); + for (const auto &c : ns) { + if (c->garbage) + continue; + int unit = 0, satisfied = 0; + for (const auto &other : *c) { + const signed char tmp = val (other); + if (tmp < 0) + continue; + if (tmp > 0) { + satisfied = other; + break; + } + if (unit) + unit = INT_MIN; + else + unit = other; + } + if (satisfied) { + LOG (c, "elimination propagation of %d finds %d satisfied", lit, + satisfied); + elim_update_removed_clause (eliminator, c, satisfied); + mark_garbage (c); + } else if (!unit) { + LOG ("empty clause during elimination propagation of %d", lit); + // need to set conflict = c for lrat + conflict = c; + learn_empty_clause (); + conflict = 0; + break; + } else if (unit != INT_MIN) { + LOG ("new unit %d during elimination propagation of %d", unit, lit); + build_chain_for_units (unit, c, 0); + assign_unit (unit); + work.push_back (unit); + } + } + if (unsat) + break; + const Occs &ps = occs (lit); + for (const auto &c : ps) { + if (c->garbage) + continue; + LOG (c, "elimination propagation of %d produces satisfied", lit); + elim_update_removed_clause (eliminator, c, lit); + mark_garbage (c); + } + } +} + +/*------------------------------------------------------------------------*/ + +// On-the-fly self-subsuming resolution during variable elimination is due +// to HyoJung Han, Fabio Somenzi, SAT'09. Basically while resolving two +// clauses we test the resolvent to be smaller than one of the antecedents. +// If this is the case the pivot can be removed from the antecedent +// on-the-fly and the resolution can be skipped during elimination. + +void Internal::elim_on_the_fly_self_subsumption (Eliminator &eliminator, + Clause *c, int pivot) { + LOG (c, "pivot %d on-the-fly self-subsuming resolution", pivot); + stats.elimotfstr++; + stats.strengthened++; + CADICAL_assert (clause.empty ()); + for (const auto &lit : *c) { + if (lit == pivot) + continue; + const signed char tmp = val (lit); + CADICAL_assert (tmp <= 0); + if (tmp < 0) + continue; + clause.push_back (lit); + } + Clause *r = new_resolved_irredundant_clause (); + elim_update_added_clause (eliminator, r); + clause.clear (); + lrat_chain.clear (); + elim_update_removed_clause (eliminator, c, pivot); + mark_garbage (c); +} + +/*------------------------------------------------------------------------*/ + +// Resolve two clauses on the pivot literal 'pivot', which is assumed to +// occur in opposite phases in 'c' and 'd'. The actual resolvent is stored +// in the temporary global 'clause' if it is not redundant. It is +// considered redundant if one of the clauses is already marked as garbage +// it is root level satisfied, the resolvent is empty, a unit, or produces a +// self-subsuming resolution, which results in the pivot to be removed from +// at least one of the antecedents. + +// Note that current root level assignments are taken into account, i.e., by +// removing root level falsified literals. The function returns true if the +// resolvent is not redundant and for instance has to be taken into account +// during bounded variable elimination. + +// Detected units are immediately assigned and in case the last argument is +// true also propagated eagerly in a elimination specific propagation +// routine, which not only finds units but also updates the schedule. + +// When this function is called during computation of the number of +// non-trivial (or non-satisfied) resolvents we can eagerly propagate units. +// But during actually adding the resolvents this results in problems as we +// found one rare test case '../test/trace/reg0056.trace' (out of billions), +// where the pivot itself was assigned during such a propagation while +// adding resolvents and lead to pushing a clause to the reconstruction +// stack that later flipped the value of the pivot (while all other literals +// in that clause were unit implied too). Not pushing the pivot clauses to +// the reconstruction stack produced a wrong model too. Our fix is to only +// eagerly propagate during computation of the number of resolvents and +// otherwise delay propagation until the end of elimination (which is less +// precise regarding scheduling but very rarely happens). + +bool Internal::resolve_clauses (Eliminator &eliminator, Clause *c, + int pivot, Clause *d, + const bool propagate_eagerly) { + + CADICAL_assert (!c->redundant); + CADICAL_assert (!d->redundant); + + stats.elimres++; + + if (c->garbage || d->garbage) + return false; + if (c->size > d->size) { + pivot = -pivot; + swap (c, d); + } + + CADICAL_assert (!level); + CADICAL_assert (clause.empty ()); + + int satisfied = 0; // Contains this satisfying literal. + int tautological = 0; // Clashing literal if tautological. + + int s = 0; // Actual literals from 'c'. + int t = 0; // Actual literals from 'd'. + + // First determine whether the first antecedent is satisfied, add its + // literals to 'clause' and mark them (except for 'pivot'). + // + for (const auto &lit : *c) { + if (lit == pivot) { + s++; + continue; + } + CADICAL_assert (lit != -pivot); + const signed char tmp = val (lit); + if (tmp > 0) { + satisfied = lit; + break; + } else if (tmp < 0) { + if (!lrat) + continue; + 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); + continue; + } else + mark (lit), clause.push_back (lit), s++; + } + if (satisfied) { + LOG (c, "satisfied by %d antecedent", satisfied); + elim_update_removed_clause (eliminator, c, satisfied); + mark_garbage (c); + clause.clear (); + lrat_chain.clear (); + clear_analyzed_literals (); + unmark (c); + return false; + } + + // Then determine whether the second antecedent is satisfied, add its + // literal to 'clause' and check whether a clashing literal is found, such + // that the resolvent would be tautological. + // + for (const auto &lit : *d) { + if (lit == -pivot) { + t++; + continue; + } + CADICAL_assert (lit != pivot); + signed char tmp = val (lit); + if (tmp > 0) { + satisfied = lit; + break; + } else if (tmp < 0) { + if (!lrat) + continue; + 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); + continue; + } else if ((tmp = marked (lit)) < 0) { + tautological = lit; + break; + } else if (!tmp) + clause.push_back (lit), t++; + else + CADICAL_assert (tmp > 0), t++; + } + + clear_analyzed_literals (); + unmark (c); + const int64_t size = clause.size (); + + if (lrat) { + lrat_chain.push_back (d->id); + lrat_chain.push_back (c->id); + } + + if (satisfied) { + LOG (d, "satisfied by %d antecedent", satisfied); + elim_update_removed_clause (eliminator, d, satisfied); + mark_garbage (d); + clause.clear (); + lrat_chain.clear (); + return false; + } + + LOG (c, "first antecedent"); + LOG (d, "second antecedent"); + + if (tautological) { + clause.clear (); + LOG ("resolvent tautological on %d", tautological); + lrat_chain.clear (); + return false; + } + + if (!size) { + clause.clear (); + LOG ("empty resolvent"); + learn_empty_clause (); // already clears lrat_chain. + return false; + } + + if (size == 1) { + int unit = clause[0]; + LOG ("unit resolvent %d", unit); + clause.clear (); + assign_unit (unit); // already clears lrat_chain. + if (propagate_eagerly) + elim_propagate (eliminator, unit); + return false; + } + + LOG (clause, "resolvent"); + CADICAL_assert (!lrat || !lrat_chain.empty ()); + + // Double self-subsuming resolution. The clauses 'c' and 'd' are + // identical except for the pivot which occurs in different phase. The + // resolvent subsumes both antecedents. + + if (s > size && t > size) { + CADICAL_assert (s == size + 1); + CADICAL_assert (t == size + 1); + clause.clear (); + // LRAT is c + d (+ eventual units) + elim_on_the_fly_self_subsumption (eliminator, c, pivot); + LOG (d, "double pivot %d on-the-fly self-subsuming resolution", -pivot); + stats.elimotfsub++; + stats.subsumed++; + elim_update_removed_clause (eliminator, d, -pivot); + mark_garbage (d); + return false; + } + + // Single self-subsuming resolution: The pivot can be removed from 'c', + // which is implemented by adding a clause which is the same as 'c' but + // with 'pivot' removed and then marking 'c' as garbage. + + if (s > size) { + CADICAL_assert (s == size + 1); + clause.clear (); + // LRAT is c + d (+ eventual units) + elim_on_the_fly_self_subsumption (eliminator, c, pivot); + return false; + } + + // Same single self-subsuming resolution situation, but only for 'd'. + + if (t > size) { + CADICAL_assert (t == size + 1); + clause.clear (); + // LRAT is c + d (+ eventual units) -> same. + elim_on_the_fly_self_subsumption (eliminator, d, -pivot); + return false; + } + if (propagate_eagerly) + lrat_chain.clear (); + return true; +} + +/*------------------------------------------------------------------------*/ + +// 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::elim_resolvents_are_bounded (Eliminator &eliminator, + int pivot) { + const bool substitute = !eliminator.gates.empty (); + const bool resolve_gates = eliminator.definition_unit; + if (substitute) + LOG ("trying to substitute %d", pivot); + + stats.elimtried++; + + CADICAL_assert (!unsat); + CADICAL_assert (active (pivot)); + + const Occs &ps = occs (pivot); + const Occs &ns = occs (-pivot); + const int64_t pos = ps.size (); + const int64_t neg = ns.size (); + if (!pos || !neg) + return lim.elimbound >= 0; + const int64_t bound = pos + neg + lim.elimbound; + + LOG ("checking number resolvents on %d bounded by " + "%" PRId64 " = %" PRId64 " + %" PRId64 " + %" PRId64, + pivot, bound, pos, neg, lim.elimbound); + + // 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_gates && substitute && c->gate == d->gate) + continue; + stats.elimrestried++; + 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.elimclslim) { + 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::elim_add_resolvents (Eliminator &eliminator, + int pivot) { + + const bool substitute = !eliminator.gates.empty (); + const bool resolve_gates = eliminator.definition_unit; + if (substitute) { + LOG ("substituting pivot %d by resolving with %zd gate clauses", pivot, + eliminator.gates.size ()); + stats.elimsubst++; + } + switch (eliminator.gatetype) { + case EQUI: + stats.eliminated_equi++; + break; + case AND: + stats.eliminated_and++; + break; + case ITE: + stats.eliminated_ite++; + break; + case XOR: + stats.eliminated_xor++; + break; + case DEF: + stats.eliminated_def++; + break; + default: + CADICAL_assert (eliminator.gatetype == NO); + } + + 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_gates && substitute && c->gate == d->gate) + 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); +} + +/*------------------------------------------------------------------------*/ + +// Remove clauses with 'pivot' and '-pivot' by marking them as garbage and +// push them on the extension stack. + +void Internal::mark_eliminated_clauses_as_garbage ( + Eliminator &eliminator, int pivot, bool &deleted_binary_clause) { + CADICAL_assert (!unsat); + + LOG ("marking irredundant clauses with %d as garbage", pivot); + + const int64_t substitute = eliminator.gates.size (); + if (substitute) + LOG ("pushing %" PRId64 " gate clauses on extension stack", substitute); +#ifndef CADICAL_NDEBUG + int64_t pushed = 0; +#endif + Occs &ps = occs (pivot); + for (const auto &c : ps) { + if (c->garbage) + continue; + CADICAL_assert (!c->redundant); + if (!substitute || c->gate) { + if (proof) + proof->weaken_minus (c); + if (c->size == 2) + deleted_binary_clause = true; + external->push_clause_on_extension_stack (c, pivot); +#ifndef CADICAL_NDEBUG + pushed++; +#endif + } + mark_garbage (c); + elim_update_removed_clause (eliminator, c, pivot); + } + erase_occs (ps); + + LOG ("marking irredundant clauses with %d as garbage", -pivot); + + Occs &ns = occs (-pivot); + for (const auto &d : ns) { + if (d->garbage) + continue; + CADICAL_assert (!d->redundant); + if (!substitute || d->gate) { + if (proof) + proof->weaken_minus (d); + if (d->size == 2) + deleted_binary_clause = true; + external->push_clause_on_extension_stack (d, -pivot); +#ifndef CADICAL_NDEBUG + pushed++; +#endif + } + mark_garbage (d); + elim_update_removed_clause (eliminator, d, -pivot); + } + erase_occs (ns); + + if (substitute) + CADICAL_assert (pushed <= substitute); + + // Unfortunately, we can not use the trick by Niklas Soerensson anymore, + // which avoids saving all clauses on the extension stack. This would + // break our new incremental 'restore' logic. +} + +/*------------------------------------------------------------------------*/ + +// Try to eliminate 'pivot' by bounded variable elimination. +void Internal::try_to_eliminate_variable (Eliminator &eliminator, int pivot, + bool &deleted_binary_clause) { + + if (!active (pivot)) + return; + CADICAL_assert (!frozen (pivot)); + + // First flush garbage clauses. + // + int64_t pos = flush_occs (pivot); + int64_t neg = flush_occs (-pivot); + + 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); + + if (pos && neg > opts.elimocclim) { + LOG ("too many occurrences thus not eliminated %d", pivot); + CADICAL_assert (!eliminator.schedule.contains (abs (pivot))); + return; + } + + 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 (pos) + find_gate_clauses (eliminator, pivot); + + if (!unsat && !val (pivot)) { + if (elim_resolvents_are_bounded (eliminator, pivot)) { + LOG ("number of resolvents on %d are bounded", pivot); + elim_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); +} + +/*------------------------------------------------------------------------*/ + +void Internal:: + mark_redundant_clauses_with_eliminated_variables_as_garbage () { + for (const auto &c : clauses) { + if (c->garbage || !c->redundant) + continue; + bool clean = true; + for (const auto &lit : *c) { + Flags &f = flags (lit); + if (f.eliminated ()) { + clean = false; + break; + } + if (f.pure ()) { + clean = false; + break; + } + } + if (!clean) + mark_garbage (c); + } +} + +/*------------------------------------------------------------------------*/ + +// 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::elim_round (bool &completed, bool &deleted_binary_clause) { + + CADICAL_assert (opts.elim); + CADICAL_assert (!unsat); + + START_SIMPLIFIER (elim, ELIM); + stats.elimrounds++; + + int64_t marked_before = last.elim.marked; + last.elim.marked = stats.mark.elim; + 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 ("elim-round", stats.elimrounds, + "limit of %" PRId64 " resolutions", delta); + + resolution_limit = stats.elimres + delta; + } else { + PHASE ("elim-round", stats.elimrounds, "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 ("elim-round", stats.elimrounds, + "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_eliminate_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 (); + + if (!completed) + last.elim.marked = marked_before; + + PHASE ("elim-round", stats.elimrounds, + "tried to eliminate %" PRId64 " variables %.0f%% (%zd remain)", + tried, percent (tried, scheduled), schedule.size ()); + + schedule.erase (); + + // Collect potential literal clause instantiation pairs, which needs full + // occurrence lists and thus we have it here before resetting them. + // + Instantiator instantiator; + if (!unsat && !terminated_asynchronously () && opts.instantiate) + collect_instantiation_candidates (instantiator); + + 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; +#ifndef CADICAL_QUIET + int64_t resolutions = stats.elimres - old_resolutions; + PHASE ("elim-round", stats.elimrounds, + "eliminated %d variables %.0f%% in %" PRId64 " resolutions", + eliminated, percent (eliminated, scheduled), resolutions); +#endif + + last.elim.subsumephases = stats.subsumephases; + const int units = stats.all.fixed - old_fixed; + report ('e', !opts.reportall && !(eliminated + units)); + STOP_SIMPLIFIER (elim, ELIM); + + if (!unsat && !terminated_asynchronously () && + instantiator) // Do we have candidate pairs? + instantiate (instantiator); + + return eliminated; // non-zero if successful +} + +/*------------------------------------------------------------------------*/ + +// Increase elimination bound (additional clauses allowed during variable +// elimination), which is triggered if elimination with last bound completed +// (including no new subsumptions). This was pioneered by GlueMiniSAT in +// the SAT Race 2015 and then picked up Chanseok Oh in his COMinisatPS +// solver, which in turn is used in the Maple series of SAT solvers. +// The bound is no increased if the maximum bound is reached. + +void Internal::increase_elimination_bound () { + + if (lim.elimbound >= opts.elimboundmax) + return; + + if (lim.elimbound < 0) + lim.elimbound = 0; + else if (!lim.elimbound) + lim.elimbound = 1; + else + lim.elimbound *= 2; + + if (lim.elimbound > opts.elimboundmax) + lim.elimbound = opts.elimboundmax; + + PHASE ("elim-phase", stats.elimphases, + "new elimination bound %" PRId64 "", lim.elimbound); + + // Now reschedule all active variables for elimination again. + // +#ifdef LOGGING + int count = 0; +#endif + for (auto idx : vars) { + if (!active (idx)) + continue; + if (flags (idx).elim) + continue; + mark_elim (idx); +#ifdef LOGGING + count++; +#endif + } + LOG ("marked %d variables as elimination candidates", count); + + report ('^'); +} + +void Internal::init_citten () { + if (!opts.elimdef) + return; + CADICAL_assert (!citten); + citten = cadical_kitten_init (); +} + +void Internal::reset_citten () { + if (citten) { + cadical_kitten_release (citten); + citten = 0; + } +} + +/*------------------------------------------------------------------------*/ + +void Internal::elim (bool update_limits) { + + if (unsat) + return; + if (level) + backtrack (); + if (!propagate ()) { + learn_empty_clause (); + return; + } + + stats.elimphases++; + PHASE ("elim-phase", stats.elimphases, + "starting at most %d elimination rounds", opts.elimrounds); + + if (external_prop) { + CADICAL_assert (!level); + private_steps = true; + } + +#ifndef CADICAL_QUIET + int old_active_variables = active (); + int old_eliminated = stats.all.eliminated; +#endif + + // Make sure there was a complete subsumption phase since last + // elimination including vivification etc. + // + if (last.elim.subsumephases == stats.subsumephases) + subsume (); + + reset_watches (); // saves lots of memory + + init_citten (); + + // 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 + elim_round (round_complete, deleted_binary_clause); + + if (!round_complete) { + PHASE ("elim-phase", stats.elimphases, "last round %d incomplete %s", + round, eliminated ? "but successful" : "and unsuccessful"); + CADICAL_assert (!phase_complete); + break; + } + + if (round++ >= opts.elimrounds) { + PHASE ("elim-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; + if (block ()) + continue; + if (cover ()) + continue; + + // Was not able to generate new variable elimination candidates after + // variable elimination round, neither through subsumption, nor blocked, + // nor covered clause elimination. + // + PHASE ("elim-phase", stats.elimphases, + "no new variable elimination candidates"); + + CADICAL_assert (round_complete); + phase_complete = true; + } + + if (phase_complete) { + stats.elimcompleted++; + PHASE ("elim-phase", stats.elimphases, + "fully completed elimination %" PRId64 + " at elimination bound %" PRId64 "", + stats.elimcompleted, lim.elimbound); + } else { + PHASE ("elim-phase", stats.elimphases, + "incomplete elimination %" PRId64 + " at elimination bound %" PRId64 "", + stats.elimcompleted + 1, lim.elimbound); + } + + reset_citten (); + 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 (); + } + } + + // If we ran variable elimination until completion we increase the + // variable elimination bound and reschedule elimination of all variables. + // + if (phase_complete) + increase_elimination_bound (); + +#ifndef CADICAL_QUIET + eliminated = stats.all.eliminated - old_eliminated; + PHASE ("elim-phase", stats.elimphases, "eliminated %d variables %.2f%%", + eliminated, percent (eliminated, old_active_variables)); +#endif + + if (external_prop) { + CADICAL_assert (!level); + private_steps = false; + } + + if (!update_limits) + return; + + int64_t delta = scale (opts.elimint * (stats.elimphases + 1)); + lim.elim = stats.conflicts + delta; + + PHASE ("elim-phase", stats.elimphases, + "new limit at %" PRId64 " conflicts after %" PRId64 " conflicts", + lim.elim, delta); + + last.elim.fixed = stats.all.fixed; +} + +} // namespace CaDiCaL + +ABC_NAMESPACE_IMPL_END diff --git a/src/sat/cadical/cadical_elimfast.cpp b/src/sat/cadical/cadical_elimfast.cpp new file mode 100644 index 0000000000..90dacf8c0a --- /dev/null +++ b/src/sat/cadical/cadical_elimfast.cpp @@ -0,0 +1,576 @@ +#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 diff --git a/src/sat/cadical/cadical_ema.cpp b/src/sat/cadical/cadical_ema.cpp new file mode 100644 index 0000000000..e8faf5d763 --- /dev/null +++ b/src/sat/cadical/cadical_ema.cpp @@ -0,0 +1,101 @@ +#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 diff --git a/src/sat/cadical/cadical_extend.cpp b/src/sat/cadical/cadical_extend.cpp new file mode 100644 index 0000000000..c549e90fa2 --- /dev/null +++ b/src/sat/cadical/cadical_extend.cpp @@ -0,0 +1,287 @@ +#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 diff --git a/src/sat/cadical/cadical_external.cpp b/src/sat/cadical/cadical_external.cpp new file mode 100644 index 0000000000..67d1f918d8 --- /dev/null +++ b/src/sat/cadical/cadical_external.cpp @@ -0,0 +1,1028 @@ +#include "global.h" + +#include "internal.hpp" +#include <cstdint> + +ABC_NAMESPACE_IMPL_START + +namespace CaDiCaL { + +External::External (Internal *i) + : internal (i), max_var (0), vsize (0), extended (false), + concluded (false), terminator (0), learner (0), fixed_listener (0), + propagator (0), solution (0), vars (max_var) { + CADICAL_assert (internal); + CADICAL_assert (!internal->external); + internal->external = this; +} + +External::~External () { + if (solution) + delete[] solution; +} + +void External::enlarge (int new_max_var) { + + CADICAL_assert (!extended); + + size_t new_vsize = vsize ? 2 * vsize : 1 + (size_t) new_max_var; + while (new_vsize <= (size_t) new_max_var) + new_vsize *= 2; + LOG ("enlarge external size from %zd to new size %zd", vsize, new_vsize); + vsize = new_vsize; +} + +void External::init (int new_max_var, bool extension) { + CADICAL_assert (!extended); + if (new_max_var <= max_var) + return; + int new_vars = new_max_var - max_var; + int old_internal_max_var = internal->max_var; + int new_internal_max_var = old_internal_max_var + new_vars; + internal->init_vars (new_internal_max_var); + if ((size_t) new_max_var >= vsize) + enlarge (new_max_var); + LOG ("initialized %d external variables", new_vars); + if (!max_var) { + CADICAL_assert (e2i.empty ()); + e2i.push_back (0); + ext_units.push_back (0); + ext_units.push_back (0); + ext_flags.push_back (0); + ervars.push_back (0); + CADICAL_assert (internal->i2e.empty ()); + internal->i2e.push_back (0); + } else { + CADICAL_assert (e2i.size () == (size_t) max_var + 1); + CADICAL_assert (internal->i2e.size () == (size_t) old_internal_max_var + 1); + } + unsigned iidx = old_internal_max_var + 1, eidx; + for (eidx = max_var + 1u; eidx <= (unsigned) new_max_var; + eidx++, iidx++) { + LOG ("mapping external %u to internal %u", eidx, iidx); + CADICAL_assert (e2i.size () == eidx); + e2i.push_back (iidx); + ext_units.push_back (0); + ext_units.push_back (0); + ext_flags.push_back (0); + ervars.push_back (0); + internal->i2e.push_back (eidx); + CADICAL_assert (internal->i2e[iidx] == (int) eidx); + CADICAL_assert (e2i[eidx] == (int) iidx); + } + if (extension) + internal->stats.variables_extension += new_vars; + else + internal->stats.variables_original += new_vars; + if (new_max_var >= (int64_t) is_observed.size ()) + is_observed.resize (1 + (size_t) new_max_var, false); + if (internal->opts.checkfrozen) + if (new_max_var >= (int64_t) moltentab.size ()) + moltentab.resize (1 + (size_t) new_max_var, false); + CADICAL_assert (iidx == (size_t) new_internal_max_var + 1); + CADICAL_assert (eidx == (size_t) new_max_var + 1); + CADICAL_assert (ext_units.size () == (size_t) new_max_var * 2 + 2); + max_var = new_max_var; +} + +/*------------------------------------------------------------------------*/ + +void External::reset_assumptions () { + assumptions.clear (); + internal->reset_assumptions (); +} + +void External::reset_concluded () { + concluded = false; + internal->reset_concluded (); +} + +void External::reset_constraint () { + constraint.clear (); + internal->reset_constraint (); +} + +void External::reset_extended () { + if (!extended) + return; + LOG ("reset extended"); + extended = false; +} + +void External::reset_limits () { internal->reset_limits (); } + +/*------------------------------------------------------------------------*/ + +// when extension is true, elit should be a fresh variable and +// we can set a flag that it is an extension variable. +// This is then used in the API contracts, that extension variables are +// never part of the input +int External::internalize (int elit, bool extension) { + int ilit; + if (elit) { + CADICAL_assert (elit != INT_MIN); + const int eidx = abs (elit); + if (extension && eidx <= max_var) + FATAL ("can not add a definition for an already used variable %d", + eidx); + if (eidx > max_var) { + init (eidx, extension); + } + if (extension) { + CADICAL_assert (ervars.size () > (size_t) eidx); + ervars[eidx] = true; + } + ilit = e2i[eidx]; + if (elit < 0) + ilit = -ilit; + if (!ilit) { + CADICAL_assert (internal->max_var < INT_MAX); + ilit = internal->max_var + 1u; + internal->init_vars (ilit); + e2i[eidx] = ilit; + LOG ("mapping external %d to internal %d", eidx, ilit); + e2i[eidx] = ilit; + internal->i2e.push_back (eidx); + CADICAL_assert (internal->i2e[ilit] == eidx); + CADICAL_assert (e2i[eidx] == ilit); + if (elit < 0) + ilit = -ilit; + } + if (internal->opts.checkfrozen) { + CADICAL_assert (eidx < (int64_t) moltentab.size ()); + if (moltentab[eidx]) + FATAL ("can not reuse molten literal %d", eidx); + } + Flags &f = internal->flags (ilit); + if (f.status == Flags::UNUSED) + internal->mark_active (ilit); + else if (f.status != Flags::ACTIVE && f.status != Flags::FIXED) + internal->reactivate (ilit); + if (!marked (tainted, elit) && marked (witness, -elit)) { + CADICAL_assert (!internal->opts.checkfrozen); + LOG ("marking tainted %d", elit); + mark (tainted, elit); + } + } else + ilit = 0; + return ilit; +} + +void External::add (int elit) { + CADICAL_assert (elit != INT_MIN); + reset_extended (); + + bool forgettable = false; + + if (internal->opts.check && + (internal->opts.checkwitness || internal->opts.checkfailed)) { + + forgettable = + internal->from_propagator && internal->ext_clause_forgettable; + + // Forgettable clauses (coming from the external propagator) are not + // saved into the external 'original' stack. They are stored separately + // in external 'forgettable_original', from where they are deleted when + // the corresponding clause is deleted (actually deleted, not just + // marked as garbage). + if (!forgettable) + original.push_back (elit); + } + + const int ilit = internalize (elit); + CADICAL_assert (!elit == !ilit); + + // The external literals of the new clause must be saved for later + // when the proof is printed during add_original_lit (0) + if (elit && (internal->proof || forgettable)) { + eclause.push_back (elit); + if (internal->lrat) { + // actually find unit of -elit (flips elit < 0) + 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); + } + } + } + + if (!elit && internal->proof && internal->lrat) { + for (const auto &elit : eclause) { + ext_flags[abs (elit)] = false; + } + } + + if (elit) + LOG ("adding external %d as internal %d", elit, ilit); + internal->add_original_lit (ilit); + + // Clean-up saved external literals once proof line is printed + if (!elit && (internal->proof || forgettable)) + eclause.clear (); +} + +void External::assume (int elit) { + CADICAL_assert (elit); + reset_extended (); + if (internal->proof) + internal->proof->add_assumption (elit); + assumptions.push_back (elit); + const int ilit = internalize (elit); + CADICAL_assert (ilit); + LOG ("assuming external %d as internal %d", elit, ilit); + internal->assume (ilit); +} + +bool External::flip (int elit) { + CADICAL_assert (elit); + CADICAL_assert (elit != INT_MIN); + CADICAL_assert (!propagator); + + int eidx = abs (elit); + if (eidx > max_var) + return false; + if (marked (witness, elit)) + return false; + int ilit = e2i[eidx]; + if (!ilit) + return false; + bool res = internal->flip (ilit); + if (res && extended) + reset_extended (); + return res; +} + +bool External::flippable (int elit) { + CADICAL_assert (elit); + CADICAL_assert (elit != INT_MIN); + CADICAL_assert (!propagator); + + int eidx = abs (elit); + if (eidx > max_var) + return false; + if (marked (witness, elit)) + return false; + int ilit = e2i[eidx]; + if (!ilit) + return false; + return internal->flippable (ilit); +} + +bool External::failed (int elit) { + CADICAL_assert (elit); + CADICAL_assert (elit != INT_MIN); + int eidx = abs (elit); + if (eidx > max_var) + return 0; + int ilit = e2i[eidx]; + if (!ilit) + return 0; + if (elit < 0) + ilit = -ilit; + return internal->failed (ilit); +} + +void External::constrain (int elit) { + if (constraint.size () && !constraint.back ()) { + LOG (constraint, "replacing previous constraint"); + reset_constraint (); + } + CADICAL_assert (elit != INT_MIN); + reset_extended (); + const int ilit = internalize (elit); + CADICAL_assert (!elit == !ilit); + if (elit) + LOG ("adding external %d as internal %d to constraint", elit, ilit); + else if (!elit && internal->proof) { + internal->proof->add_constraint (constraint); + } + constraint.push_back (elit); + internal->constrain (ilit); +} + +bool External::failed_constraint () { + return internal->failed_constraint (); +} + +void External::phase (int elit) { + CADICAL_assert (elit); + CADICAL_assert (elit != INT_MIN); + const int ilit = internalize (elit); + internal->phase (ilit); +} + +void External::unphase (int elit) { + CADICAL_assert (elit); + CADICAL_assert (elit != INT_MIN); + int eidx = abs (elit); + if (eidx > max_var) { + UNUSED: + LOG ("resetting forced phase of unused external %d ignored", elit); + return; + } + int ilit = e2i[eidx]; + if (!ilit) + goto UNUSED; + if (elit < 0) + ilit = -ilit; + internal->unphase (ilit); +} + +/*------------------------------------------------------------------------*/ + +// External propagation related functions +// +// Note that when an already assigned variable is added as observed, the +// solver will backtrack to undo this assignment. +// +void External::add_observed_var (int elit) { + if (!propagator) { + LOG ("No connected propagator that could observe the variable, " + "observed flag is not set."); + return; + } + + CADICAL_assert (elit); + CADICAL_assert (elit != INT_MIN); + reset_extended (); // tainting! + + int eidx = abs (elit); + if (eidx <= max_var && + (marked (witness, elit) || marked (witness, -elit))) { + LOG ("Error, only clean variables are allowed to become observed."); + CADICAL_assert (false); + + // TODO: here needs to come the taint and restore of the newly + // observed variable. Restore_clauses must be called before continue. + // LOG ("marking tainted %d", elit); + // mark (tainted, elit); + // mark (tainted, -elit); + // restore_clauses ... + } + + if (eidx >= (int64_t) is_observed.size ()) + is_observed.resize (1 + (size_t) eidx, false); + + if (is_observed[eidx]) + return; + + LOG ("marking %d as externally watched", eidx); + + // Will do the necessary internalization + freeze (elit); + is_observed[eidx] = true; + + int ilit = internalize (elit); + // internal add-observed-var backtracks to a lower decision level to + // unassign the variable in case it was already assigned previously (but + // not on the current level) + internal->add_observed_var (ilit); + + if (propagator->is_lazy) + return; + + // In case this variable was already assigned (e.g. via unit clause) and + // got compacted to map to another (not observed) variable, it can not be + // unnasigned so it must be notified explicitly now. (-> Can lead to + // repeated fixed assignment notifications, in case it was unobserved and + // observed again. But a repeated notification is less error-prone than + // never notifying an assignment.) + const int tmp = fixed (elit); + if (!tmp) + return; + int unit = tmp < 0 ? -elit : elit; + + LOG ("notify propagator about fixed assignment upon observe for %d", + unit); + + // internal add-observed-var had to backtrack to root-level already + CADICAL_assert (!internal->level); + + std::vector<int> assigned = {unit}; + propagator->notify_assignment (assigned); +} + +void External::remove_observed_var (int elit) { + if (!propagator) { + LOG ("No connected propagator that could have watched the variable"); + return; + } + int eidx = abs (elit); + + if (eidx > max_var) + return; + + if (is_observed[eidx]) { + // Follow opposite order of add_observed_var, first remove internal + // is_observed + int ilit = e2i[eidx]; // internalize (elit); + internal->remove_observed_var (ilit); + + is_observed[eidx] = false; + melt (elit); + LOG ("unmarking %d as externally watched", eidx); + } +} + +void External::reset_observed_vars () { + // Shouldn't be called if there is no connected propagator + CADICAL_assert (propagator); + reset_extended (); + + internal->notified = 0; + LOG ("reset notified counter to 0"); + + if (!is_observed.size ()) + return; + + CADICAL_assert (!max_var || (size_t) max_var + 1 == is_observed.size ()); + + for (auto elit : vars) { + int eidx = abs (elit); + CADICAL_assert (eidx <= max_var); + if (is_observed[eidx]) { + int ilit = internalize (elit); + internal->remove_observed_var (ilit); + LOG ("unmarking %d as externally watched", eidx); + is_observed[eidx] = false; + melt (elit); + } + } +} + +bool External::observed (int elit) { + CADICAL_assert (elit); + CADICAL_assert (elit != INT_MIN); + int eidx = abs (elit); + if (eidx > max_var) + return false; + if (eidx >= (int) is_observed.size ()) + return false; + + return is_observed[eidx]; +} + +bool External::is_witness (int elit) { + CADICAL_assert (elit); + CADICAL_assert (elit != INT_MIN); + int eidx = abs (elit); + if (eidx > max_var) + return false; + return (marked (witness, elit) || marked (witness, -elit)); +} + +bool External::is_decision (int elit) { + CADICAL_assert (elit); + CADICAL_assert (elit != INT_MIN); + int eidx = abs (elit); + if (eidx > max_var) + return false; + + int ilit = internalize (elit); + return internal->is_decision (ilit); +} + +void External::force_backtrack (size_t new_level) { + if (!propagator) { + LOG ("No connected propagator that could force backtracking"); + return; + } + LOG ("force backtrack to level %zd", new_level); + internal->force_backtrack (new_level); +} + +/*------------------------------------------------------------------------*/ + +int External::propagate_assumptions () { + int res = internal->propagate_assumptions (); + if (res == 10 && !extended) + extend (); // Call solution reconstruction + check_solve_result (res); + reset_limits (); + return res; +} + +void External::implied (std::vector<int> &trailed) { + std::vector<int> ilit_implicants; + internal->implied (ilit_implicants); + + // Those implied literals must be filtered out that are witnesses + // on the reconstruction stack -> no inplace externalize is possible. + // (Internal does not see these marks, so no earlier filter is + // possible.) + + trailed.clear(); + + for (const auto &ilit : ilit_implicants) { + CADICAL_assert (ilit); + const int elit = internal->externalize (ilit); + const int eidx = abs (elit); + const bool is_extension_var = ervars[eidx]; + if (!marked (tainted, elit) && !is_extension_var) { + trailed.push_back (elit); + } + } +} + +void External::conclude_unknown () { + if (!internal->proof || concluded) + return; + concluded = true; + + vector<int> trail; + implied (trail); + internal->proof->conclude_unknown (trail); +} + +/*------------------------------------------------------------------------*/ + +// Internal checker if 'solve' claims the formula to be satisfiable. + +void External::check_satisfiable () { + LOG ("checking satisfiable"); + if (!extended) + extend (); + if (internal->opts.checkwitness) + check_assignment (&External::ival); + if (internal->opts.checkassumptions && !assumptions.empty ()) + check_assumptions_satisfied (); + if (internal->opts.checkconstraint && !constraint.empty ()) + check_constraint_satisfied (); +} + +// Internal checker if 'solve' claims formula to be unsatisfiable. + +void External::check_unsatisfiable () { + LOG ("checking unsatisfiable"); + if (!internal->opts.checkfailed) + return; + if (!assumptions.empty () || !constraint.empty ()) + check_failing (); +} + +// Check result of 'solve' to be correct. + +void External::check_solve_result (int res) { + if (!internal->opts.check) + return; + if (res == 10) + check_satisfiable (); + if (res == 20) + check_unsatisfiable (); +} + +// Prepare checking that completely molten literals are not used as argument +// of 'add' or 'assume', which is invalid under freezing semantics. This +// case would be caught by our 'restore' implementation so is only needed +// for checking the deprecated 'freeze' semantics. + +void External::update_molten_literals () { + if (!internal->opts.checkfrozen) + return; + CADICAL_assert ((size_t) max_var + 1 == moltentab.size ()); +#ifdef LOGGING + int registered = 0, molten = 0; +#endif + for (auto lit : vars) { + if (moltentab[lit]) { + LOG ("skipping already molten literal %d", lit); +#ifdef LOGGING + molten++; +#endif + } else if (frozen (lit)) + LOG ("skipping currently frozen literal %d", lit); + else { + LOG ("new molten literal %d", lit); + moltentab[lit] = true; +#ifdef LOGGING + registered++; + molten++; +#endif + } + } + LOG ("registered %d new molten literals", registered); + LOG ("reached in total %d molten literals", molten); +} + +int External::solve (bool preprocess_only) { + reset_extended (); + update_molten_literals (); + int res = internal->solve (preprocess_only); + check_solve_result (res); + reset_limits (); + return res; +} + +void External::terminate () { internal->terminate (); } + +int External::lookahead () { + reset_extended (); + update_molten_literals (); + int ilit = internal->lookahead (); + const int elit = + (ilit && ilit != INT_MIN) ? internal->externalize (ilit) : 0; + LOG ("lookahead internal %d external %d", ilit, elit); + return elit; +} + +CaDiCaL::CubesWithStatus External::generate_cubes (int depth, + int min_depth = 0) { + reset_extended (); + update_molten_literals (); + reset_limits (); + auto cubes = internal->generate_cubes (depth, min_depth); + auto externalize = [this] (int ilit) { + const int elit = ilit ? internal->externalize (ilit) : 0; + MSG ("lookahead internal %d external %d", ilit, elit); + return elit; + }; + auto externalize_map = [this, externalize] (std::vector<int> cube) { + (void) this; + MSG ("Cube : "); + std::for_each (begin (cube), end (cube), externalize); + }; + std::for_each (begin (cubes.cubes), end (cubes.cubes), externalize_map); + + return cubes; +} + +/*------------------------------------------------------------------------*/ + +void External::freeze (int elit) { + reset_extended (); + int ilit = internalize (elit); + unsigned eidx = vidx (elit); + if (eidx >= frozentab.size ()) + frozentab.resize (eidx + 1, 0); + unsigned &ref = frozentab[eidx]; + if (ref < UINT_MAX) { + ref++; + LOG ("external variable %d frozen once and now frozen %u times", eidx, + ref); + } else + LOG ("external variable %d frozen but remains frozen forever", eidx); + internal->freeze (ilit); +} + +void External::melt (int elit) { + reset_extended (); + int ilit = internalize (elit); + unsigned eidx = vidx (elit); + CADICAL_assert (eidx < frozentab.size ()); + unsigned &ref = frozentab[eidx]; + CADICAL_assert (ref > 0); + if (ref < UINT_MAX) { + if (!--ref) { + if (observed (elit)) { + ref++; + LOG ("external variable %d is observed, can not be completely " + "molten", + eidx); + } else + LOG ("external variable %d melted once and now completely melted", + eidx); + } else + LOG ("external variable %d melted once but remains frozen %u times", + eidx, ref); + } else + LOG ("external variable %d melted but remains frozen forever", eidx); + internal->melt (ilit); +} + +/*------------------------------------------------------------------------*/ + +void External::check_assignment (int (External::*a) (int) const) { + + // First check all assigned and consistent. + // + for (auto idx : vars) { + if (!(this->*a) (idx)) + FATAL ("unassigned variable: %d", idx); + int value_idx = (this->*a) (idx); + int value_neg_idx = (this->*a) (-idx); + if (value_idx == idx) + CADICAL_assert (value_neg_idx == idx); + else { + CADICAL_assert (value_idx == -idx); + CADICAL_assert (value_neg_idx == -idx); + } + if (value_idx != value_neg_idx) + FATAL ("inconsistently assigned literals %d and %d", idx, -idx); + } + + // Then check that all (saved) original clauses are satisfied. + // + bool satisfied = false; + const auto end = original.end (); + auto start = original.begin (), i = start; +#ifndef CADICAL_QUIET + int64_t count = 0; +#endif + for (; i != end; i++) { + int lit = *i; + if (!lit) { + if (!satisfied) { + fatal_message_start (); + fputs ("unsatisfied clause:\n", stderr); + for (auto j = start; j != i; j++) + fprintf (stderr, "%d ", *j); + fputc ('0', stderr); + fatal_message_end (); + } + satisfied = false; + start = i + 1; +#ifndef CADICAL_QUIET + count++; +#endif + } else if (!satisfied && (this->*a) (lit) == lit) + satisfied = true; + } + + bool presence_flag; + // Check those forgettable external clauses that are still present, but + // only if the external propagator is still connected (otherwise solution + // reconstruction is allowed to touch the previously observed variables so + // there is no guarantee that the final model will satisfy these clauses.) + for (const auto &forgettables : forgettable_original) { + if (!propagator) + break; + presence_flag = true; + satisfied = false; +#ifndef CADICAL_QUIET + count++; +#endif + std::vector<int> literals; + for (const auto lit : forgettables.second) { + if (presence_flag) { + // First integer is a Boolean flag, not a literal + if (!lit) { + // Deleted clauses can be ignored, they count as satisfied + satisfied = true; + break; + } + presence_flag = false; + continue; + } + + if ((this->*a) (lit) == lit) { + satisfied = true; + break; + } + } + + if (!satisfied) { + fatal_message_start (); + fputs ("unsatisfied external forgettable clause:\n", stderr); + for (size_t j = 1; j < forgettables.second.size (); j++) + fprintf (stderr, "%d ", forgettables.second[j]); + fputc ('0', stderr); + fatal_message_end (); + } + } +#ifndef CADICAL_QUIET + VERBOSE (1, "satisfying assignment checked on %" PRId64 " clauses", + count); +#endif +} + +/*------------------------------------------------------------------------*/ + +void External::check_assumptions_satisfied () { + for (const auto &lit : assumptions) { + // Not 'signed char' !!!! + const int tmp = ival (lit); + if (tmp != lit) + FATAL ("assumption %d falsified", lit); + if (!tmp) + FATAL ("assumption %d unassigned", lit); + } + VERBOSE (1, "checked that %zd assumptions are satisfied", + assumptions.size ()); +} + +void External::check_constraint_satisfied () { + for (const auto lit : constraint) { + if (ival (lit) == lit) { + VERBOSE (1, "checked that constraint is satisfied"); + return; + } + } + FATAL ("constraint not satisfied"); +} + +void External::check_failing () { + Solver *checker = new Solver (); + DeferDeletePtr<Solver> delete_checker (checker); + checker->prefix ("checker "); +#ifdef LOGGING + if (internal->opts.log) + checker->set ("log", true); +#endif + + for (const auto lit : assumptions) { + if (!failed (lit)) + continue; + LOG ("checking failed literal %d in core", lit); + checker->add (lit); + checker->add (0); + } + if (failed_constraint ()) { + LOG (constraint, "checking failed constraint"); + for (const auto lit : constraint) + checker->add (lit); + } else if (constraint.size ()) + LOG (constraint, "constraint satisfied and ignored"); + + // Add original clauses as last step, failing () and failed_constraint () + // might add more external clauses (due to lazy explanation) + for (const auto lit : original) + checker->add (lit); + + // Add every forgettable external clauses + for (const auto &forgettables : forgettable_original) { + bool presence_flag = true; + for (const auto lit : forgettables.second) { + if (presence_flag) { + // First integer is a Boolean flag, not a literal, ignore it here + presence_flag = false; + continue; + } + checker->add (lit); + } + checker->add (0); + } + + int res = checker->solve (); + if (res != 20) + FATAL ("failed assumptions do not form a core"); + delete_checker.free (); + VERBOSE (1, "checked that %zd failing assumptions form a core", + assumptions.size ()); +} + +/*------------------------------------------------------------------------*/ + +// Traversal of unit clauses is implemented here. + +// In principle we want to traverse the clauses of the simplified formula +// only, particularly eliminated variables should be completely removed. +// This poses the question what to do with unit clauses. Should they be +// considered part of the simplified formula or of the witness to construct +// solutions for the original formula? Ideally they should be considered +// to be part of the witness only, i.e., as they have been simplified away. + +// Therefore we distinguish frozen and non-frozen units during clause +// traversal. Frozen units are treated as unit clauses while non-frozen +// units are treated as if they were already eliminated and put on the +// extension stack as witness clauses. + +// Furthermore, eliminating units during 'compact' could be interpreted as +// variable elimination, i.e., just add the resolvents (remove falsified +// literals), then drop the clauses with the unit, and push the unit on the +// extension stack. This is of course only OK if the user did not freeze +// that variable (even implicitly during assumptions). + +// Thanks go to Fahiem Bacchus for asking why there is a necessity to +// distinguish these two cases (frozen and non-frozen units). The answer is +// that it is not strictly necessary, and this distinction could be avoided +// by always treating units as remaining unit clauses, thus only using the +// first of the two following functions and dropping the 'if (!frozen (idx)) +// continue;' check in it. This has however the down-side that those units +// are still in the simplified formula and only as units. I would not +// consider such a formula as really being 'simplified'. On the other hand +// if the user explicitly freezes a literal, then it should continue to be +// in the simplified formula during traversal. So also only using the +// second function is not ideal. + +// There is however a catch where this solution breaks down (in the sense of +// producing less optimal results - that is keeping units in the formula +// which better would be witness clauses). The problem is with compact +// preprocessing which removes eliminated but also fixed internal variables. +// One internal unit (fixed) variable is kept and all the other external +// literals which became unit are mapped to that internal literal (negated +// or positively). Compact is called non-deterministically from the point +// of the user and thus there is no control on when this happens. If +// compact happens those external units are mapped to a single internal +// literal now and then all share the same 'frozen' counter. So if the +// user freezes one of them all in essence get frozen, which in turn then +// makes a difference in terms of traversing such a unit as unit clause or +// as unit witness. + +bool External::traverse_all_frozen_units_as_clauses (ClauseIterator &it) { + if (internal->unsat) + return true; + + vector<int> clause; + + for (auto idx : vars) { + if (!frozen (idx)) + continue; + const int tmp = fixed (idx); + if (!tmp) + continue; + int unit = tmp < 0 ? -idx : idx; + clause.push_back (unit); + if (!it.clause (clause)) + return false; + clause.clear (); + } + + return true; +} + +bool External::traverse_all_non_frozen_units_as_witnesses ( + WitnessIterator &it) { + if (internal->unsat) + return true; + + vector<int> clause_and_witness; + for (auto idx : vars) { + if (frozen (idx)) + continue; + const int tmp = fixed (idx); + if (!tmp) + continue; + int unit = tmp < 0 ? -idx : idx; + const int ilit = e2i[idx] * (tmp < 0 ? -1 : 1); + // heurstically add + max_var to the id to avoid reusing ids + const int64_t id = internal->lrat ? internal->unit_id (ilit) : 1; + CADICAL_assert (id); + clause_and_witness.push_back (unit); + if (!it.witness (clause_and_witness, clause_and_witness, id + max_var)) + return false; + clause_and_witness.clear (); + } + + return true; +} + +/*------------------------------------------------------------------------*/ + +void External::copy_flags (External &other) const { + const vector<Flags> &this_ftab = internal->ftab; + vector<Flags> &other_ftab = other.internal->ftab; + const unsigned limit = min (max_var, other.max_var); + for (unsigned eidx = 1; eidx <= limit; eidx++) { + const int this_ilit = e2i[eidx]; + if (!this_ilit) + continue; + const int other_ilit = other.e2i[eidx]; + if (!other_ilit) + continue; + if (!internal->active (this_ilit)) + continue; + if (!other.internal->active (other_ilit)) + continue; + CADICAL_assert (this_ilit != INT_MIN); + CADICAL_assert (other_ilit != INT_MIN); + const Flags &this_flags = this_ftab[abs (this_ilit)]; + Flags &other_flags = other_ftab[abs (other_ilit)]; + this_flags.copy (other_flags); + } +} + +/*------------------------------------------------------------------------*/ + +void External::export_learned_empty_clause () { + CADICAL_assert (learner); + if (learner->learning (0)) { + LOG ("exporting learned empty clause"); + learner->learn (0); + } else + LOG ("not exporting learned empty clause"); +} + +void External::export_learned_unit_clause (int ilit) { + CADICAL_assert (learner); + if (learner->learning (1)) { + LOG ("exporting learned unit clause"); + const int elit = internal->externalize (ilit); + CADICAL_assert (elit); + learner->learn (elit); + learner->learn (0); + } else + LOG ("not exporting learned unit clause"); +} + +void External::export_learned_large_clause (const vector<int> &clause) { + CADICAL_assert (learner); + size_t size = clause.size (); + CADICAL_assert (size <= (unsigned) INT_MAX); + if (learner->learning ((int) size)) { + LOG ("exporting learned clause of size %zu", size); + for (auto ilit : clause) { + const int elit = internal->externalize (ilit); + CADICAL_assert (elit); + learner->learn (elit); + } + learner->learn (0); + } else + LOG ("not exporting learned clause of size %zu", size); +} + +} // namespace CaDiCaL + +ABC_NAMESPACE_IMPL_END diff --git a/src/sat/cadical/cadical_external_propagate.cpp b/src/sat/cadical/cadical_external_propagate.cpp new file mode 100644 index 0000000000..215170c8b1 --- /dev/null +++ b/src/sat/cadical/cadical_external_propagate.cpp @@ -0,0 +1,1232 @@ +#include "global.h" + +#include "internal.hpp" + +ABC_NAMESPACE_IMPL_START + +namespace CaDiCaL { + +/*----------------------------------------------------------------------------*/ +// +// Mark a variable as an observed one. It can be a new variable. It is +// assumed to be clean (not eliminated by previous simplifications). +// +void Internal::add_observed_var (int ilit) { + int idx = vidx (ilit); + if (idx >= (int64_t) relevanttab.size ()) + relevanttab.resize (1 + (size_t) idx, 0); + unsigned &ref = relevanttab[idx]; + if (ref < UINT_MAX) { + ref++; + LOG ("variable %d is observed %u times", idx, ref); + } else + LOG ("variable %d remains observed forever", idx); + // TODO: instead of actually backtracking, it would be enough to notify + // backtrack and re-play again every levels' notification to the + // propagator + if (val (ilit) && level && !fixed (ilit)) { + // The variable is already assigned, but we can not send a notification + // about it because it happened on an earlier decision level. + // To not break the stack-like view of the trail, we simply backtrack to + // undo this unnotifiable assignment. + const int assignment_level = var (ilit).level; + backtrack (assignment_level - 1); + } else if (level && fixed (ilit)) { + backtrack (0); + } +} + +/*----------------------------------------------------------------------------*/ +// +// Removing an observed variable should happen only once it is ensured +// that there is no unexplained propagation in the implication +// graph involving this variable. +// +void Internal::remove_observed_var (int ilit) { + if (!fixed (ilit) && level) { + backtrack (); + } + + CADICAL_assert (fixed (ilit) || !level); + + const int idx = vidx (ilit); + CADICAL_assert ((size_t) idx < relevanttab.size ()); + unsigned &ref = relevanttab[idx]; + CADICAL_assert (fixed (ilit) || ref > 0); + if (fixed (ilit)) + ref = 0; + else if (ref < UINT_MAX) { + if (!--ref) { + LOG ("variable %d is not observed anymore", idx); + } else + LOG ("variable %d is unobserved once but remains observed %u times", + ilit, ref); + } else + LOG ("variable %d remains observed forever", idx); +} + +/*----------------------------------------------------------------------------*/ +// +// Supposed to be used only by mobical. +// +bool Internal::observed (int ilit) const { + CADICAL_assert ((size_t) vidx (ilit) < relevanttab.size ()); + return relevanttab[vidx (ilit)] > 0; +} + +/*----------------------------------------------------------------------------*/ +// +// Check for unexplained propagations upon disconnecting external propagator +// +void Internal::set_tainted_literal () { + if (!opts.ilb) { + return; + } + for (auto idx : vars) { + if (!val (idx)) + continue; + if (var (idx).reason != external_reason) + continue; + if (!tainted_literal) { + tainted_literal = idx; + continue; + } + CADICAL_assert (val (tainted_literal)); + if (var (idx).level < var (tainted_literal).level) { + tainted_literal = idx; + } + } +} + +void Internal::renotify_trail_after_ilb () { + if (!external_prop || external_prop_is_lazy || !trail.size () || + !opts.ilb) { + return; + } + LOG ("notify external propagator about new assignments (after ilb)"); +#ifndef CADICAL_NDEBUG + LOG ("(decision level: %d, trail size: %zd, notified %zd)", level, + trail.size (), notified); +#endif + renotify_full_trail (); +} + +void Internal::renotify_trail_after_local_search () { + if (!external_prop || external_prop_is_lazy || !trail.size ()) { + return; + } + LOG ("notify external propagator about new assignments (after local " + "search)"); +#ifndef CADICAL_NDEBUG + LOG ("(decision level: %d, trail size: %zd, notified %zd)", level, + trail.size (), notified); +#endif + renotify_full_trail (); +} + +// It repeats ALL assignments of the trail, so the already notified +// root-level assignments will be notified multiple times. + +void Internal::renotify_full_trail () { + const size_t end_of_trail = trail.size (); + if (level) { + notified = 0; // TODO: save the last notified root-level position + // somewhere and use it here + notify_backtrack (0); + } + std::vector<int> assigned; + + int prev_max_level = 0; + int current_level = 0; + int propagator_level = 0; + + while (notified < end_of_trail) { + int ilit = trail[notified++]; + // In theory, 0 ilit can happen due to pseudo-decision levels + if (!ilit) + current_level = prev_max_level + 1; + else + current_level = var (ilit).level; + + if (current_level > propagator_level) { + if (assigned.size ()) + external->propagator->notify_assignment (assigned); + while (current_level > propagator_level) { + external->propagator->notify_new_decision_level (); + propagator_level++; + } + assigned.clear (); + } + // Current level can be smaller than prev_max_level due to chrono + if (current_level > prev_max_level) + prev_max_level = current_level; + + if (!observed (ilit)) + continue; + + int elit = externalize (ilit); // TODO: double-check tainting + CADICAL_assert (elit); + // Fixed variables might get mapped (during compact) to another + // non-observed but fixed variable. + // This happens on root level, so notification about their assignment is + // already done. + CADICAL_assert (external->observed (elit) || fixed (ilit)); + if (!external->ervars[abs (elit)]) + assigned.push_back (elit); + } + if (assigned.size ()) + external->propagator->notify_assignment (assigned); + assigned.clear (); + + // In case there are some left over empty levels on the top of the trail, + // the external propagtor must be notified about them so the levels are + // synced + while (level > propagator_level) { + external->propagator->notify_new_decision_level (); + propagator_level++; + } + + return; +} + +/*----------------------------------------------------------------------------*/ +// +// Check if the variable is assigned by decision. +// +bool Internal::is_decision (int ilit) { + if (!level || fixed (ilit) || !val (ilit)) + return false; + + const int idx = vidx (ilit); + Var &v = var (idx); +#ifndef CADICAL_NDEBUG + LOG (v.reason, + "is_decision: i%d (current level: %d, is_fixed: %d, v.level: %d, " + "is_external_reason: %d, v.reason: )", + ilit, level, fixed (ilit), v.level, v.reason == external_reason); +#endif + if (!v.level || v.reason) + return false; + CADICAL_assert (!v.reason); + return true; +} + +void Internal::force_backtrack (size_t new_level) { + if (!forced_backt_allowed || level <= 0 || new_level >= (size_t) level) + return; + +#ifndef CADICAL_NDEBUG + LOG ("external propagator forces backtrack to decision level" + "%zd (from level %d)", + new_level, level); +#endif + backtrack (new_level); +} + +/*----------------------------------------------------------------------------*/ +// +// Call external propagator to check if there is a literal to be propagated. +// The reason of the propagation is not necessarily asked at that point. +// +// In case the externally propagated literal is already falsified, the +// reason is asked and conflict analysis starts. In case the externally +// propagated literal is already satisfied, nothing happens. +// +bool Internal::external_propagate () { + if (level) + require_mode (SEARCH); + CADICAL_assert (!unsat); + + size_t before = num_assigned; + bool cb_repropagate_needed = true; + while (cb_repropagate_needed && !conflict && external_prop && + !external_prop_is_lazy && !private_steps) { +#ifndef CADICAL_NDEBUG + LOG ("external propagation starts (decision level: %d, trail size: " + "%zd, notified %zd)", + level, trail.size (), notified); +#endif + cb_repropagate_needed = false; + // external->reset_extended (); //TODO for inprocessing + + notify_assignments (); + + int elit = external->propagator->cb_propagate (); + stats.ext_prop.ext_cb++; + stats.ext_prop.eprop_call++; + while (elit) { + CADICAL_assert (external->is_observed[abs (elit)]); + int ilit = external->e2i[abs (elit)]; + if (elit < 0) + ilit = -ilit; + int tmp = val (ilit); +#ifndef CADICAL_NDEBUG + CADICAL_assert (fixed (ilit) || observed (ilit)); + LOG ("External propagation of e%d (i%d val: %d)", elit, ilit, tmp); +#endif + if (!tmp) { + // variable is not assigned, it can be propagated + if (!level) { + Clause *res = learn_external_reason_clause (ilit, elit); +#ifndef LOGGING + LOG (res, "reason clause of external propagation of %d:", elit); +#endif + (void) res; + } else + search_assign_external (ilit); + stats.ext_prop.eprop_prop++; + + if (unsat || conflict) + break; + propagate (); + if (unsat || conflict) + break; + notify_assignments (); + } else if (tmp < 0) { + LOG ("External propgation of %d is falsified under current trail", + ilit); + stats.ext_prop.eprop_conf++; + int level_before = level; + size_t assigned = num_assigned; + Clause *res = learn_external_reason_clause (ilit, elit); +#ifndef LOGGING + LOG (res, "reason clause of external propagation of %d:", elit); +#endif + (void) res; + bool trail_changed = + (num_assigned != assigned || level != level_before || + propagated < trail.size ()); + + if (unsat || conflict) + break; + + if (trail_changed) { + propagate (); + if (unsat || conflict) + break; + notify_assignments (); + } + } // else (tmp > 0) -> the case of a satisfied literal is ignored + elit = external->propagator->cb_propagate (); + stats.ext_prop.ext_cb++; + stats.ext_prop.eprop_call++; + } + +#ifndef CADICAL_NDEBUG + LOG ("External propagation ends (decision level: %d, trail size: %zd, " + "notified %zd)", + level, trail.size (), notified); +#endif + if (!unsat && !conflict) { + int level_before = level; + size_t assigned = num_assigned; + bool has_external_clause = ask_external_clause (); + // New observed variable might have triggered a backtrack during this + // ask_external_clause call, so we need to propagate before continuing + stats.ext_prop.ext_cb++; + stats.ext_prop.elearn_call++; + + bool trail_changed = + (num_assigned != assigned || level != level_before || + propagated < trail.size ()); + if (trail_changed) { + propagate (); // unsat or conflict will be caught later + if (!unsat || !conflict) + notify_assignments (); + } + +#ifndef CADICAL_NDEBUG + if (has_external_clause) + LOG ("New external clauses are to be added."); + else + LOG ("No external clauses to be added."); +#endif + + while (has_external_clause) { + level_before = level; + assigned = num_assigned; + + add_external_clause (0); + trail_changed = + (num_assigned != assigned || level != level_before || + propagated < trail.size ()); + cb_repropagate_needed = true; + + if (unsat || conflict) { + cb_repropagate_needed = false; + break; + } + + if (trail_changed) { + propagate (); + if (unsat || conflict) { + cb_repropagate_needed = false; + break; + } + + notify_assignments (); + } + has_external_clause = ask_external_clause (); + stats.ext_prop.ext_cb++; + stats.ext_prop.elearn_call++; + } + } +#ifndef CADICAL_NDEBUG + LOG ("External clause addition ends on decision level %d at trail " + "size " + "%zd (notified %zd)", + level, trail.size (), notified); +#endif + } + if (before < num_assigned) + did_external_prop = true; + return !conflict; +} + +/*----------------------------------------------------------------------------*/ +// +// Helper function, calls 'cb_has_external_clause', while maintains the +// related redundancy type of the clause. +// + +bool Internal::ask_external_clause () { + ext_clause_forgettable = false; + bool res = + external->propagator->cb_has_external_clause (ext_clause_forgettable); + + return res; +} +/*----------------------------------------------------------------------------*/ +// +// Literals of the externally learned clause must be reordered based on the +// assignment levels of the literals. +// +void Internal::move_literals_to_watch () { + if (clause.size () < 2) + return; + if (!level) + return; + + for (int i = 0; i < 2; i++) { + int highest_position = i; + int highest_literal = clause[i]; + + int highest_level = var (highest_literal).level; + int highest_value = val (highest_literal); + + for (size_t j = i + 1; j < clause.size (); j++) { + const int other = clause[j]; + const int other_level = var (other).level; + const int other_value = val (other); + + if (other_value < 0) { + if (highest_value >= 0) + continue; + if (other_level <= highest_level) + continue; + } else if (other_value > 0) { + if (highest_value > 0 && other_level >= highest_level) + continue; + } else { + if (highest_value >= 0) + continue; + } + + highest_position = j; + highest_literal = other; + highest_level = other_level; + highest_value = other_value; + } +#ifndef CADICAL_NDEBUG + LOG ("highest position: %d highest level: %d highest value: %d", + highest_position, highest_level, highest_value); +#endif + + if (highest_position == i) + continue; + if (highest_position > i) { + std::swap (clause[i], clause[highest_position]); + } + } +} + +/*----------------------------------------------------------------------------*/ +// +// Reads out from the external propagator the lemma/proapgation reason +// clause literal by literal. In case propagated_elit is 0, it is about an +// external clause via 'cb_add_external_clause_lit'. Otherwise, it is about +// learning the reason of 'propagated_elit' via 'cb_add_reason_clause_lit'. +// The learned clause is simplified by the current root-level assignment +// (i.e. root-level falsified literals are removed, root satisfied clauses +// are skipped). Duplicate literals are removed, tauotologies are detected +// and skipped. It always adds the original (un-simplified) external clause +// to the proof as an input clause and +// the simplified version of it (except exceptions below) as a derived +// clause. +// +// In case the external clause, after simplifications, is satisfied, no +// clause is constructed, and the function returns 0. In case the external +// clause, after simplifications, is empty, no clause is constructed, unsat +// is set true and the function returns 0. In case the external clause, +// after simplifications, is unit, no clause is constructed, +// 'Internal::clause' has the unit literal (without 0) and the function +// returns 0. +// +// In every other cases a new clause is constructed and the pointer is in +// newest_clause +// +void Internal::add_external_clause (int propagated_elit, + bool no_backtrack) { + CADICAL_assert (original.empty ()); + int elit = 0; + + if (propagated_elit) { + // Propagation reason clauses are by default assumed to be forgettable + // irredundant. In case they would be unforgettably important, the + // propagator can add them as an explicit unforgettable external clause + // or set 'are_reasons_forgettable' to false. + ext_clause_forgettable = external->propagator->are_reasons_forgettable; +#ifndef CADICAL_NDEBUG + LOG ("add external reason of propagated lit: %d", propagated_elit); +#endif + elit = external->propagator->cb_add_reason_clause_lit (propagated_elit); + } else + elit = external->propagator->cb_add_external_clause_lit (); + + // we need to be build a new LRAT chain if we are already in the middle of + // the analysis (like during failed assumptions) + LOG (lrat_chain, "lrat chain before"); + std::vector<int64_t> lrat_chain_ext = std::move (lrat_chain); + lrat_chain.clear (); + clause.clear (); + + // Read out the external lemma into original and simplify it into clause + CADICAL_assert (clause.empty ()); + CADICAL_assert (original.empty ()); + + CADICAL_assert (!force_no_backtrack); + CADICAL_assert (!from_propagator); + force_no_backtrack = no_backtrack; + from_propagator = true; + while (elit) { + CADICAL_assert (external->is_observed[abs (elit)]); + external->add (elit); + if (propagated_elit) + elit = + external->propagator->cb_add_reason_clause_lit (propagated_elit); + else + elit = external->propagator->cb_add_external_clause_lit (); + } + external->add (elit); + CADICAL_assert (original.empty ()); + CADICAL_assert (clause.empty ()); + force_no_backtrack = false; + from_propagator = false; + lrat_chain = std::move (lrat_chain_ext); + LOG (lrat_chain, "lrat chain after"); +} + +/*----------------------------------------------------------------------------*/ +// +// Recursively calls 'learn_external_reason_clause' to explain every +// backward reachable externally propagated literal starting from 'ilit'. +// +void Internal::explain_reason (int ilit, Clause *reason, int &open) { + if (!opts.exteagerreasons) + return; +#ifndef CADICAL_NDEBUG + LOG (reason, "explain_reason of %d (open: %d)", ilit, open); +#endif + CADICAL_assert (reason); + CADICAL_assert (reason != external_reason); + for (const auto &other : *reason) { + if (other == ilit) + continue; + Flags &f = flags (other); + if (f.seen) + continue; + Var &v = var (other); + if (!v.level) + continue; + CADICAL_assert (val (other) < 0); + CADICAL_assert (v.level <= level); + if (v.reason == external_reason) { + v.reason = learn_external_reason_clause (-other, 0, true); + } + if (v.level && v.reason) { + f.seen = true; + open++; + } + } +} + +/*----------------------------------------------------------------------------*/ +// +// In case external propagation was used, the reason clauses of the relevant +// propagations must be learned lazily before/during conflict analysis. +// While conflict analysis needs to analyze only the current level, lazy +// clause learning must check every clause on every level that is backward +// reachable from the conflicting clause to guarantee that the assignment +// levels of the variables are accurate. So this explanation round is +// separated from the conflict analysis, thereby guranteeing that the flags +// and datastructures can be properly used later. +// +// This function must be called before the conflict analysis, in order to +// guarantee that every relevant reason clause is indeed learned already and +// to be sure that the levels of assignments are set correctly. +// +// Later TODO: experiment with bounded explanation (explain only those +// literals that are directly used during conflict analysis + +// minimizing/shrinking). The assignment levels are then only +// over-approximated. +// +void Internal::explain_external_propagations () { + CADICAL_assert (conflict); + CADICAL_assert (clause.empty ()); + + Clause *reason = conflict; + std::vector<int> seen_lits; + int open = 0; // Seen but not explained literal + + explain_reason (0, reason, open); // marks conflict clause lits as seen + int i = trail.size (); // Start at end-of-trail + while (i > 0) { + const int lit = trail[--i]; + if (!flags (lit).seen) + continue; + seen_lits.push_back (lit); + Var &v = var (lit); + if (!v.level) + continue; + if (v.reason) { + open--; + explain_reason (lit, v.reason, open); + } + if (!open) + break; + } + CADICAL_assert (!open); + + if (!opts.exteagerrecalc) { + for (auto lit : seen_lits) { + Flags &f = flags (lit); + f.seen = false; + } +#ifndef CADICAL_NDEBUG + for (auto idx : vars) { + CADICAL_assert (!flags (idx).seen); + } +#endif + } + + // Traverse now in the opposite direction (from lower to higher levels) + // and calculate the actual assignment level for the seen assignments. + for (auto it = seen_lits.rbegin (); it != seen_lits.rend (); ++it) { + const int lit = *it; + Flags &f = flags (lit); + Var &v = var (lit); + if (v.reason) { + int real_level = 0; + for (const auto &other : *v.reason) { + if (other == lit) + continue; + int tmp = var (other).level; + if (tmp > real_level) + real_level = tmp; + } + if (v.level && !real_level) { + build_chain_for_units (lit, v.reason, 1); + learn_unit_clause (lit); + lrat_chain.clear (); + v.reason = 0; + } + CADICAL_assert (v.level >= real_level); + if (v.level > real_level) { + v.level = real_level; + } + } + f.seen = false; + } + +#if 0 // has been fuzzed extensively + for (auto idx : vars) { + CADICAL_assert (!flags (idx).seen); + } +#endif +} + +/*----------------------------------------------------------------------------*/ +// +// Learns the reason clause of the propagation of ilit from the +// external propagator via 'add_external_clause'. +// In case of falsified propagation steps, if the propagated literal is +// already fixed to the opposite value, externalize will not necessarily +// give back the original elit (but an equivalent one). To avoid that, in +// falsified propagation cases the propagated elit is added as a second +// argument. +// +Clause *Internal::learn_external_reason_clause (int ilit, + int falsified_elit, + bool no_backtrack) { + CADICAL_assert (external->propagator); + // we cannot modify clause during analysis + auto clause_tmp = std::move (clause); + + CADICAL_assert (clause.empty ()); + CADICAL_assert (original.empty ()); + + stats.ext_prop.eprop_expl++; + + int elit = 0; + if (!falsified_elit) { + CADICAL_assert (!fixed (ilit)); + elit = externalize (ilit); + } else + elit = falsified_elit; + + LOG ("ilit: %d, elit: %d", ilit, elit); + add_external_clause (elit, no_backtrack); + +#ifndef CADICAL_NDEBUG + if (!falsified_elit && newest_clause) { + // Check if external propagation is correct wrt to the topological order + // defined by the trail. In case it is a falsified external propagation + // step, the order does not matter, the reason simply supposed to be a + // falsified clause. + const int propagated_ilit = ilit; + for (auto const reason_ilit : *newest_clause) { + CADICAL_assert (var (reason_ilit).trail <= var (propagated_ilit).trail); + } + } +#endif + + clause = std::move (clause_tmp); + return newest_clause; +} + +/*----------------------------------------------------------------------------*/ +// +// Helper function to be able to call learn_external_reason_clause when the +// internal clause is already used in the caller side (for example during +// proof checking). These calls are assumed to be without a falsified elit. +// Dont use it in general instead of learn_external_reason_clause because it +// does not support the corner cases where a literal remains in clause. +// +Clause *Internal::wrapped_learn_external_reason_clause (int ilit) { + Clause *res; + std::vector<int64_t> chain_tmp{std::move (lrat_chain)}; + lrat_chain.clear (); + if (clause.empty ()) { + res = learn_external_reason_clause (ilit, 0, true); + } else { + std::vector<int> clause_tmp{std::move (clause)}; + clause.clear (); + res = learn_external_reason_clause (ilit, 0, true); + // The learn_external_reason clause can leave a literal in clause when + // there is a falsified elit arg. Here it is not allowed to + // happen. + CADICAL_assert (clause.empty ()); + + clause = std::move (clause_tmp); + clause_tmp.clear (); + } + CADICAL_assert (lrat_chain.empty ()); + lrat_chain = std::move (chain_tmp); + chain_tmp.clear (); + return res; +} + +/*----------------------------------------------------------------------------*/ +// +// Checks if the new clause forces backtracking, new assignments or conflict +// analysis +// +void Internal::handle_external_clause (Clause *res) { + if (from_propagator) + stats.ext_prop.elearned++; + // at level 0 we have to do nothing... + if (!level) + return; + if (!res) { + if (from_propagator) + stats.ext_prop.elearn_prop++; + // new unit clause. For now just backtrack. + CADICAL_assert (!force_no_backtrack); + CADICAL_assert (level); + // if (!opts.chrono) { + backtrack (); + // } + return; + } + if (from_propagator) + stats.ext_prop.elearned++; + CADICAL_assert (res->size >= 2); + const int pos0 = res->literals[0]; + const int pos1 = res->literals[1]; + if (force_no_backtrack) { + CADICAL_assert (val (pos1) < 0); + CADICAL_assert (val (pos0) >= 0); + return; + // TODO: maybe fix levels + } + const int l1 = var (pos1).level; + if (val (pos0) < 0) { // conflicting or propagating clause + CADICAL_assert (0 < l1 && l1 <= var (pos0).level); + if (!opts.chrono) { + backtrack (l1); + } + if (val (pos0) < 0) { + conflict = res; + if (!from_propagator) { + // its better to backtrack instead of analyze + backtrack (l1 - 1); + conflict = 0; + CADICAL_assert (!val (pos0) && !val (pos1)); + } + } else { + search_assign_driving (pos0, res); + } + if (from_propagator) + stats.ext_prop.elearn_conf++; + return; + } + if (val (pos1) < 0 && !val (pos0)) { // propagating clause + if (!opts.chrono) { + backtrack (l1); + } + search_assign_driving (pos0, res); + if (from_propagator) + stats.ext_prop.elearn_conf++; + return; + } +} + +/*----------------------------------------------------------------------------*/ +// +// Asks the external propagator if the current solution is OK +// by calling 'cb_check_found_model (model)'. +// +// The checked model is built up after everything is restored +// from the reconstruction stack and every variable is reactivated +// and so it is not just simply the trail (i.e. it can be expensive). +// +// If the external propagator approves the model, the function +// returns true. +// +// If the propagator does not approve the model, the solver asks +// the propagator to add an external clause. +// This external clause, however, does NOT have to be falsified by +// the current model. The possible cases and reactions are described +// below in the function. The possible states after that function: +// - A solution was found and accepted by the external propagator +// - A conflicting clause was learned from the external propagator +// - The empty clause was learned due to something new learned from +// the external propagator. +// +// In case only new variables were introduced, but no new clauses were +// added, the function will return without a conflict to the outer CDCL +// loop, where the new (not yet satisfied) variables are recognized and +// the search continues. +bool Internal::external_check_solution () { + if (!external_prop) + return true; + + bool trail_changed = true; + bool added_new_clauses = false; + while (trail_changed || added_new_clauses) { + notify_assignments (); + if (!satisfied ()) + break; + trail_changed = false; // to be on the safe side + added_new_clauses = false; + LOG ("Final check by external propagator is invoked."); + stats.ext_prop.echeck_call++; + external->reset_extended (); + external->extend (); + + std::vector<int> etrail; + + // Here the variables must be filtered by external->is_observed, + // because fixed variables are internally not necessarily observed + // anymore. + for (int idx = 1; idx <= external->max_var; idx++) { + if (!external->is_observed[idx]) + continue; + const int lit = external->ival (idx); + etrail.push_back (lit); +#ifndef CADICAL_NDEBUG +#ifdef LOGGING + bool p = external->vals[idx]; + LOG ("evals[%d]: %d ival(%d): %d", idx, p, idx, lit); +#endif +#endif + } + + bool is_consistent = + external->propagator->cb_check_found_model (etrail); + stats.ext_prop.ext_cb++; + if (is_consistent) { + LOG ("Found solution is approved by external propagator."); + return true; + } + + bool has_external_clause = ask_external_clause (); + + stats.ext_prop.ext_cb++; + stats.ext_prop.elearn_call++; + + if (has_external_clause) + LOG ( + "Found solution triggered new clauses from external propagator."); + + while (has_external_clause) { + int level_before = level; + size_t assigned = num_assigned; + add_external_clause (0); + bool trail_changed = + (num_assigned != assigned || level != level_before || + propagated < trail.size ()); + added_new_clauses = true; + // + // There are many possible scenarios here: + // - Learned conflicting clause: return to CDCL loop (conflict true) + // - Learned conflicting unit clause that after backtrack+BCP leads to + // a new complete solution: force the outer loop to check the new + // model (trail_changed is true, but (conflict & unsat) is false) + // - Learned empty clause: return to CDCL loop (unsat true) + // - Learned a non-conflicting unit clause: + // Though it does not invalidate the current solution, the solver + // will backtrack to the root level and will repropagate it. The + // search will start again (saved phases hopefully make it quick), + // but it is needed in order to guarantee that every fixed variable + // is properly handled+notified (important for incremental use + // cases). + // - Otherwise: the solution is considered approved and the CDCL-loop + // can return with res = 10. + // + if (unsat || conflict || trail_changed) + break; + has_external_clause = ask_external_clause (); + stats.ext_prop.ext_cb++; + stats.ext_prop.elearn_call++; + } + LOG ("No more external clause to add."); + if (unsat || conflict) + break; + } + + if (!unsat && conflict) { + const int conflict_level = var (conflict->literals[0]).level; + if (conflict_level != level) { + backtrack (conflict_level); + } + } + + return !conflict; +} + +/*----------------------------------------------------------------------------*/ +// +// Notify the external propagator that an observed variable got assigned. +// +void Internal::notify_assignments () { + if (!external_prop || external_prop_is_lazy || private_steps) + return; + + const size_t end_of_trail = trail.size (); + + if (notified >= end_of_trail) + return; + + LOG ("notify external propagator about new assignments"); + std::vector<int> assigned; + + while (notified < end_of_trail) { + int ilit = trail[notified++]; + if (!observed (ilit)) + continue; + + int elit = externalize (ilit); // TODO: double-check tainting + CADICAL_assert (elit); + if (external->ervars[abs (elit)]) + continue; + // Fixed variables might get mapped (during compact) to another + // non-observed but fixed variable. + // This happens on root level, so notification about their assignment is + // already done. + CADICAL_assert (external->observed (elit) || + (fixed (ilit) && !external->ervars[abs (elit)])); + assigned.push_back (elit); + } + + external->propagator->notify_assignment (assigned); + return; +} + +/*----------------------------------------------------------------------------*/ + +void Internal::connect_propagator () { + if (level) + backtrack (); +} + +/*----------------------------------------------------------------------------*/ +// +// Notify the external propagator that a new decision level is started. +// +void Internal::notify_decision () { + if (!external_prop || external_prop_is_lazy || private_steps) + return; + external->propagator->notify_new_decision_level (); +} + +/*----------------------------------------------------------------------------*/ +// +// Notify the external propagator that backtrack to new_level. +// +void Internal::notify_backtrack (size_t new_level) { + if (!external_prop || external_prop_is_lazy || private_steps) + return; + external->propagator->notify_backtrack (new_level); +} + +/*----------------------------------------------------------------------------*/ +// +// Ask the external propagator if there is a suggested literal as next +// decision. +// +int Internal::ask_decision () { + if (!external_prop || external_prop_is_lazy || private_steps) + return 0; + + CADICAL_assert (!unsat); + CADICAL_assert (!conflict); + notify_assignments (); + int level_before = level; + forced_backt_allowed = true; + int elit = external->propagator->cb_decide (); + forced_backt_allowed = false; + stats.ext_prop.ext_cb++; + + if (level_before != level) { + + propagate (); + CADICAL_assert (!unsat); + CADICAL_assert (!conflict); + notify_assignments (); + + // In case the external propagator forced to backtrack below the + // pseduo decision levels, we must go back to the CDCL loop instead of + // making a decision. + if ((size_t) level < assumptions.size () || + ((size_t) level == assumptions.size () && constraint.size ())) { + return 0; + } + } + + if (!elit) + return 0; + LOG ("external propagator proposes decision: %d", elit); + CADICAL_assert (external->is_observed[abs (elit)]); + if (!external->is_observed[abs (elit)]) + return 0; + + int ilit = external->e2i[abs (elit)]; + if (elit < 0) + ilit = -ilit; + + CADICAL_assert (fixed (ilit) || observed (ilit)); + + LOG ("Asking external propagator for decision returned: %d (internal: " + "%d, fixed: %d, val: %d)", + elit, ilit, fixed (ilit), val (ilit)); + + if (fixed (ilit) || val (ilit)) { + LOG ("Proposed decision variable is already assigned, falling back to " + "internal decision."); + return 0; + } + + return ilit; +} + +/*----------------------------------------------------------------------------*/ +// +// Check if the clause is a forgettable clause coming from the external +// propagator. +// +bool Internal::is_external_forgettable (int64_t id) { + CADICAL_assert (opts.check); + return (external->forgettable_original.find (id) != + external->forgettable_original.end ()); +} + +/*----------------------------------------------------------------------------*/ +// +// When an external forgettable clause is deleted, it is marked in the +// 'forgettable_original' hash, so that the internal model checking can +// ignore it. +// +void Internal::mark_garbage_external_forgettable (int64_t id) { + CADICAL_assert (opts.check); + CADICAL_assert (is_external_forgettable (id)); + + LOG (external->forgettable_original[id], + "forgettable external lemma is deleted:"); + // Mark as removed by flipping the first flag to false. + external->forgettable_original[id][0] = 0; +} + +/*----------------------------------------------------------------------------*/ +// +// Check that the literals in the clause are properly ordered. Used only +// internally for debug purposes. +// +void Internal::check_watched_literal_invariants () { +#ifndef CADICAL_NDEBUG + int v0 = 0; + int v1 = 0; + + if (val (clause[0]) > 0) + v0 = 1; + else if (val (clause[0]) < 0) + v0 = -1; + + if (val (clause[1]) > 0) + v1 = 1; + else if (val (clause[1]) < 0) + v1 = -1; + CADICAL_assert (v0 >= v1); +#endif + if (val (clause[0]) > 0) { + if (val (clause[1]) > 0) { // Case 1: Both literals are satisfied + // They are ordered by lower to higher decision level + CADICAL_assert (var (clause[0]).level <= var (clause[1]).level); + + // Every other literal of the clause is either + // - satisfied at higher level + // - unassigned + // - falsified + for (size_t i = 2; i < clause.size (); i++) + CADICAL_assert (val (clause[i]) <= 0 || + (var (clause[1]).level <= var (clause[i]).level)); + + } else if (val (clause[1]) == + 0) { // Case 2: First satisfied, next unassigned + + // Every other literal of the clause is either + // - unassigned + // - falsified + for (size_t i = 2; i < clause.size (); i++) + CADICAL_assert (val (clause[i]) <= 0); + + } else { // Case 3: First satisfied, next falsified -> could have been a + // reason of a previous propagation + // Every other literal of the clause is falsified but at a lower + // decision level + for (size_t i = 2; i < clause.size (); i++) + CADICAL_assert (val (clause[i]) < 0 && + (var (clause[1]).level >= var (clause[i]).level)); + } + } else if (val (clause[0]) == 0) { + if (val (clause[1]) == 0) { // Case 4: Both literals are unassigned + + // Every other literal of the clause is either + // - unassigned + // - falsified + for (size_t i = 2; i < clause.size (); i++) + CADICAL_assert (val (clause[i]) <= 0); + + } else { // Case 5: First unassigned, next falsified -> PROPAGATE + // Every other literal of the clause is falsified but at a lower + // decision level + for (size_t i = 2; i < clause.size (); i++) + CADICAL_assert (val (clause[i]) < 0 && + (var (clause[1]).level >= var (clause[i]).level)); + } + } else { + CADICAL_assert (val (clause[0]) < 0 && + val (clause[1]) < 0); // Case 6: Both literals are falsified + + // They are ordered by higher to lower decision level + CADICAL_assert (var (clause[0]).level >= var (clause[1]).level); + + // Every other literal of the clause is falsified, but at a lower level + for (size_t i = 2; i < clause.size (); i++) + CADICAL_assert (val (clause[i]) < 0 && + (var (clause[1]).level >= var (clause[i]).level)); + } +} + +#ifndef CADICAL_NDEBUG + +/*----------------------------------------------------------------------------*/ +// +// An expensive function that can be used for deep-debug trail-related +// issues in mobical. Do not use it unless it is really unavoidable. +// +// eq_class contains all the merged external literals that are currently +// compacted to the internal literal of trail[0] and return true. +// +// In case trail[0] does not exists or is not on the root level, the +// function returns false (indicating that there was no merger literal +// found). +// +bool Internal::get_merged_literals (std::vector<int> &eq_class) { + eq_class.clear (); + + if (!trail.size ()) + return false; + + int ilit = trail[0]; + size_t lit_level = var (ilit).level; + + if (!lit_level) { + // Collect all the variables that are merged and mapped to that ilit + size_t e2i_size = external->e2i.size (); + int ivar = abs (ilit); + for (size_t i = 0; i < e2i_size; i++) { + int other = abs (external->e2i[i]); + if (other == ivar) { + if (external->e2i[i] == ilit) + eq_class.push_back (i); + else + eq_class.push_back (-1 * i); + } + } + + return true; + } + + return false; +} + +/*----------------------------------------------------------------------------*/ +// +// Collect all external variables that are FIXED internally. Again an +// expensive function that should be called only for debugging in mobical. +// +// Do not use it unless it is really unavoidable. +// +void Internal::get_all_fixed_literals (std::vector<int> &fixed_lits) { + fixed_lits.clear (); + if (!trail.size ()) + return; + + int e2i_size = external->e2i.size (); + int ilit; + for (int eidx = 1; eidx < e2i_size; eidx++) { + ilit = external->e2i[eidx]; + if (ilit && !external->ervars[eidx]) { + Flags &f = flags (ilit); + if (f.status == Flags::FIXED) { + fixed_lits.push_back (vals[abs (ilit)] * eidx); + } + } + } +} +#endif + +} // namespace CaDiCaL + +ABC_NAMESPACE_IMPL_END diff --git a/src/sat/cadical/cadical_factor.cpp b/src/sat/cadical/cadical_factor.cpp new file mode 100644 index 0000000000..3fee406855 --- /dev/null +++ b/src/sat/cadical/cadical_factor.cpp @@ -0,0 +1,927 @@ +#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 diff --git a/src/sat/cadical/cadical_file.cpp b/src/sat/cadical/cadical_file.cpp new file mode 100644 index 0000000000..98f19f752d --- /dev/null +++ b/src/sat/cadical/cadical_file.cpp @@ -0,0 +1,527 @@ +#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 diff --git a/src/sat/cadical/cadical_flags.cpp b/src/sat/cadical/cadical_flags.cpp new file mode 100644 index 0000000000..5c06a33b4a --- /dev/null +++ b/src/sat/cadical/cadical_flags.cpp @@ -0,0 +1,141 @@ +#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 diff --git a/src/sat/cadical/cadical_flip.cpp b/src/sat/cadical/cadical_flip.cpp new file mode 100644 index 0000000000..a89e3abb7c --- /dev/null +++ b/src/sat/cadical/cadical_flip.cpp @@ -0,0 +1,275 @@ +#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 diff --git a/src/sat/cadical/cadical_format.cpp b/src/sat/cadical/cadical_format.cpp new file mode 100644 index 0000000000..942d9d74a4 --- /dev/null +++ b/src/sat/cadical/cadical_format.cpp @@ -0,0 +1,95 @@ +#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 diff --git a/src/sat/cadical/cadical_frattracer.cpp b/src/sat/cadical/cadical_frattracer.cpp new file mode 100644 index 0000000000..d35a182719 --- /dev/null +++ b/src/sat/cadical/cadical_frattracer.cpp @@ -0,0 +1,283 @@ +#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 diff --git a/src/sat/cadical/cadical_gates.cpp b/src/sat/cadical/cadical_gates.cpp new file mode 100644 index 0000000000..06a73d1ebd --- /dev/null +++ b/src/sat/cadical/cadical_gates.cpp @@ -0,0 +1,772 @@ +#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 diff --git a/src/sat/cadical/cadical_idruptracer.cpp b/src/sat/cadical/cadical_idruptracer.cpp new file mode 100644 index 0000000000..ec6467163b --- /dev/null +++ b/src/sat/cadical/cadical_idruptracer.cpp @@ -0,0 +1,572 @@ +#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 diff --git a/src/sat/cadical/cadical_instantiate.cpp b/src/sat/cadical/cadical_instantiate.cpp new file mode 100644 index 0000000000..2c34015b2e --- /dev/null +++ b/src/sat/cadical/cadical_instantiate.cpp @@ -0,0 +1,371 @@ +#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 diff --git a/src/sat/cadical/cadical_internal.cpp b/src/sat/cadical/cadical_internal.cpp new file mode 100644 index 0000000000..abe18c59cd --- /dev/null +++ b/src/sat/cadical/cadical_internal.cpp @@ -0,0 +1,1196 @@ +#include "global.h" + +#include "internal.hpp" + +ABC_NAMESPACE_IMPL_START + +namespace CaDiCaL { + +/*------------------------------------------------------------------------*/ +static Clause external_reason_clause; + +Internal::Internal () + : mode (SEARCH), unsat (false), iterating (false), + localsearching (false), lookingahead (false), preprocessing (false), + protected_reasons (false), force_saved_phase (false), + searching_lucky_phases (false), stable (false), reported (false), + external_prop (false), did_external_prop (false), + external_prop_is_lazy (true), forced_backt_allowed (false), + + private_steps (false), rephased (0), vsize (0), max_var (0), + clause_id (0), original_id (0), reserved_ids (0), conflict_id (0), + saved_decisions (0), concluded (false), lrat (false), frat (false), + level (0), vals (0), score_inc (1.0), scores (this), conflict (0), + ignore (0), external_reason (&external_reason_clause), + newest_clause (0), force_no_backtrack (false), + from_propagator (false), ext_clause_forgettable (false), + tainted_literal (0), notified (0), probe_reason (0), propagated (0), + propagated2 (0), propergated (0), best_assigned (0), + target_assigned (0), no_conflict_until (0), unsat_constraint (false), + marked_failed (true), sweep_incomplete (false), citten (0), + num_assigned (0), proof (0), opts (this), +#ifndef CADICAL_QUIET + profiles (this), force_phase_messages (false), +#endif + arena (this), prefix ("c "), internal (this), external (0), + termination_forced (false), vars (this->max_var), + lits (this->max_var) { + control.push_back (Level (0, 0)); + + // The 'dummy_binary' is used in 'try_to_subsume_clause' to fake a real + // clause, which then can be used to subsume or strengthen the given + // clause in one routine for both binary and non binary clauses. This + // fake binary clause is always kept non-redundant (and not-moved etc.) + // due to the following 'memset'. Only literals will be changed. + + // In a previous version we used local automatic allocated 'Clause' on the + // stack, which became incompatible with several compilers (see the + // discussion on flexible array member in 'Clause.cpp'). + + size_t bytes = Clause::bytes (2); + dummy_binary = (Clause *) new char[bytes]; + memset (dummy_binary, 0, bytes); + dummy_binary->size = 2; +} + +Internal::~Internal () { + // If a memory exception ocurred a profile might still be active. +#ifndef CADICAL_QUIET +#define PROFILE(NAME, LEVEL) \ + if (PROFILE_ACTIVE (NAME)) \ + STOP (NAME); + PROFILES +#undef PROFILE +#endif + delete[] (char *) dummy_binary; + for (const auto &c : clauses) + delete_clause (c); + if (proof) + delete proof; + for (auto &tracer : tracers) + delete tracer; + for (auto &filetracer : file_tracers) + delete filetracer; + for (auto &stattracer : stat_tracers) + delete stattracer; + if (vals) { + vals -= vsize; + delete[] vals; + } +} + +/*------------------------------------------------------------------------*/ + +// Values in 'vals' can be accessed in the range '[-max_var,max_var]' that +// is directly by a literal. This is crucial for performance. By shifting +// the start of 'vals' appropriately, we achieve that negative offsets from +// the start of 'vals' can be used. We also need to set both values at +// 'lit' and '-lit' during assignments. In MiniSAT integer literals are +// encoded, using the least significant bit as negation. This avoids taking +// the 'abs ()' (as in our solution) and thus also avoids a branch in the +// hot-spot of the solver (clause traversal in propagation). That solution +// requires another (branch less) negation of the values though and +// debugging is harder since literals occur only encoded in clauses. +// The main draw-back of our solution is that we have to shift the memory +// and access it through negative indices, which looks less clean (but still +// as far I can tell is properly defined C / C++). You might get a warning +// by static analyzers though. Clang with '--analyze' thought that this +// idiom would generate a memory leak thus we use the following dummy. + +static signed char *ignore_clang_analyze_memory_leak_warning; + +void Internal::enlarge_vals (size_t new_vsize) { + signed char *new_vals; + const size_t bytes = 2u * new_vsize; + new_vals = new signed char[bytes]; // g++-4.8 does not like ... { 0 }; + memset (new_vals, 0, bytes); + ignore_clang_analyze_memory_leak_warning = new_vals; + new_vals += new_vsize; + + if (vals) { + memcpy (new_vals - max_var, vals - max_var, 2u * max_var + 1u); + vals -= vsize; + delete[] vals; + } else + CADICAL_assert (!vsize); + vals = new_vals; +} + +/*------------------------------------------------------------------------*/ + +void Internal::enlarge (int new_max_var) { + // New variables can be created that can invoke enlarge anytime (via calls + // during ipasir-up call-backs), thus assuming (!level) is not correct + size_t new_vsize = vsize ? 2 * vsize : 1 + (size_t) new_max_var; + while (new_vsize <= (size_t) new_max_var) + new_vsize *= 2; + LOG ("enlarge internal size from %zd to new size %zd", vsize, new_vsize); + // Ordered in the size of allocated memory (larger block first). + if (lrat || frat) + enlarge_zero (unit_clauses_idx, 2 * new_vsize); + enlarge_only (wtab, 2 * new_vsize); + enlarge_only (vtab, new_vsize); + enlarge_zero (parents, new_vsize); + enlarge_only (links, new_vsize); + enlarge_zero (btab, new_vsize); + enlarge_zero (gtab, new_vsize); + enlarge_zero (stab, new_vsize); + enlarge_init (ptab, 2 * new_vsize, -1); + enlarge_only (ftab, new_vsize); + enlarge_vals (new_vsize); + vsize = new_vsize; + if (external) + enlarge_zero (relevanttab, new_vsize); + const signed char val = opts.phase ? 1 : -1; + enlarge_init (phases.saved, new_vsize, val); + enlarge_zero (phases.forced, new_vsize); + enlarge_zero (phases.target, new_vsize); + enlarge_zero (phases.best, new_vsize); + enlarge_zero (phases.prev, new_vsize); + enlarge_zero (phases.min, new_vsize); + enlarge_zero (marks, new_vsize); +} + +void Internal::init_vars (int new_max_var) { + if (new_max_var <= max_var) + return; + // New variables can be created that can invoke enlarge anytime (via calls + // during ipasir-up call-backs), thus assuming (!level) is not correct + LOG ("initializing %d internal variables from %d to %d", + new_max_var - max_var, max_var + 1, new_max_var); + if ((size_t) new_max_var >= vsize) + enlarge (new_max_var); +#ifndef CADICAL_NDEBUG + for (int64_t i = -new_max_var; i < -max_var; i++) + CADICAL_assert (!vals[i]); + for (unsigned i = max_var + 1; i <= (unsigned) new_max_var; i++) + CADICAL_assert (!vals[i]), CADICAL_assert (!btab[i]), CADICAL_assert (!gtab[i]); + for (uint64_t i = 2 * ((uint64_t) max_var + 1); + i <= 2 * (uint64_t) new_max_var + 1; i++) + CADICAL_assert (ptab[i] == -1); +#endif + CADICAL_assert (!btab[0]); + int old_max_var = max_var; + max_var = new_max_var; + init_queue (old_max_var, new_max_var); + init_scores (old_max_var, new_max_var); + int initialized = new_max_var - old_max_var; + stats.vars += initialized; + stats.unused += initialized; + stats.inactive += initialized; + LOG ("finished initializing %d internal variables", initialized); +} + +void Internal::add_original_lit (int lit) { + CADICAL_assert (abs (lit) <= max_var); + if (lit) { + original.push_back (lit); + } else { + const int64_t id = + original_id < reserved_ids ? ++original_id : ++clause_id; + if (proof) { + // Use the external form of the clause for printing in proof + // Externalize(internalized literal) != external literal + CADICAL_assert (!original.size () || !external->eclause.empty ()); + proof->add_external_original_clause (id, false, external->eclause); + } + if (internal->opts.check && + (internal->opts.checkwitness || internal->opts.checkfailed)) { + bool forgettable = from_propagator && ext_clause_forgettable; + if (forgettable && opts.check) { + CADICAL_assert (!original.size () || !external->eclause.empty ()); + + // First integer is the presence-flag (even if the clause is empty) + external->forgettable_original[id] = {1}; + + for (auto const &elit : external->eclause) + external->forgettable_original[id].push_back (elit); + + LOG (external->eclause, + "clause added to external forgettable map:"); + } + } + + add_new_original_clause (id); + original.clear (); + } +} + +void Internal::finish_added_clause_with_id (int64_t id, bool restore) { + if (proof) { + // Use the external form of the clause for printing in proof + // Externalize(internalized literal) != external literal + CADICAL_assert (!original.size () || !external->eclause.empty ()); + proof->add_external_original_clause (id, false, external->eclause, + restore); + } + add_new_original_clause (id); + original.clear (); +} + +/*------------------------------------------------------------------------*/ + +void Internal::reserve_ids (int number) { + // return; + LOG ("reserving %d ids", number); + CADICAL_assert (number >= 0); + CADICAL_assert (!clause_id && !reserved_ids && !original_id); + clause_id = reserved_ids = number; + if (proof) + proof->begin_proof (reserved_ids); +} + +/*------------------------------------------------------------------------*/ + +#ifdef PROFILE_MODE + +// Separating these makes it easier to profile stable and unstable search. + +bool Internal::propagate_wrapper () { + if (stable) + return propagate_stable (); + else + return propagate_unstable (); +} + +void Internal::analyze_wrapper () { + if (stable) + analyze_stable (); + else + analyze_unstable (); +} + +int Internal::decide_wrapper () { + if (stable) + return decide_stable (); + else + return decide_unstable (); +} + +#endif + +/*------------------------------------------------------------------------*/ + +// This is the main CDCL loop with interleaved inprocessing. + +int Internal::cdcl_loop_with_inprocessing () { + + int res = 0; + + START (search); + + if (stable) { + START (stable); + report ('['); + } else { + START (unstable); + report ('{'); + } + + while (!res) { + if (unsat) + res = 20; + else if (unsat_constraint) + res = 20; + else if (!propagate_wrapper ()) + analyze_wrapper (); // propagate and analyze + else if (iterating) + iterate (); // report learned unit + else if (!external_propagate () || unsat) { // external propagation + if (unsat) + continue; + else + analyze (); + } else if (satisfied ()) { // found model + if (!external_check_solution () || unsat) { + if (unsat) + continue; + else + analyze (); + } else if (satisfied ()) + res = 10; + } else if (search_limits_hit ()) + break; // decision or conflict limit + else if (terminated_asynchronously ()) // externally terminated + break; + else if (restarting ()) + restart (); // restart by backtracking + else if (rephasing ()) + rephase (); // reset variable phases + else if (reducing ()) + reduce (); // collect useless clauses + else if (inprobing ()) + inprobe (); // schedule of inprocessing + else if (ineliminating ()) + elim (); // variable elimination + else if (compacting ()) + compact (); // collect variables + else if (conditioning ()) + condition (); // globally blocked clauses + else + res = decide (); // next decision + } + + if (stable) { + STOP (stable); + report (']'); + } else { + STOP (unstable); + report ('}'); + } + + STOP (search); + + return res; +} + +int Internal::propagate_assumptions () { + if (proof) + proof->solve_query (); + if (opts.ilb) { + if (opts.ilbassumptions) + sort_and_reuse_assumptions (); + stats.ilbtriggers++; + stats.ilbsuccess += (level > 0); + stats.levelsreused += level; + if (level) { + CADICAL_assert (control.size () > 1); + stats.literalsreused += num_assigned - control[1].trail; + } + } + init_search_limits (); + init_report_limits (); + + int res = already_solved (); // root-level propagation is done here + + int last_assumption_level = assumptions.size (); + if (constraint.size ()) + last_assumption_level++; + + if (!res) { + restore_clauses (); + while (!res) { + if (unsat) + res = 20; + else if (unsat_constraint) + res = 20; + else if (!propagate ()) { + // let analyze run to get failed assumptions + analyze (); + } else if (!external_propagate () || unsat) { // external propagation + if (unsat) + continue; + else + analyze (); + } else if (satisfied ()) { // found model + if (!external_check_solution () || unsat) { + if (unsat) + continue; + else + analyze (); + } else if (satisfied ()) + res = 10; + } else if (search_limits_hit ()) + break; // decision or conflict limit + else if (terminated_asynchronously ()) // externally terminated + break; + else { + if (level >= last_assumption_level) + break; + res = decide (); + } + } + } + + if (unsat || unsat_constraint) + res = 20; + + if (!res && satisfied ()) + res = 10; + + finalize (res); + reset_solving (); + report_solving (res); + + return res; +} + +void Internal::implied (std::vector<int> &entrailed) { + int last_assumption_level = assumptions.size (); + if (constraint.size ()) + last_assumption_level++; + + size_t trail_limit = trail.size(); + if (level > last_assumption_level) + trail_limit = control[last_assumption_level + 1].trail; + + for (size_t i = 0; i < trail_limit; i++) + entrailed.push_back (trail[i]); +} + +/*------------------------------------------------------------------------*/ + +// Most of the limits are only initialized in the first 'solve' call and +// increased as in a stand-alone non-incremental SAT call except for those +// explicitly marked as being reset below. + +void Internal::init_report_limits () { + reported = false; + lim.report = 0; + lim.recompute_tier = 5000; +} + +void Internal::init_preprocessing_limits () { + + const bool incremental = lim.initialized; + if (incremental) + LOG ("reinitializing preprocessing limits incrementally"); + else + LOG ("initializing preprocessing limits and increments"); + + const char *mode = 0; + + /*----------------------------------------------------------------------*/ + + if (incremental) + mode = "keeping"; + else { + last.elim.marked = -1; + lim.elim = stats.conflicts + scale (opts.elimint); + mode = "initial"; + } + (void) mode; + LOG ("%s elim limit %" PRId64 " after %" PRId64 " conflicts", mode, + lim.elim, lim.elim - stats.conflicts); + + // Initialize and reset elimination bounds in any case. + + lim.elimbound = opts.elimboundmin; + LOG ("elimination bound %" PRId64 "", lim.elimbound); + + /*----------------------------------------------------------------------*/ + + if (!incremental) { + + last.ternary.marked = -1; // TODO this should not be necessary... + + lim.compact = stats.conflicts + opts.compactint; + LOG ("initial compact limit %" PRId64 " increment %" PRId64 "", + lim.compact, lim.compact - stats.conflicts); + } + + /*----------------------------------------------------------------------*/ + + if (incremental) + mode = "keeping"; + else { + double delta = log10 (stats.added.irredundant); + delta = delta * delta; + lim.inprobe = stats.conflicts + opts.inprobeint * delta; + mode = "initial"; + } + (void) mode; + LOG ("%s probe limit %" PRId64 " after %" PRId64 " conflicts", mode, + lim.inprobe, lim.inprobe - stats.conflicts); + + /*----------------------------------------------------------------------*/ + + if (incremental) + mode = "keeping"; + else { + lim.condition = stats.conflicts + opts.conditionint; + mode = "initial"; + } + LOG ("%s condition limit %" PRId64 " increment %" PRId64, mode, + lim.condition, lim.condition - stats.conflicts); + + /*----------------------------------------------------------------------*/ + + // Initial preprocessing rounds. + + if (inc.preprocessing <= 0) { + lim.preprocessing = 0; + LOG ("no preprocessing"); + } else { + lim.preprocessing = inc.preprocessing; + LOG ("limiting to %" PRId64 " preprocessing rounds", lim.preprocessing); + } +} + +void Internal::init_search_limits () { + + const bool incremental = lim.initialized; + if (incremental) + LOG ("reinitializing search limits incrementally"); + else + LOG ("initializing search limits and increments"); + + const char *mode = 0; + + /*----------------------------------------------------------------------*/ + + if (incremental) + mode = "keeping"; + else { + last.reduce.conflicts = -1; + lim.reduce = stats.conflicts + opts.reduceinit; + mode = "initial"; + } + (void) mode; + LOG ("%s reduce limit %" PRId64 " after %" PRId64 " conflicts", mode, + lim.reduce, lim.reduce - stats.conflicts); + + /*----------------------------------------------------------------------*/ + + if (incremental) + mode = "keeping"; + else { + lim.flush = opts.flushint; + inc.flush = opts.flushint; + mode = "initial"; + } + (void) mode; + LOG ("%s flush limit %" PRId64 " interval %" PRId64 "", mode, lim.flush, + inc.flush); + + /*----------------------------------------------------------------------*/ + + // Initialize or reset 'rephase' limits in any case. + + lim.rephase = stats.conflicts + opts.rephaseint; + lim.rephased[0] = lim.rephased[1] = 0; + LOG ("new rephase limit %" PRId64 " after %" PRId64 " conflicts", + lim.rephase, lim.rephase - stats.conflicts); + + /*----------------------------------------------------------------------*/ + + // Initialize or reset 'restart' limits in any case. + + lim.restart = stats.conflicts + opts.restartint; + LOG ("new restart limit %" PRId64 " increment %" PRId64 "", lim.restart, + lim.restart - stats.conflicts); + + /*----------------------------------------------------------------------*/ + + if (!incremental) { + stable = opts.stabilize && opts.stabilizeonly; + if (stable) + LOG ("starting in always forced stable phase"); + else + LOG ("starting in default non-stable phase"); + init_averages (); + } else if (opts.stabilize && opts.stabilizeonly) { + LOG ("keeping always forced stable phase"); + CADICAL_assert (stable); + } else if (stable) { + LOG ("switching back to default non-stable phase"); + stable = false; + swap_averages (); + } else + LOG ("keeping non-stable phase"); + + if (!incremental) { + inc.stabilize = 0; + lim.stabilize = stats.conflicts + opts.stabilizeinit; + LOG ("initial stabilize limit %" PRId64 " after %d conflicts", + lim.stabilize, (int) opts.stabilizeinit); + } + + if (opts.stabilize && opts.reluctant) { + LOG ("new restart reluctant doubling sequence period %d", + opts.reluctant); + reluctant.enable (opts.reluctant, opts.reluctantmax); + } else + reluctant.disable (); + + /*----------------------------------------------------------------------*/ + + // Conflict and decision limits. + + if (inc.conflicts < 0) { + lim.conflicts = -1; + LOG ("no limit on conflicts"); + } else { + lim.conflicts = stats.conflicts + inc.conflicts; + LOG ("conflict limit after %" PRId64 " conflicts at %" PRId64 + " conflicts", + inc.conflicts, lim.conflicts); + } + + if (inc.decisions < 0) { + lim.decisions = -1; + LOG ("no limit on decisions"); + } else { + lim.decisions = stats.decisions + inc.decisions; + LOG ("conflict limit after %" PRId64 " decisions at %" PRId64 + " decisions", + inc.decisions, lim.decisions); + } + + /*----------------------------------------------------------------------*/ + + // Initial preprocessing rounds. + + if (inc.localsearch <= 0) { + lim.localsearch = 0; + LOG ("no local search"); + } else { + lim.localsearch = inc.localsearch; + LOG ("limiting to %" PRId64 " local search rounds", lim.localsearch); + } + + /*----------------------------------------------------------------------*/ + + lim.initialized = true; +} + +/*------------------------------------------------------------------------*/ + +bool Internal::preprocess_round (int round) { + (void) round; + if (unsat) + return false; + if (!max_var) + return false; + START (preprocess); + struct { + int64_t vars, clauses; + } before, after; + before.vars = active (); + before.clauses = stats.current.irredundant; + stats.preprocessings++; + CADICAL_assert (!preprocessing); + preprocessing = true; + PHASE ("preprocessing", stats.preprocessings, + "starting round %d with %" PRId64 " variables and %" PRId64 + " clauses", + round, before.vars, before.clauses); + int old_elimbound = lim.elimbound; + if (opts.inprobing) + inprobe (false); + if (opts.elim) + elim (false); + if (opts.condition) + condition (false); + + after.vars = active (); + after.clauses = stats.current.irredundant; + CADICAL_assert (preprocessing); + preprocessing = false; + PHASE ("preprocessing", stats.preprocessings, + "finished round %d with %" PRId64 " variables and %" PRId64 + " clauses", + round, after.vars, after.clauses); + STOP (preprocess); + report ('P'); + if (unsat) + return false; + if (after.vars < before.vars) + return true; + if (old_elimbound < lim.elimbound) + return true; + return false; +} + +// for now counts as one of the preprocessing rounds TODO: change this? +void Internal::preprocess_quickly () { + if (unsat) + return; + if (!max_var) + return; + if (!opts.preprocesslight) + return; + START (preprocess); + struct { + int64_t vars, clauses; + } before, after; + before.vars = active (); + before.clauses = stats.current.irredundant; + // stats.preprocessings++; + CADICAL_assert (!preprocessing); + preprocessing = true; + PHASE ("preprocessing", stats.preprocessings, + "starting with %" PRId64 " variables and %" PRId64 " clauses", + before.vars, before.clauses); + + if (extract_gates ()) + decompose (); + + if (sweep ()) + decompose (); + + if (opts.factor) + factor (); + + if (opts.fastelim) + elimfast (); + // if (opts.condition) + // condition (false); + after.vars = active (); + after.clauses = stats.current.irredundant; + CADICAL_assert (preprocessing); + preprocessing = false; + PHASE ("preprocessing", stats.preprocessings, + "finished with %" PRId64 " variables and %" PRId64 " clauses", + after.vars, after.clauses); + STOP (preprocess); + report ('P'); +} + +int Internal::preprocess () { + preprocess_quickly (); + for (int i = 0; i < lim.preprocessing; i++) + if (!preprocess_round (i)) + break; + if (unsat) + return 20; + return 0; +} + +/*------------------------------------------------------------------------*/ + +int Internal::try_to_satisfy_formula_by_saved_phases () { + LOG ("satisfying formula by saved phases"); + CADICAL_assert (!level); + CADICAL_assert (!force_saved_phase); + CADICAL_assert (propagated == trail.size ()); + force_saved_phase = true; + if (external_prop) { + CADICAL_assert (!level); + LOG ("external notifications are turned off during preprocessing."); + private_steps = true; + } + int res = 0; + while (!res) { + if (satisfied ()) { + LOG ("formula indeed satisfied by saved phases"); + res = 10; + } else if (decide ()) { + LOG ("inconsistent assumptions with redundant clauses and phases"); + res = 20; + } else if (!propagate ()) { + LOG ("saved phases do not satisfy redundant clauses"); + CADICAL_assert (level > 0); + backtrack (); + conflict = 0; // ignore conflict + CADICAL_assert (!res); + break; + } + } + CADICAL_assert (force_saved_phase); + force_saved_phase = false; + if (external_prop) { + private_steps = false; + LOG ("external notifications are turned back on."); + if (!level) + notify_assignments (); // In case fixed assignments were found. + else { + renotify_trail_after_local_search (); + } + } + return res; +} + +/*------------------------------------------------------------------------*/ + +void Internal::produce_failed_assumptions () { + LOG ("producing failed assumptions"); + CADICAL_assert (!level); + CADICAL_assert (!assumptions.empty ()); + while (!unsat) { + CADICAL_assert (!satisfied ()); + notify_assignments (); + if (decide ()) + break; + while (!unsat && !propagate ()) + analyze (); + } + notify_assignments (); + if (unsat) + LOG ("formula is actually unsatisfiable unconditionally"); + else + LOG ("assumptions indeed failing"); +} + +/*------------------------------------------------------------------------*/ + +int Internal::local_search_round (int round) { + + CADICAL_assert (round > 0); + + if (unsat) + return false; + if (!max_var) + return false; + + START_OUTER_WALK (); + CADICAL_assert (!localsearching); + localsearching = true; + + // Determine propagation limit quadratically scaled with rounds. + // + int64_t limit = opts.walkmineff; + limit *= round; + if (LONG_MAX / round > limit) + limit *= round; + else + limit = LONG_MAX; + + int res = walk_round (limit, true); + + CADICAL_assert (localsearching); + localsearching = false; + STOP_OUTER_WALK (); + + report ('L'); + + return res; +} + +int Internal::local_search () { + + if (unsat) + return 0; + if (!max_var) + return 0; + if (!opts.walk) + return 0; + if (constraint.size ()) + return 0; + + int res = 0; + + for (int i = 1; !res && i <= lim.localsearch; i++) + res = local_search_round (i); + + if (res == 10) { + LOG ("local search determined formula to be satisfiable"); + CADICAL_assert (!stats.walk.minimum); + res = try_to_satisfy_formula_by_saved_phases (); + } else if (res == 20) { + LOG ("local search determined assumptions to be inconsistent"); + CADICAL_assert (!assumptions.empty ()); + produce_failed_assumptions (); + } + + return res; +} + +/*------------------------------------------------------------------------*/ + +// if preprocess_only is false and opts.ilb is true we do not preprocess +// such that we do not have to backtrack to level 0. +// +int Internal::solve (bool preprocess_only) { + CADICAL_assert (clause.empty ()); + START (solve); + if (proof) + proof->solve_query (); + if (opts.ilb) { + if (opts.ilbassumptions) + sort_and_reuse_assumptions (); + stats.ilbtriggers++; + stats.ilbsuccess += (level > 0); + stats.levelsreused += level; + if (level) { + CADICAL_assert (control.size () > 1); + stats.literalsreused += num_assigned - control[1].trail; + } + if (external->propagator) + renotify_trail_after_ilb (); + } + if (preprocess_only) + LOG ("internal solving in preprocessing only mode"); + else + LOG ("internal solving in full mode"); + init_report_limits (); + int res = already_solved (); + if (!res && preprocess_only && level) + backtrack (); + if (!res) + res = restore_clauses (); + if (!res || (res == 10 && external_prop)) { + init_preprocessing_limits (); + if (!preprocess_only) + init_search_limits (); + } + if (!preprocess_only) { + if (!res && !level) + res = local_search (); + } + if (!res && !level) + res = preprocess (); + if (!preprocess_only) { + if (!res && !level) + res = local_search (); + if (!res && !level) + res = lucky_phases (); + if (!res || (res == 10 && external_prop)) { + if (res == 10 && external_prop && level) + backtrack (); + res = cdcl_loop_with_inprocessing (); + } + } + finalize (res); + reset_solving (); + report_solving (res); + STOP (solve); + return res; +} + +int Internal::already_solved () { + int res = 0; + if (unsat || unsat_constraint) { + LOG ("already inconsistent"); + res = 20; + } else { + if (level && !opts.ilb) + backtrack (); + if (!level && !propagate ()) { + LOG ("root level propagation produces conflict"); + learn_empty_clause (); + res = 20; + } + if (max_var == 0 && res == 0) + res = 10; + } + return res; +} +void Internal::report_solving (int res) { + if (res == 10) + report ('1'); + else if (res == 20) + report ('0'); + else + report ('?'); +} + +void Internal::reset_solving () { + if (termination_forced) { + + // TODO this leads potentially to a data race if the external + // user is calling 'terminate' twice within one 'solve' call. + // A proper solution would be to guard / protect setting the + // 'termination_forced' flag and only allow it during solving and + // ignore it otherwise thus also the second time it is called during a + // 'solve' call. We could move resetting it also the start of + // 'solve'. + // + termination_forced = false; + + LOG ("reset forced termination"); + } +} + +int Internal::restore_clauses () { + int res = 0; + if (opts.restoreall <= 1 && external->tainted.empty ()) { + LOG ("no tainted literals and nothing to restore"); + report ('*'); + } else { + report ('+'); + // remove_garbage_binaries (); + external->restore_clauses (); + internal->report ('r'); + if (!unsat && !level && !propagate ()) { + LOG ("root level propagation after restore produces conflict"); + learn_empty_clause (); + res = 20; + } + } + return res; +} + +int Internal::lookahead () { + CADICAL_assert (clause.empty ()); + START (lookahead); + CADICAL_assert (!lookingahead); + lookingahead = true; + if (external_prop) { + if (level) { + // Combining lookahead with external propagator is limited + // Note that lookahead_probing (); would also force backtrack anyway + backtrack (); + } + LOG ("external notifications are turned off during preprocessing."); + private_steps = true; + } + int tmp = already_solved (); + if (!tmp) + tmp = restore_clauses (); + int res = 0; + if (!tmp) + res = lookahead_probing (); + if (res == INT_MIN) + res = 0; + reset_solving (); + report_solving (tmp); + CADICAL_assert (lookingahead); + lookingahead = false; + STOP (lookahead); + if (external_prop) { + private_steps = false; + LOG ("external notifications are turned back on."); + notify_assignments (); // In case fixed assignments were found. + } + return res; +} + +/*------------------------------------------------------------------------*/ + +void Internal::finalize (int res) { + if (!proof) + return; + LOG ("finalizing"); + // finalize external units + if (frat) { + for (const auto &evar : external->vars) { + CADICAL_assert (evar > 0); + const auto eidx = 2 * evar; + int sign = 1; + int64_t id = external->ext_units[eidx]; + if (!id) { + sign = -1; + id = external->ext_units[eidx + 1]; + } + if (id) { + proof->finalize_external_unit (id, evar * sign); + } + } + // finalize internal units + for (const auto &lit : lits) { + const auto elit = externalize (lit); + if (elit) { + const unsigned eidx = (elit < 0) + 2u * (unsigned) abs (elit); + const int64_t id = external->ext_units[eidx]; + if (id) { + CADICAL_assert (unit_clauses (vlit (lit)) == id); + continue; + } + } + const int64_t id = unit_clauses (vlit (lit)); + if (!id) + continue; + proof->finalize_unit (id, lit); + } + // See the discussion in 'propagate' on why garbage binary clauses stick + // around. + for (const auto &c : clauses) + if (!c->garbage || (c->size == 2 && !c->flushed)) + proof->finalize_clause (c); + + // finalize conflict and proof + if (conflict_id) { + proof->finalize_clause (conflict_id, {}); + } + } + proof->report_status (res, conflict_id); + if (res == 10) + external->conclude_sat (); + else if (res == 20) + conclude_unsat (); + else if (!res) + external->conclude_unknown (); +} + +/*------------------------------------------------------------------------*/ + +void Internal::print_statistics () { + stats.print (this); + for (auto &st : stat_tracers) + st->print_stats (); +} + +/*------------------------------------------------------------------------*/ + +// Only useful for debugging purposes. + +void Internal::dump (Clause *c) { + for (const auto &lit : *c) + printf ("%d ", lit); + printf ("0\n"); +} + +void Internal::dump () { + int64_t m = assumptions.size (); + for (auto idx : vars) + if (fixed (idx)) + m++; + for (const auto &c : clauses) + if (!c->garbage) + m++; + printf ("p cnf %d %" PRId64 "\n", max_var, m); + for (auto idx : vars) { + const int tmp = fixed (idx); + if (tmp) + printf ("%d 0\n", tmp < 0 ? -idx : idx); + } + for (const auto &c : clauses) + if (!c->garbage) + dump (c); + for (const auto &lit : assumptions) + printf ("%d 0\n", lit); + fflush (stdout); +} + +/*------------------------------------------------------------------------*/ + +bool Internal::traverse_constraint (ClauseIterator &it) { + if (constraint.empty () && !unsat_constraint) + return true; + + vector<int> eclause; + if (unsat) + return it.clause (eclause); + + LOG (constraint, "traversing constraint"); + bool satisfied = false; + for (auto ilit : constraint) { + const int tmp = fixed (ilit); + if (tmp > 0) { + satisfied = true; + break; + } + if (tmp < 0) + continue; + const int elit = externalize (ilit); + eclause.push_back (elit); + } + if (!satisfied && !it.clause (eclause)) + return false; + + return true; +} +/*------------------------------------------------------------------------*/ + +bool Internal::traverse_clauses (ClauseIterator &it) { + vector<int> eclause; + if (unsat) + return it.clause (eclause); + for (const auto &c : clauses) { + if (c->garbage) + continue; + if (c->redundant) + continue; + bool satisfied = false; + for (const auto &ilit : *c) { + const int tmp = fixed (ilit); + if (tmp > 0) { + satisfied = true; + break; + } + if (tmp < 0) + continue; + const int elit = externalize (ilit); + eclause.push_back (elit); + } + if (!satisfied && !it.clause (eclause)) + return false; + eclause.clear (); + } + return true; +} + +} // namespace CaDiCaL + +ABC_NAMESPACE_IMPL_END diff --git a/src/sat/cadical/cadical_ipasir.cpp b/src/sat/cadical/cadical_ipasir.cpp new file mode 100644 index 0000000000..cb88deb4b2 --- /dev/null +++ b/src/sat/cadical/cadical_ipasir.cpp @@ -0,0 +1,46 @@ +#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 diff --git a/src/sat/cadical/cadical_kitten.c b/src/sat/cadical/cadical_kitten.c new file mode 100644 index 0000000000..da37389803 --- /dev/null +++ b/src/sat/cadical/cadical_kitten.c @@ -0,0 +1,2609 @@ +#include "global.h" + +#include "kitten.h" +#include "random.h" +#include "stack.h" + +#include <assert.h> +#include <inttypes.h> +#include <limits.h> +#include <stdarg.h> +#include <stdbool.h> +#include <stdint.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +ABC_NAMESPACE_IMPL_START + +typedef signed char value; + +static void die (const char *fmt, ...) { + fputs ("cadical_kitten: error: ", stderr); + va_list ap; + va_start (ap, fmt); + vfprintf (stderr, fmt, ap); + va_end (ap); + fputc ('\n', stderr); + exit (1); +} + +static inline void *cadical_kitten_calloc (size_t n, size_t size) { + void *res = calloc (n, size); + if (n && size && !res) + die ("out of memory allocating '%zu * %zu' bytes", n, size); + return res; +} + +#define CALLOC(T, P, N) \ + do { \ + (P) = (T*)cadical_kitten_calloc (N, sizeof *(P)); \ + } while (0) +#define DEALLOC(P, N) free (P) + +#undef ENLARGE_STACK + +#define ENLARGE_STACK(S) \ + do { \ + CADICAL_assert (FULL_STACK (S)); \ + const size_t SIZE = SIZE_STACK (S); \ + const size_t OLD_CAPACITY = CAPACITY_STACK (S); \ + const size_t NEW_CAPACITY = OLD_CAPACITY ? 2 * OLD_CAPACITY : 1; \ + const size_t BYTES = NEW_CAPACITY * sizeof *(S).begin; \ + (S).begin = (unsigned*)realloc ((S).begin, BYTES); \ + if (!(S).begin) \ + die ("out of memory reallocating '%zu' bytes", BYTES); \ + (S).allocated = (S).begin + NEW_CAPACITY; \ + (S).end = (S).begin + SIZE; \ + } while (0) + +// Beside allocators above also use stand alone statistics counters. + +#define INC(NAME) \ + do { \ + statistics *statistics = &cadical_kitten->statistics; \ + CADICAL_assert (statistics->NAME < UINT64_MAX); \ + statistics->NAME++; \ + } while (0) + +#define ADD(NAME, DELTA) \ + do { \ + statistics *statistics = &cadical_kitten->statistics; \ + CADICAL_assert (statistics->NAME <= UINT64_MAX - (DELTA)); \ + statistics->NAME += (DELTA); \ + } while (0) + +#define KITTEN_TICKS (cadical_kitten->statistics.cadical_kitten_ticks) + +#define INVALID UINT_MAX +#define MAX_VARS ((1u << 31) - 1) + +#define CORE_FLAG (1u) +#define LEARNED_FLAG (2u) + +// clang-format off + +typedef struct kar kar; +typedef struct kink kink; +typedef struct klause klause; +typedef STACK (unsigned) klauses; +typedef unsigneds katches; + +// clang-format on + +struct kar { + unsigned level; + unsigned reason; +}; + +struct kink { + unsigned next; + unsigned prev; + uint64_t stamp; +}; + +struct klause { + unsigned aux; + unsigned size; + unsigned flags; + unsigned lits[1]; +}; + +typedef struct statistics statistics; + +struct statistics { + uint64_t learned; + uint64_t original; + uint64_t cadical_kitten_flip; + uint64_t cadical_kitten_flipped; + uint64_t cadical_kitten_sat; + uint64_t cadical_kitten_solved; + uint64_t cadical_kitten_conflicts; + uint64_t cadical_kitten_decisions; + uint64_t cadical_kitten_propagations; + uint64_t cadical_kitten_ticks; + uint64_t cadical_kitten_unknown; + uint64_t cadical_kitten_unsat; +}; + +typedef struct kimits kimits; + +struct kimits { + uint64_t ticks; +}; + +struct cadical_kitten { + // First zero initialized field in 'clear_cadical_kitten' is 'status'. + // + int status; + +#if defined(LOGGING) + bool logging; +#endif + bool antecedents; + bool learned; + + unsigned level; + unsigned propagated; + unsigned unassigned; + unsigned inconsistent; + unsigned failing; + + uint64_t generator; + + size_t lits; + size_t evars; + + size_t end_original_ref; + + struct { + unsigned first, last; + uint64_t stamp; + unsigned search; + } queue; + + // The 'size' field below is the first not zero reinitialized field + // by 'memset' in 'clear_cadical_kitten' (after 'kissat'). + + size_t size; + size_t esize; + + kar *vars; + kink *links; + value *marks; + value *values; + bool *failed; + unsigned char *phases; + unsigned *import; + katches *watches; + + unsigneds analyzed; + unsigneds assumptions; + unsigneds core; + unsigneds rcore; + unsigneds eclause; + unsigneds export_; + unsigneds klause; + unsigneds klauses; + unsigneds resolved; + unsigneds trail; + unsigneds units; + unsigneds prime[2]; + + kimits limits; + int (*terminator) (void *); + void *terminator_data; + unsigneds clause; + uint64_t initialized; + statistics statistics; +}; + +/*------------------------------------------------------------------------*/ + +static inline bool is_core_klause (klause *c) { + return c->flags & CORE_FLAG; +} + +static inline bool is_learned_klause (klause *c) { + return c->flags & LEARNED_FLAG; +} + +static inline void set_core_klause (klause *c) { c->flags |= CORE_FLAG; } + +static inline void unset_core_klause (klause *c) { c->flags &= ~CORE_FLAG; } + +static inline klause *dereference_klause (cadical_kitten *cadical_kitten, unsigned ref) { + unsigned *res = BEGIN_STACK (cadical_kitten->klauses) + ref; + CADICAL_assert (res < END_STACK (cadical_kitten->klauses)); + return (klause *) res; +} + +static inline unsigned reference_klause (cadical_kitten *cadical_kitten, const klause *c) { + const unsigned *const begin = BEGIN_STACK (cadical_kitten->klauses); + const unsigned *p = (const unsigned *) c; + CADICAL_assert (begin <= p); + CADICAL_assert (p < END_STACK (cadical_kitten->klauses)); + const unsigned res = p - begin; + return res; +} + +/*------------------------------------------------------------------------*/ + +#define KATCHES(KIT) (cadical_kitten->watches[CADICAL_assert ((KIT) < cadical_kitten->lits), (KIT)]) + +#define all_klauses(C) \ + klause *C = begin_klauses (cadical_kitten), *end_##C = end_klauses (cadical_kitten); \ + (C) != end_##C; \ + (C) = next_klause (cadical_kitten, C) + +#define all_original_klauses(C) \ + klause *C = begin_klauses (cadical_kitten), \ + *end_##C = end_original_klauses (cadical_kitten); \ + (C) != end_##C; \ + (C) = next_klause (cadical_kitten, C) + +#define all_learned_klauses(C) \ + klause *C = begin_learned_klauses (cadical_kitten), \ + *end_##C = end_klauses (cadical_kitten); \ + (C) != end_##C; \ + (C) = next_klause (cadical_kitten, C) + +#define all_kits(KIT) \ + size_t KIT = 0, KIT_##END = cadical_kitten->lits; \ + KIT != KIT_##END; \ + KIT++ + +#define BEGIN_KLAUSE(C) (C)->lits + +#define END_KLAUSE(C) (BEGIN_KLAUSE (C) + (C)->size) + +#define all_literals_in_klause(KIT, C) \ + unsigned KIT, *KIT##_PTR = BEGIN_KLAUSE (C), \ + *KIT##_END = END_KLAUSE (C); \ + KIT##_PTR != KIT##_END && ((KIT = *KIT##_PTR), true); \ + ++KIT##_PTR + +#define all_antecedents(REF, C) \ + unsigned REF, *REF##_PTR = antecedents (C), \ + *REF##_END = REF##_PTR + (C)->aux; \ + REF##_PTR != REF##_END && ((REF = *REF##_PTR), true); \ + ++REF##_PTR + +#ifdef LOGGING + +#define logging (cadical_kitten->logging) + +static void log_basic (cadical_kitten *, const char *, ...) + __attribute__ ((format (printf, 2, 3))); + +static void log_basic (cadical_kitten *cadical_kitten, const char *fmt, ...) { + CADICAL_assert (logging); + printf ("c KITTEN %u ", cadical_kitten->level); + va_list ap; + va_start (ap, fmt); + vprintf (fmt, ap); + va_end (ap); + fputc ('\n', stdout); + fflush (stdout); +} + +static void log_reference (cadical_kitten *, unsigned, const char *, ...) + __attribute__ ((format (printf, 3, 4))); + +static void log_reference (cadical_kitten *cadical_kitten, unsigned ref, const char *fmt, + ...) { + klause *c = dereference_klause (cadical_kitten, ref); + CADICAL_assert (logging); + printf ("c KITTEN %u ", cadical_kitten->level); + va_list ap; + va_start (ap, fmt); + vprintf (fmt, ap); + va_end (ap); + if (is_learned_klause (c)) { + fputs (" learned", stdout); + if (c->aux) + printf ("[%u]", c->aux); + } else { + fputs (" original", stdout); + if (c->aux != INVALID) + printf ("[%u]", c->aux); + } + printf (" size %u clause[%u]", c->size, ref); + value *values = cadical_kitten->values; + kar *vars = cadical_kitten->vars; + for (all_literals_in_klause (lit, c)) { + printf (" %u", lit); + const value value = values[lit]; + if (value) + printf ("@%u=%d", vars[lit / 2].level, (int) value); + } + fputc ('\n', stdout); + fflush (stdout); +} + +static void log_literals (cadical_kitten *, unsigned *, unsigned, const char *, ...) + __attribute__ ((format (printf, 4, 5))); + +static void log_literals (cadical_kitten *cadical_kitten, unsigned *lits, unsigned size, + const char *fmt, ...) { + CADICAL_assert (logging); + printf ("c KITTEN %u ", cadical_kitten->level); + va_list ap; + va_start (ap, fmt); + vprintf (fmt, ap); + va_end (ap); + value *values = cadical_kitten->values; + kar *vars = cadical_kitten->vars; + for (unsigned i = 0; i < size; i++) { + const unsigned lit = lits[i]; + printf (" %u", lit); + const value value = values[lit]; + if (value) + printf ("@%u=%d", vars[lit / 2].level, (int) value); + } + fputc ('\n', stdout); + fflush (stdout); +} + +#define LOG(...) \ + do { \ + if (logging) \ + log_basic (cadical_kitten, __VA_ARGS__); \ + } while (0) + +#define ROG(...) \ + do { \ + if (logging) \ + log_reference (cadical_kitten, __VA_ARGS__); \ + } while (0) + +#define LOGLITS(...) \ + do { \ + if (logging) \ + log_literals (cadical_kitten, __VA_ARGS__); \ + } while (0) + +#else + +#define LOG(...) \ + do { \ + } while (0) +#define ROG(...) \ + do { \ + } while (0) +#define LOGLITS(...) \ + do { \ + } while (0) + +#endif + +static void check_queue (cadical_kitten *cadical_kitten) { +#ifdef CHECK_KITTEN + const unsigned vars = cadical_kitten->lits / 2; + unsigned found = 0, prev = INVALID; + kink *links = cadical_kitten->links; + uint64_t stamp = 0; + for (unsigned idx = cadical_kitten->queue.first, next; idx != INVALID; + idx = next) { + kink *link = links + idx; + CADICAL_assert (link->prev == prev); + CADICAL_assert (!found || stamp < link->stamp); + CADICAL_assert (link->stamp < cadical_kitten->queue.stamp); + stamp = link->stamp; + next = link->next; + prev = idx; + found++; + } + CADICAL_assert (found == vars); + unsigned next = INVALID; + found = 0; + for (unsigned idx = cadical_kitten->queue.last, prev; idx != INVALID; + idx = prev) { + kink *link = links + idx; + CADICAL_assert (link->next == next); + prev = link->prev; + next = idx; + found++; + } + CADICAL_assert (found == vars); + value *values = cadical_kitten->values; + bool first = true; + for (unsigned idx = cadical_kitten->queue.search, next; idx != INVALID; + idx = next) { + kink *link = links + idx; + next = link->next; + const unsigned lit = 2 * idx; + CADICAL_assert (first || values[lit]); + first = false; + } +#else + (void) cadical_kitten; +#endif +} + +static void update_search (cadical_kitten *cadical_kitten, unsigned idx) { + if (cadical_kitten->queue.search == idx) + return; + cadical_kitten->queue.search = idx; + LOG ("search updated to %u stamped %" PRIu64, idx, + cadical_kitten->links[idx].stamp); +} + +static void enqueue (cadical_kitten *cadical_kitten, unsigned idx) { + LOG ("enqueue %u", idx); + kink *links = cadical_kitten->links; + kink *l = links + idx; + const unsigned last = cadical_kitten->queue.last; + if (last == INVALID) + cadical_kitten->queue.first = idx; + else + links[last].next = idx; + l->prev = last; + l->next = INVALID; + cadical_kitten->queue.last = idx; + l->stamp = cadical_kitten->queue.stamp++; + LOG ("stamp %" PRIu64, l->stamp); +} + +static void dequeue (cadical_kitten *cadical_kitten, unsigned idx) { + LOG ("dequeue %u", idx); + kink *links = cadical_kitten->links; + kink *l = links + idx; + const unsigned prev = l->prev; + const unsigned next = l->next; + if (prev == INVALID) + cadical_kitten->queue.first = next; + else + links[prev].next = next; + if (next == INVALID) + cadical_kitten->queue.last = prev; + else + links[next].prev = prev; +} + +static void init_queue (cadical_kitten *cadical_kitten, size_t old_vars, size_t new_vars) { + for (size_t idx = old_vars; idx < new_vars; idx++) { + CADICAL_assert (!cadical_kitten->values[2 * idx]); + CADICAL_assert (cadical_kitten->unassigned < UINT_MAX); + cadical_kitten->unassigned++; + enqueue (cadical_kitten, idx); + } + LOG ("initialized decision queue from %zu to %zu", old_vars, new_vars); + update_search (cadical_kitten, cadical_kitten->queue.last); + check_queue (cadical_kitten); +} + +static void initialize_cadical_kitten (cadical_kitten *cadical_kitten) { + cadical_kitten->queue.first = INVALID; + cadical_kitten->queue.last = INVALID; + cadical_kitten->inconsistent = INVALID; + cadical_kitten->failing = INVALID; + cadical_kitten->queue.search = INVALID; + cadical_kitten->terminator = 0; + cadical_kitten->terminator_data = 0; + cadical_kitten->limits.ticks = UINT64_MAX; + cadical_kitten->generator = cadical_kitten->initialized++; +} + +static void clear_cadical_kitten (cadical_kitten *cadical_kitten) { + size_t bytes = (char *) &cadical_kitten->size - (char *) &cadical_kitten->status; + memset (&cadical_kitten->status, 0, bytes); + memset (&cadical_kitten->statistics, 0, sizeof (statistics)); + initialize_cadical_kitten (cadical_kitten); +} + +#define RESIZE1(T, P) \ + do { \ + void *OLD_PTR = (P); \ + CALLOC (T, (P), new_size / 2); \ + const size_t BYTES = old_vars * sizeof *(P); \ + memcpy ((P), OLD_PTR, BYTES); \ + void *NEW_PTR = (P); \ + (P) = (T*)OLD_PTR; \ + DEALLOC ((P), old_size / 2); \ + (P) = (T*)NEW_PTR; \ + } while (0) + +#define RESIZE2(T, P) \ + do { \ + void *OLD_PTR = (P); \ + CALLOC (T, (P), new_size); \ + const size_t BYTES = old_lits * sizeof *(P); \ + memcpy ((P), OLD_PTR, BYTES); \ + void *NEW_PTR = (P); \ + (P) = (T*)OLD_PTR; \ + DEALLOC ((P), old_size); \ + (P) = (T*)NEW_PTR; \ + } while (0) + +static void enlarge_internal (cadical_kitten *cadical_kitten, size_t lit) { + const size_t new_lits = (lit | 1) + 1; + const size_t old_lits = cadical_kitten->lits; + CADICAL_assert (old_lits <= lit); + CADICAL_assert (old_lits < new_lits); + CADICAL_assert ((lit ^ 1) < new_lits); + CADICAL_assert (lit < new_lits); + const size_t old_size = cadical_kitten->size; + const unsigned new_vars = new_lits / 2; + const unsigned old_vars = old_lits / 2; + if (old_size < new_lits) { + size_t new_size = old_size ? 2 * old_size : 2; + while (new_size <= lit) + new_size *= 2; + LOG ("internal literals resized to %zu from %zu (requested %zu)", + new_size, old_size, new_lits); + + RESIZE1 (value, cadical_kitten->marks); + RESIZE1 (unsigned char, cadical_kitten->phases); + RESIZE2 (value, cadical_kitten->values); + RESIZE2 (bool, cadical_kitten->failed); + RESIZE1 (kar, cadical_kitten->vars); + RESIZE1 (kink, cadical_kitten->links); + RESIZE2 (katches, cadical_kitten->watches); + + cadical_kitten->size = new_size; + } + cadical_kitten->lits = new_lits; + init_queue (cadical_kitten, old_vars, new_vars); + LOG ("internal literals activated until %zu literals", new_lits); + return; +} + +static const char *status_to_string (int status) { + switch (status) { + case 10: + return "formula satisfied"; + case 11: + return "formula satisfied and prime implicant computed"; + case 20: + return "formula inconsistent"; + case 21: + return "formula inconsistent and core computed"; + default: + CADICAL_assert (!status); + return "formula unsolved"; + } +} + +static void invalid_api_usage (const char *fun, const char *fmt, ...) { + fprintf (stderr, "cadical_kitten: fatal error: invalid API usage in '%s': ", fun); + va_list ap; + va_start (ap, fmt); + vfprintf (stderr, fmt, ap); + va_end (ap); + fputc ('\n', stderr); + fflush (stderr); + abort (); +} + +#define INVALID_API_USAGE(...) invalid_api_usage (__func__, __VA_ARGS__) + +#define REQUIRE_INITIALIZED() \ + do { \ + if (!cadical_kitten) \ + INVALID_API_USAGE ("solver argument zero"); \ + } while (0) + +#define REQUIRE_STATUS(EXPECTED) \ + do { \ + REQUIRE_INITIALIZED (); \ + if (cadical_kitten->status != (EXPECTED)) \ + INVALID_API_USAGE ("invalid status '%s' (expected '%s')", \ + status_to_string (cadical_kitten->status), \ + status_to_string (EXPECTED)); \ + } while (0) + +#define UPDATE_STATUS(STATUS) \ + do { \ + if (cadical_kitten->status != (STATUS)) \ + LOG ("updating status from '%s' to '%s'", \ + status_to_string (cadical_kitten->status), status_to_string (STATUS)); \ + else \ + LOG ("keeping status at '%s'", status_to_string (STATUS)); \ + cadical_kitten->status = (STATUS); \ + } while (0) + +cadical_kitten *cadical_kitten_init (void) { + cadical_kitten *cadical_kitten; + CALLOC (struct cadical_kitten, cadical_kitten, 1); + initialize_cadical_kitten (cadical_kitten); + return cadical_kitten; +} + +#ifdef LOGGING +void cadical_kitten_set_logging (cadical_kitten *cadical_kitten) { logging = true; } +#endif + +void cadical_kitten_track_antecedents (cadical_kitten *cadical_kitten) { + REQUIRE_STATUS (0); + + if (cadical_kitten->learned) + INVALID_API_USAGE ("can not start tracking antecedents after learning"); + + LOG ("enabling antecedents tracking"); + cadical_kitten->antecedents = true; +} + +void cadical_kitten_randomize_phases (cadical_kitten *cadical_kitten) { + REQUIRE_INITIALIZED (); + + LOG ("randomizing phases"); + + unsigned char *phases = cadical_kitten->phases; + const unsigned vars = cadical_kitten->size / 2; + + uint64_t random = kissat_next_random64 (&cadical_kitten->generator); + + unsigned i = 0; + const unsigned rest = vars & ~63u; + + while (i != rest) { + uint64_t *p = (uint64_t *) (phases + i); + p[0] = (random >> 0) & 0x0101010101010101; + p[1] = (random >> 1) & 0x0101010101010101; + p[2] = (random >> 2) & 0x0101010101010101; + p[3] = (random >> 3) & 0x0101010101010101; + p[4] = (random >> 4) & 0x0101010101010101; + p[5] = (random >> 5) & 0x0101010101010101; + p[6] = (random >> 6) & 0x0101010101010101; + p[7] = (random >> 7) & 0x0101010101010101; + random = kissat_next_random64 (&cadical_kitten->generator); + i += 64; + } + + unsigned shift = 0; + while (i != vars) + phases[i++] = (random >> shift++) & 1; +} + +void cadical_kitten_flip_phases (cadical_kitten *cadical_kitten) { + REQUIRE_INITIALIZED (); + + LOG ("flipping phases"); + + unsigned char *phases = cadical_kitten->phases; + const unsigned vars = cadical_kitten->size / 2; + + unsigned i = 0; + const unsigned rest = vars & ~7u; + + while (i != rest) { + uint64_t *p = (uint64_t *) (phases + i); + *p ^= 0x0101010101010101; + i += 8; + } + + while (i != vars) + phases[i++] ^= 1; +} + +void cadical_kitten_no_ticks_limit (cadical_kitten *cadical_kitten) { + REQUIRE_INITIALIZED (); + LOG ("forcing no ticks limit"); + cadical_kitten->limits.ticks = UINT64_MAX; +} + +uint64_t cadical_kitten_current_ticks (cadical_kitten *cadical_kitten) { + REQUIRE_INITIALIZED (); + const uint64_t current = KITTEN_TICKS; + return current; +} + +void cadical_kitten_set_ticks_limit (cadical_kitten *cadical_kitten, uint64_t delta) { + REQUIRE_INITIALIZED (); + const uint64_t current = KITTEN_TICKS; + uint64_t limit; + if (UINT64_MAX - delta <= current) { + LOG ("forcing unlimited ticks limit"); + limit = UINT64_MAX; + } else { + limit = current + delta; + LOG ("new limit of %" PRIu64 " ticks after %" PRIu64, limit, delta); + } + + cadical_kitten->limits.ticks = limit; +} + +void cadical_kitten_no_terminator (cadical_kitten *cadical_kitten) { + REQUIRE_INITIALIZED (); + LOG ("removing terminator"); + cadical_kitten->terminator = 0; + cadical_kitten->terminator_data = 0; +} + +void cadical_kitten_set_terminator (cadical_kitten *cadical_kitten, void *data, + int (*terminator) (void *)) { + REQUIRE_INITIALIZED (); + LOG ("setting terminator"); + cadical_kitten->terminator = terminator; + cadical_kitten->terminator_data = data; +} + +static void shuffle_unsigned_array (cadical_kitten *cadical_kitten, size_t size, + unsigned *a) { + for (size_t i = 0; i < size; i++) { + const size_t j = kissat_pick_random (&cadical_kitten->generator, 0, i); + if (j == i) + continue; + const unsigned first = a[i]; + const unsigned second = a[j]; + a[i] = second; + a[j] = first; + } +} + +static void shuffle_unsigned_stack (cadical_kitten *cadical_kitten, unsigneds *stack) { + const size_t size = SIZE_STACK (*stack); + unsigned *a = BEGIN_STACK (*stack); + shuffle_unsigned_array (cadical_kitten, size, a); +} + +static void shuffle_katches (cadical_kitten *cadical_kitten) { + LOG ("shuffling watch lists"); + for (size_t lit = 0; lit < cadical_kitten->lits; lit++) + shuffle_unsigned_stack (cadical_kitten, &KATCHES (lit)); +} + +static void shuffle_queue (cadical_kitten *cadical_kitten) { + LOG ("shuffling variable decision order"); + + const unsigned vars = cadical_kitten->lits / 2; + for (unsigned i = 0; i < vars; i++) { + const unsigned idx = kissat_pick_random (&cadical_kitten->generator, 0, vars); + dequeue (cadical_kitten, idx); + enqueue (cadical_kitten, idx); + } + update_search (cadical_kitten, cadical_kitten->queue.last); +} + +static void shuffle_units (cadical_kitten *cadical_kitten) { + LOG ("shuffling units"); + shuffle_unsigned_stack (cadical_kitten, &cadical_kitten->units); +} + +void cadical_kitten_shuffle_clauses (cadical_kitten *cadical_kitten) { + REQUIRE_STATUS (0); + shuffle_queue (cadical_kitten); + shuffle_katches (cadical_kitten); + shuffle_units (cadical_kitten); +} + +static inline unsigned *antecedents (klause *c) { + CADICAL_assert (is_learned_klause (c)); + return c->lits + c->size; +} + +static inline void watch_klause (cadical_kitten *cadical_kitten, unsigned lit, + unsigned ref) { + ROG (ref, "watching %u in", lit); + katches *watches = &KATCHES (lit); + PUSH_STACK (*watches, ref); +} + +static inline void connect_new_klause (cadical_kitten *cadical_kitten, unsigned ref) { + ROG (ref, "new"); + + klause *c = dereference_klause (cadical_kitten, ref); + + if (!c->size) { + if (cadical_kitten->inconsistent == INVALID) { + ROG (ref, "registering inconsistent empty"); + cadical_kitten->inconsistent = ref; + } else + ROG (ref, "ignoring inconsistent empty"); + } else if (c->size == 1) { + ROG (ref, "watching unit"); + PUSH_STACK (cadical_kitten->units, ref); + } else { + watch_klause (cadical_kitten, c->lits[0], ref); + watch_klause (cadical_kitten, c->lits[1], ref); + } +} + +static unsigned new_reference (cadical_kitten *cadical_kitten) { + size_t ref = SIZE_STACK (cadical_kitten->klauses); + if (ref >= INVALID) { + die ("maximum number of literals exhausted"); + } + const unsigned res = (unsigned) ref; + CADICAL_assert (res != INVALID); + INC (cadical_kitten_ticks); + return res; +} + +static void new_original_klause (cadical_kitten *cadical_kitten, unsigned id) { + unsigned res = new_reference (cadical_kitten); + unsigned size = SIZE_STACK (cadical_kitten->klause); + unsigneds *klauses = &cadical_kitten->klauses; + PUSH_STACK (*klauses, id); + PUSH_STACK (*klauses, size); + PUSH_STACK (*klauses, 0); + for (all_stack (unsigned, lit, cadical_kitten->klause)) + PUSH_STACK (*klauses, lit); + connect_new_klause (cadical_kitten, res); + cadical_kitten->end_original_ref = SIZE_STACK (*klauses); + cadical_kitten->statistics.original++; +} + +static void enlarge_external (cadical_kitten *cadical_kitten, size_t eidx) { + const size_t old_size = cadical_kitten->esize; + const unsigned old_evars = cadical_kitten->evars; + CADICAL_assert (old_evars <= eidx); + const unsigned new_evars = eidx + 1; + if (old_size <= eidx) { + size_t new_size = old_size ? 2 * old_size : 1; + while (new_size <= eidx) + new_size *= 2; + LOG ("external resizing to %zu variables from %zu (requested %u)", + new_size, old_size, new_evars); + unsigned *old_import = cadical_kitten->import; + CALLOC (unsigned, cadical_kitten->import, new_size); + const size_t bytes = old_evars * sizeof *cadical_kitten->import; + memcpy (cadical_kitten->import, old_import, bytes); + DEALLOC (old_import, old_size); + cadical_kitten->esize = new_size; + } + cadical_kitten->evars = new_evars; + LOG ("external variables enlarged to %u", new_evars); +} + +static unsigned import_literal (cadical_kitten *cadical_kitten, unsigned elit) { + const unsigned eidx = elit / 2; + if (eidx >= cadical_kitten->evars) + enlarge_external (cadical_kitten, eidx); + + unsigned iidx = cadical_kitten->import[eidx]; + if (!iidx) { + iidx = SIZE_STACK (cadical_kitten->export_); + PUSH_STACK (cadical_kitten->export_, eidx); + cadical_kitten->import[eidx] = iidx + 1; + } else + iidx--; + unsigned ilit = 2 * iidx + (elit & 1); + LOG ("imported external literal %u as internal literal %u", elit, ilit); + if (ilit >= cadical_kitten->lits) + enlarge_internal (cadical_kitten, ilit); + return ilit; +} + +static unsigned export_literal (cadical_kitten *cadical_kitten, unsigned ilit) { + const unsigned iidx = ilit / 2; + CADICAL_assert (iidx < SIZE_STACK (cadical_kitten->export_)); + const unsigned eidx = PEEK_STACK (cadical_kitten->export_, iidx); + const unsigned elit = 2 * eidx + (ilit & 1); + return elit; +} + +unsigned cadical_new_learned_klause (cadical_kitten *cadical_kitten) { + unsigned res = new_reference (cadical_kitten); + unsigneds *klauses = &cadical_kitten->klauses; + const size_t size = SIZE_STACK (cadical_kitten->klause); + CADICAL_assert (size <= UINT_MAX); + const size_t aux = + cadical_kitten->antecedents ? SIZE_STACK (cadical_kitten->resolved) : 0; + CADICAL_assert (aux <= UINT_MAX); + PUSH_STACK (*klauses, (unsigned) aux); + PUSH_STACK (*klauses, (unsigned) size); + PUSH_STACK (*klauses, LEARNED_FLAG); + for (all_stack (unsigned, lit, cadical_kitten->klause)) + PUSH_STACK (*klauses, lit); + if (aux) + for (all_stack (unsigned, ref, cadical_kitten->resolved)) + PUSH_STACK (*klauses, ref); + connect_new_klause (cadical_kitten, res); + cadical_kitten->learned = true; + cadical_kitten->statistics.learned++; + return res; +} + +void cadical_kitten_clear (cadical_kitten *cadical_kitten) { + LOG ("clear cadical_kitten of size %zu", cadical_kitten->size); + + CADICAL_assert (EMPTY_STACK (cadical_kitten->analyzed)); + CADICAL_assert (EMPTY_STACK (cadical_kitten->eclause)); + CADICAL_assert (EMPTY_STACK (cadical_kitten->resolved)); + + CLEAR_STACK (cadical_kitten->assumptions); + CLEAR_STACK (cadical_kitten->core); + CLEAR_STACK (cadical_kitten->klause); + CLEAR_STACK (cadical_kitten->klauses); + CLEAR_STACK (cadical_kitten->trail); + CLEAR_STACK (cadical_kitten->units); + CLEAR_STACK (cadical_kitten->clause); + CLEAR_STACK (cadical_kitten->prime[0]); + CLEAR_STACK (cadical_kitten->prime[1]); + + for (all_kits (kit)) + CLEAR_STACK (KATCHES (kit)); + + while (!EMPTY_STACK (cadical_kitten->export_)) + cadical_kitten->import[POP_STACK (cadical_kitten->export_)] = 0; + + const size_t lits = cadical_kitten->size; + const unsigned vars = lits / 2; + +#ifndef CADICAL_NDEBUG + for (unsigned i = 0; i < vars; i++) + CADICAL_assert (!cadical_kitten->marks[i]); +#endif + + memset (cadical_kitten->phases, 0, vars); + memset (cadical_kitten->values, 0, lits); + memset (cadical_kitten->failed, 0, lits); + memset (cadical_kitten->vars, 0, vars); + + clear_cadical_kitten (cadical_kitten); +} + +void cadical_kitten_release (cadical_kitten *cadical_kitten) { + RELEASE_STACK (cadical_kitten->analyzed); + RELEASE_STACK (cadical_kitten->assumptions); + RELEASE_STACK (cadical_kitten->core); + RELEASE_STACK (cadical_kitten->eclause); + RELEASE_STACK (cadical_kitten->export_); + RELEASE_STACK (cadical_kitten->klause); + RELEASE_STACK (cadical_kitten->klauses); + RELEASE_STACK (cadical_kitten->resolved); + RELEASE_STACK (cadical_kitten->trail); + RELEASE_STACK (cadical_kitten->units); + RELEASE_STACK (cadical_kitten->clause); + RELEASE_STACK (cadical_kitten->prime[0]); + RELEASE_STACK (cadical_kitten->prime[1]); + + for (size_t lit = 0; lit < cadical_kitten->size; lit++) + RELEASE_STACK (cadical_kitten->watches[lit]); + + const size_t lits = cadical_kitten->size; + const unsigned vars = lits / 2; + DEALLOC (cadical_kitten->marks, vars); + DEALLOC (cadical_kitten->phases, vars); + DEALLOC (cadical_kitten->values, lits); + DEALLOC (cadical_kitten->failed, lits); + DEALLOC (cadical_kitten->vars, vars); + DEALLOC (cadical_kitten->links, vars); + DEALLOC (cadical_kitten->watches, lits); + DEALLOC (cadical_kitten->import, cadical_kitten->esize); + (void) vars; + + free (cadical_kitten); +} + +static inline void move_to_front (cadical_kitten *cadical_kitten, unsigned idx) { + if (idx == cadical_kitten->queue.last) + return; + LOG ("move to front variable %u", idx); + dequeue (cadical_kitten, idx); + enqueue (cadical_kitten, idx); + CADICAL_assert (cadical_kitten->values[2 * idx]); +} + +static inline void assign (cadical_kitten *cadical_kitten, unsigned lit, unsigned reason) { +#ifdef LOGGING + if (reason == INVALID) + LOG ("assign %u as decision", lit); + else + ROG (reason, "assign %u reason", lit); +#endif + value *values = cadical_kitten->values; + const unsigned not_lit = lit ^ 1; + CADICAL_assert (!values[lit]); + CADICAL_assert (!values[not_lit]); + values[lit] = 1; + values[not_lit] = -1; + const unsigned idx = lit / 2; + const unsigned sign = lit & 1; + cadical_kitten->phases[idx] = sign; + PUSH_STACK (cadical_kitten->trail, lit); + kar *v = cadical_kitten->vars + idx; + v->level = cadical_kitten->level; + if (!v->level) { + CADICAL_assert (reason != INVALID); + klause *c = dereference_klause (cadical_kitten, reason); + if (c->size > 1) { + if (cadical_kitten->antecedents) { + PUSH_STACK (cadical_kitten->resolved, reason); + for (all_literals_in_klause (other, c)) + if (other != lit) { + const unsigned other_idx = other / 2; + const unsigned other_ref = cadical_kitten->vars[other_idx].reason; + CADICAL_assert (other_ref != INVALID); + PUSH_STACK (cadical_kitten->resolved, other_ref); + } + } + PUSH_STACK (cadical_kitten->klause, lit); + reason = cadical_new_learned_klause (cadical_kitten); + CLEAR_STACK (cadical_kitten->resolved); + CLEAR_STACK (cadical_kitten->klause); + } + } + v->reason = reason; + CADICAL_assert (cadical_kitten->unassigned); + cadical_kitten->unassigned--; +} + +static inline unsigned propagate_literal (cadical_kitten *cadical_kitten, unsigned lit) { + LOG ("propagating %u", lit); + value *values = cadical_kitten->values; + CADICAL_assert (values[lit] > 0); + const unsigned not_lit = lit ^ 1; + katches *watches = cadical_kitten->watches + not_lit; + unsigned conflict = INVALID; + unsigned *q = BEGIN_STACK (*watches); + const unsigned *const end_watches = END_STACK (*watches); + unsigned const *p = q; + uint64_t ticks = (((char *) end_watches - (char *) q) >> 7) + 1; + while (p != end_watches) { + const unsigned ref = *q++ = *p++; + klause *c = dereference_klause (cadical_kitten, ref); + CADICAL_assert (c->size > 1); + unsigned *lits = c->lits; + const unsigned other = lits[0] ^ lits[1] ^ not_lit; + const value other_value = values[other]; + ticks++; + if (other_value > 0) + continue; + value replacement_value = -1; + unsigned replacement = INVALID; + const unsigned *const end_lits = lits + c->size; + unsigned *r; + for (r = lits + 2; r != end_lits; r++) { + replacement = *r; + replacement_value = values[replacement]; + if (replacement_value >= 0) + break; + } + if (replacement_value >= 0) { + CADICAL_assert (replacement != INVALID); + ROG (ref, "unwatching %u in", not_lit); + lits[0] = other; + lits[1] = replacement; + *r = not_lit; + watch_klause (cadical_kitten, replacement, ref); + q--; + } else if (other_value < 0) { + ROG (ref, "conflict"); + INC (cadical_kitten_conflicts); + conflict = ref; + break; + } else { + CADICAL_assert (!other_value); + assign (cadical_kitten, other, ref); + } + } + while (p != end_watches) + *q++ = *p++; + SET_END_OF_STACK (*watches, q); + ADD (cadical_kitten_ticks, ticks); + return conflict; +} + +static inline unsigned propagate (cadical_kitten *cadical_kitten) { + CADICAL_assert (cadical_kitten->inconsistent == INVALID); + unsigned propagated = 0; + unsigned conflict = INVALID; + while (conflict == INVALID && + cadical_kitten->propagated < SIZE_STACK (cadical_kitten->trail)) { + if (cadical_kitten->terminator && + cadical_kitten->terminator (cadical_kitten->terminator_data)) { + break; + } + const unsigned lit = PEEK_STACK (cadical_kitten->trail, cadical_kitten->propagated); + conflict = propagate_literal (cadical_kitten, lit); + cadical_kitten->propagated++; + propagated++; + } + ADD (cadical_kitten_propagations, propagated); + return conflict; +} + +static void bump (cadical_kitten *cadical_kitten) { + value *marks = cadical_kitten->marks; + for (all_stack (unsigned, idx, cadical_kitten->analyzed)) { + marks[idx] = 0; + move_to_front (cadical_kitten, idx); + } + check_queue (cadical_kitten); +} + +static inline void unassign (cadical_kitten *cadical_kitten, value *values, unsigned lit) { + const unsigned not_lit = lit ^ 1; + CADICAL_assert (values[lit]); + CADICAL_assert (values[not_lit]); + const unsigned idx = lit / 2; +#ifdef LOGGING + kar *var = cadical_kitten->vars + idx; + cadical_kitten->level = var->level; + LOG ("unassign %u", lit); +#endif + values[lit] = values[not_lit] = 0; + CADICAL_assert (cadical_kitten->unassigned < cadical_kitten->lits / 2); + cadical_kitten->unassigned++; + kink *links = cadical_kitten->links; + kink *link = links + idx; + if (link->stamp > links[cadical_kitten->queue.search].stamp) + update_search (cadical_kitten, idx); +} + +static void backtrack (cadical_kitten *cadical_kitten, unsigned jump) { + check_queue (cadical_kitten); + CADICAL_assert (jump < cadical_kitten->level); + LOG ("back%s to level %u", + (cadical_kitten->level == jump + 1 ? "tracking" : "jumping"), jump); + kar *vars = cadical_kitten->vars; + value *values = cadical_kitten->values; + unsigneds *trail = &cadical_kitten->trail; + while (!EMPTY_STACK (*trail)) { + const unsigned lit = TOP_STACK (*trail); + const unsigned idx = lit / 2; + const unsigned level = vars[idx].level; + if (level == jump) + break; + (void) POP_STACK (*trail); + unassign (cadical_kitten, values, lit); + } + cadical_kitten->propagated = SIZE_STACK (*trail); + cadical_kitten->level = jump; + check_queue (cadical_kitten); +} + +void cadical_completely_backtrack_to_root_level (cadical_kitten *cadical_kitten) { + check_queue (cadical_kitten); + LOG ("completely backtracking to level 0"); + value *values = cadical_kitten->values; + unsigneds *trail = &cadical_kitten->trail; + unsigneds *units = &cadical_kitten->units; +#ifndef CADICAL_NDEBUG + kar *vars = cadical_kitten->vars; +#endif + for (all_stack (unsigned, lit, *trail)) { + CADICAL_assert (vars[lit / 2].level); + unassign (cadical_kitten, values, lit); + } + CLEAR_STACK (*trail); + for (all_stack (unsigned, ref, *units)) { + klause *c = dereference_klause (cadical_kitten, ref); + CADICAL_assert (c->size == 1); + const unsigned unit = c->lits[0]; + const value value = values[unit]; + if (value <= 0) + continue; + unassign (cadical_kitten, values, unit); + } + cadical_kitten->propagated = 0; + cadical_kitten->level = 0; + check_queue (cadical_kitten); +} + +static void analyze (cadical_kitten *cadical_kitten, unsigned conflict) { + CADICAL_assert (cadical_kitten->level); + CADICAL_assert (cadical_kitten->inconsistent == INVALID); + CADICAL_assert (EMPTY_STACK (cadical_kitten->analyzed)); + CADICAL_assert (EMPTY_STACK (cadical_kitten->resolved)); + CADICAL_assert (EMPTY_STACK (cadical_kitten->klause)); + PUSH_STACK (cadical_kitten->klause, INVALID); + unsigned reason = conflict; + value *marks = cadical_kitten->marks; + const kar *const vars = cadical_kitten->vars; + const unsigned level = cadical_kitten->level; + unsigned const *p = END_STACK (cadical_kitten->trail); + unsigned open = 0, jump = 0, size = 1, uip; + for (;;) { + CADICAL_assert (reason != INVALID); + klause *c = dereference_klause (cadical_kitten, reason); + CADICAL_assert (c); + ROG (reason, "analyzing"); + PUSH_STACK (cadical_kitten->resolved, reason); + for (all_literals_in_klause (lit, c)) { + const unsigned idx = lit / 2; + if (marks[idx]) + continue; + CADICAL_assert (cadical_kitten->values[lit] < 0); + LOG ("analyzed %u", lit); + marks[idx] = true; + PUSH_STACK (cadical_kitten->analyzed, idx); + const kar *const v = vars + idx; + const unsigned tmp = v->level; + if (tmp < level) { + if (tmp > jump) { + jump = tmp; + if (size > 1) { + const unsigned other = PEEK_STACK (cadical_kitten->klause, 1); + POKE_STACK (cadical_kitten->klause, 1, lit); + lit = other; + } + } + PUSH_STACK (cadical_kitten->klause, lit); + size++; + } else + open++; + } + unsigned idx; + do { + CADICAL_assert (BEGIN_STACK (cadical_kitten->trail) < p); + uip = *--p; + } while (!marks[idx = uip / 2]); + CADICAL_assert (open); + if (!--open) + break; + reason = vars[idx].reason; + } + const unsigned not_uip = uip ^ 1; + LOG ("first UIP %u jump level %u size %u", not_uip, jump, size); + POKE_STACK (cadical_kitten->klause, 0, not_uip); + bump (cadical_kitten); + CLEAR_STACK (cadical_kitten->analyzed); + const unsigned learned_ref = cadical_new_learned_klause (cadical_kitten); + CLEAR_STACK (cadical_kitten->resolved); + CLEAR_STACK (cadical_kitten->klause); + backtrack (cadical_kitten, jump); + assign (cadical_kitten, not_uip, learned_ref); +} + +static void failing (cadical_kitten *cadical_kitten) { + CADICAL_assert (cadical_kitten->inconsistent == INVALID); + CADICAL_assert (!EMPTY_STACK (cadical_kitten->assumptions)); + CADICAL_assert (EMPTY_STACK (cadical_kitten->analyzed)); + CADICAL_assert (EMPTY_STACK (cadical_kitten->resolved)); + CADICAL_assert (EMPTY_STACK (cadical_kitten->klause)); + LOG ("analyzing failing assumptions"); + const value *const values = cadical_kitten->values; + const kar *const vars = cadical_kitten->vars; + unsigned failed_clashing = INVALID; + unsigned first_failed = INVALID; + unsigned failed_unit = INVALID; + for (all_stack (unsigned, lit, cadical_kitten->assumptions)) { + if (values[lit] >= 0) + continue; + if (first_failed == INVALID) + first_failed = lit; + const unsigned failed_idx = lit / 2; + const kar *const failed_var = vars + failed_idx; + if (!failed_var->level) { + failed_unit = lit; + break; + } + if (failed_clashing == INVALID && failed_var->reason == INVALID) + failed_clashing = lit; + } + unsigned failed; + if (failed_unit != INVALID) + failed = failed_unit; + else if (failed_clashing != INVALID) + failed = failed_clashing; + else + failed = first_failed; + CADICAL_assert (failed != INVALID); + const unsigned failed_idx = failed / 2; + const kar *const failed_var = vars + failed_idx; + const unsigned failed_reason = failed_var->reason; + LOG ("first failed assumption %u", failed); + cadical_kitten->failed[failed] = true; + + if (failed_unit != INVALID) { + CADICAL_assert (dereference_klause (cadical_kitten, failed_reason)->size == 1); + LOG ("root-level falsified assumption %u", failed); + cadical_kitten->failing = failed_reason; + ROG (cadical_kitten->failing, "failing reason"); + return; + } + + const unsigned not_failed = failed ^ 1; + if (failed_clashing != INVALID) { + LOG ("clashing with negated assumption %u", not_failed); + cadical_kitten->failed[not_failed] = true; + CADICAL_assert (cadical_kitten->failing == INVALID); + return; + } + + value *marks = cadical_kitten->marks; + CADICAL_assert (!marks[failed_idx]); + marks[failed_idx] = true; + PUSH_STACK (cadical_kitten->analyzed, failed_idx); + PUSH_STACK (cadical_kitten->klause, not_failed); + + unsigneds work; + INIT_STACK (work); + + LOGLITS (BEGIN_STACK (cadical_kitten->trail), SIZE_STACK (cadical_kitten->trail), + "trail"); + + CADICAL_assert (SIZE_STACK (cadical_kitten->trail)); + unsigned const *p = END_STACK (cadical_kitten->trail); + unsigned open = 1; + for (;;) { + if (!open) + break; + open--; + unsigned idx, uip; + do { + CADICAL_assert (BEGIN_STACK (cadical_kitten->trail) < p); + uip = *--p; + } while (!marks[idx = uip / 2]); + + const kar *var = vars + idx; + const unsigned reason = var->reason; + if (reason == INVALID) { + unsigned lit = 2 * idx; + if (values[lit] < 0) + lit ^= 1; + LOG ("failed assumption %u", lit); + CADICAL_assert (!cadical_kitten->failed[lit]); + cadical_kitten->failed[lit] = true; + const unsigned not_lit = lit ^ 1; + PUSH_STACK (cadical_kitten->klause, not_lit); + } else { + ROG (reason, "analyzing"); + PUSH_STACK (cadical_kitten->resolved, reason); + klause *c = dereference_klause (cadical_kitten, reason); + for (all_literals_in_klause (other, c)) { + const unsigned other_idx = other / 2; + if (marks[other_idx]) + continue; + CADICAL_assert (other_idx != idx); + marks[other_idx] = true; + CADICAL_assert (values[other]); + if (vars[other_idx].level) + open++; + else + PUSH_STACK (work, other_idx); + PUSH_STACK (cadical_kitten->analyzed, other_idx); + + LOG ("analyzing final literal %u", other ^ 1); + } + } + } + for (size_t next = 0; next < SIZE_STACK (work); next++) { + const unsigned idx = PEEK_STACK (work, next); + const kar *var = vars + idx; + const unsigned reason = var->reason; + if (reason == INVALID) { + unsigned lit = 2 * idx; + if (values[lit] < 0) + lit ^= 1; + LOG ("failed assumption %u", lit); + CADICAL_assert (!cadical_kitten->failed[lit]); + cadical_kitten->failed[lit] = true; + const unsigned not_lit = lit ^ 1; + PUSH_STACK (cadical_kitten->klause, not_lit); + } else { + ROG (reason, "analyzing unit"); + PUSH_STACK (cadical_kitten->resolved, reason); + } + } + + // this is bfs not dfs so it does not work for lrat :/ + /* + for (size_t next = 0; next < SIZE_STACK (cadical_kitten->analyzed); next++) { + const unsigned idx = PEEK_STACK (cadical_kitten->analyzed, next); + CADICAL_assert (marks[idx]); + const kar *var = vars + idx; + const unsigned reason = var->reason; + if (reason == INVALID) { + unsigned lit = 2 * idx; + if (values[lit] < 0) + lit ^= 1; + LOG ("failed assumption %u", lit); + CADICAL_assert (!cadical_kitten->failed[lit]); + cadical_kitten->failed[lit] = true; + const unsigned not_lit = lit ^ 1; + PUSH_STACK (cadical_kitten->klause, not_lit); + } else { + ROG (reason, "analyzing"); + PUSH_STACK (cadical_kitten->resolved, reason); + klause *c = dereference_klause (cadical_kitten, reason); + for (all_literals_in_klause (other, c)) { + const unsigned other_idx = other / 2; + if (other_idx == idx) + continue; + if (marks[other_idx]) + continue; + marks[other_idx] = true; + PUSH_STACK (cadical_kitten->analyzed, other_idx); + LOG ("analyzing final literal %u", other ^ 1); + } + } + } + */ + + for (all_stack (unsigned, idx, cadical_kitten->analyzed)) + CADICAL_assert (marks[idx]), marks[idx] = 0; + CLEAR_STACK (cadical_kitten->analyzed); + + RELEASE_STACK (work); + + const size_t resolved = SIZE_STACK (cadical_kitten->resolved); + CADICAL_assert (resolved); + + if (resolved == 1) { + cadical_kitten->failing = PEEK_STACK (cadical_kitten->resolved, 0); + ROG (cadical_kitten->failing, "reusing as core"); + } else { + cadical_kitten->failing = cadical_new_learned_klause (cadical_kitten); + ROG (cadical_kitten->failing, "new core"); + } + + CLEAR_STACK (cadical_kitten->resolved); + CLEAR_STACK (cadical_kitten->klause); +} + +static void flush_trail (cadical_kitten *cadical_kitten) { + unsigneds *trail = &cadical_kitten->trail; + LOG ("flushing %zu root-level literals from trail", SIZE_STACK (*trail)); + CADICAL_assert (!cadical_kitten->level); + cadical_kitten->propagated = 0; + CLEAR_STACK (*trail); +} + +static int decide (cadical_kitten *cadical_kitten) { + if (!cadical_kitten->level && !EMPTY_STACK (cadical_kitten->trail)) + flush_trail (cadical_kitten); + + const value *const values = cadical_kitten->values; + unsigned decision = INVALID; + const size_t assumptions = SIZE_STACK (cadical_kitten->assumptions); + while (cadical_kitten->level < assumptions) { + unsigned assumption = PEEK_STACK (cadical_kitten->assumptions, cadical_kitten->level); + value value = values[assumption]; + if (value < 0) { + LOG ("found failing assumption %u", assumption); + failing (cadical_kitten); + return 20; + } else if (value > 0) { + + cadical_kitten->level++; + LOG ("pseudo decision level %u for already satisfied assumption %u", + cadical_kitten->level, assumption); + } else { + decision = assumption; + LOG ("using assumption %u as decision", decision); + break; + } + } + + if (!cadical_kitten->unassigned) + return 10; + + if (KITTEN_TICKS >= cadical_kitten->limits.ticks) { + LOG ("ticks limit %" PRIu64 " hit after %" PRIu64 " ticks", + cadical_kitten->limits.ticks, KITTEN_TICKS); + return -1; + } + + if (cadical_kitten->terminator && cadical_kitten->terminator (cadical_kitten->terminator_data)) { + LOG ("terminator requested termination"); + return -1; + } + + if (decision == INVALID) { + unsigned idx = cadical_kitten->queue.search; + const kink *const links = cadical_kitten->links; + for (;;) { + CADICAL_assert (idx != INVALID); + if (!values[2 * idx]) + break; + idx = links[idx].prev; + } + update_search (cadical_kitten, idx); + const unsigned phase = cadical_kitten->phases[idx]; + decision = 2 * idx + phase; + LOG ("decision %u variable %u phase %u", decision, idx, phase); + } + INC (cadical_kitten_decisions); + cadical_kitten->level++; + assign (cadical_kitten, decision, INVALID); + return 0; +} + +static void inconsistent (cadical_kitten *cadical_kitten, unsigned ref) { + CADICAL_assert (ref != INVALID); + CADICAL_assert (cadical_kitten->inconsistent == INVALID); + + if (!cadical_kitten->antecedents) { + cadical_kitten->inconsistent = ref; + ROG (ref, "registering inconsistent virtually empty"); + return; + } + + unsigneds *analyzed = &cadical_kitten->analyzed; + unsigneds *resolved = &cadical_kitten->resolved; + + CADICAL_assert (EMPTY_STACK (*analyzed)); + CADICAL_assert (EMPTY_STACK (*resolved)); + + value *marks = cadical_kitten->marks; + const kar *const vars = cadical_kitten->vars; + unsigned next = 0; + + for (;;) { + CADICAL_assert (ref != INVALID); + klause *c = dereference_klause (cadical_kitten, ref); + CADICAL_assert (c); + ROG (ref, "analyzing inconsistent"); + PUSH_STACK (*resolved, ref); + for (all_literals_in_klause (lit, c)) { + const unsigned idx = lit / 2; + CADICAL_assert (!vars[idx].level); + if (marks[idx]) + continue; + CADICAL_assert (cadical_kitten->values[lit] < 0); + LOG ("analyzed %u", lit); + marks[idx] = true; + PUSH_STACK (cadical_kitten->analyzed, idx); + } + if (next == SIZE_STACK (cadical_kitten->analyzed)) + break; + const unsigned idx = PEEK_STACK (cadical_kitten->analyzed, next); + next++; + const kar *const v = vars + idx; + CADICAL_assert (!v->level); + ref = v->reason; + } + CADICAL_assert (EMPTY_STACK (cadical_kitten->klause)); + ref = cadical_new_learned_klause (cadical_kitten); + ROG (ref, "registering final inconsistent empty"); + cadical_kitten->inconsistent = ref; + + for (all_stack (unsigned, idx, *analyzed)) + marks[idx] = 0; + + CLEAR_STACK (*analyzed); + CLEAR_STACK (*resolved); +} + +static int propagate_units (cadical_kitten *cadical_kitten) { + if (cadical_kitten->inconsistent != INVALID) + return 20; + + if (EMPTY_STACK (cadical_kitten->units)) { + LOG ("no root level unit clauses"); + return 0; + } + + LOG ("propagating %zu root level unit clauses", + SIZE_STACK (cadical_kitten->units)); + + const value *const values = cadical_kitten->values; + + for (size_t next = 0; next < SIZE_STACK (cadical_kitten->units); next++) { + const unsigned ref = PEEK_STACK (cadical_kitten->units, next); + CADICAL_assert (ref != INVALID); + klause *c = dereference_klause (cadical_kitten, ref); + CADICAL_assert (c->size == 1); + ROG (ref, "propagating unit"); + const unsigned unit = c->lits[0]; + const value value = values[unit]; + if (value > 0) + continue; + if (value < 0) { + inconsistent (cadical_kitten, ref); + return 20; + } + assign (cadical_kitten, unit, ref); + } + const unsigned conflict = propagate (cadical_kitten); + if (conflict == INVALID) + return 0; + inconsistent (cadical_kitten, conflict); + return 20; +} + +/*------------------------------------------------------------------------*/ + +static klause *begin_klauses (cadical_kitten *cadical_kitten) { + return (klause *) BEGIN_STACK (cadical_kitten->klauses); +} + +static klause *end_original_klauses (cadical_kitten *cadical_kitten) { + return (klause *) (BEGIN_STACK (cadical_kitten->klauses) + + cadical_kitten->end_original_ref); +} + +static klause *end_klauses (cadical_kitten *cadical_kitten) { + return (klause *) END_STACK (cadical_kitten->klauses); +} + +static klause *next_klause (cadical_kitten *cadical_kitten, klause *c) { + CADICAL_assert (begin_klauses (cadical_kitten) <= c); + CADICAL_assert (c < end_klauses (cadical_kitten)); + unsigned *res = c->lits + c->size; + if (cadical_kitten->antecedents && is_learned_klause (c)) + res += c->aux; + return (klause *) res; +} + +/*------------------------------------------------------------------------*/ + +static void reset_core (cadical_kitten *cadical_kitten) { + LOG ("resetting core clauses"); + size_t reset = 0; + for (all_klauses (c)) + if (is_core_klause (c)) + unset_core_klause (c), reset++; + LOG ("reset %zu core clauses", reset); + CLEAR_STACK (cadical_kitten->core); +} + +static void reset_assumptions (cadical_kitten *cadical_kitten) { + LOG ("reset %zu assumptions", SIZE_STACK (cadical_kitten->assumptions)); + while (!EMPTY_STACK (cadical_kitten->assumptions)) { + const unsigned assumption = POP_STACK (cadical_kitten->assumptions); + cadical_kitten->failed[assumption] = false; + } +#ifndef CADICAL_NDEBUG + for (size_t i = 0; i < cadical_kitten->size; i++) + CADICAL_assert (!cadical_kitten->failed[i]); +#endif + CLEAR_STACK (cadical_kitten->assumptions); + if (cadical_kitten->failing != INVALID) { + ROG (cadical_kitten->failing, "reset failed assumption reason"); + cadical_kitten->failing = INVALID; + } +} + +static void reset_incremental (cadical_kitten *cadical_kitten) { + // if (cadical_kitten->level) + cadical_completely_backtrack_to_root_level (cadical_kitten); + if (!EMPTY_STACK (cadical_kitten->assumptions)) + reset_assumptions (cadical_kitten); + else + CADICAL_assert (cadical_kitten->failing == INVALID); + if (cadical_kitten->status == 21) + reset_core (cadical_kitten); + UPDATE_STATUS (0); +} + +/*------------------------------------------------------------------------*/ + +static bool flip_literal (cadical_kitten *cadical_kitten, unsigned lit) { + INC (cadical_kitten_flip); + signed char *values = cadical_kitten->values; + if (values[lit] < 0) + lit ^= 1; + LOG ("trying to flip value of satisfied literal %u", lit); + CADICAL_assert (values[lit] > 0); + katches *watches = cadical_kitten->watches + lit; + unsigned *q = BEGIN_STACK (*watches); + const unsigned *const end_watches = END_STACK (*watches); + unsigned const *p = q; + uint64_t ticks = (((char *) end_watches - (char *) q) >> 7) + 1; + bool res = true; + while (p != end_watches) { + const unsigned ref = *q++ = *p++; + klause *c = dereference_klause (cadical_kitten, ref); + unsigned *lits = c->lits; + const unsigned other = lits[0] ^ lits[1] ^ lit; + const value other_value = values[other]; + ticks++; + if (other_value > 0) + continue; + value replacement_value = -1; + unsigned replacement = INVALID; + const unsigned *const end_lits = lits + c->size; + unsigned *r; + for (r = lits + 2; r != end_lits; r++) { + replacement = *r; + CADICAL_assert (replacement != lit); + replacement_value = values[replacement]; + CADICAL_assert (replacement_value); + if (replacement_value > 0) + break; + } + if (replacement_value > 0) { + CADICAL_assert (replacement != INVALID); + ROG (ref, "unwatching %u in", lit); + lits[0] = other; + lits[1] = replacement; + *r = lit; + watch_klause (cadical_kitten, replacement, ref); + q--; + } else { + CADICAL_assert (replacement_value < 0); + ROG (ref, "single satisfied"); + res = false; + break; + } + } + while (p != end_watches) + *q++ = *p++; + SET_END_OF_STACK (*watches, q); + ADD (cadical_kitten_ticks, ticks); + if (res) { + LOG ("flipping value of %u", lit); + values[lit] = -1; + const unsigned not_lit = lit ^ 1; + values[not_lit] = 1; + INC (cadical_kitten_flipped); + } else + LOG ("failed to flip value of %u", lit); + return res; +} + +/*------------------------------------------------------------------------*/ + +// this cadical specific clause addition avoids copying clauses multiple +// times just to convert literals to unsigned representation. +// +static unsigned int2u (int lit) { + CADICAL_assert (lit != 0); + int idx = abs (lit) - 1; + return (lit < 0) + 2u * (unsigned) idx; +} + +void cadical_kitten_assume (cadical_kitten *cadical_kitten, unsigned elit) { + REQUIRE_INITIALIZED (); + if (cadical_kitten->status) + reset_incremental (cadical_kitten); + const unsigned ilit = import_literal (cadical_kitten, elit); + LOG ("registering assumption %u", ilit); + PUSH_STACK (cadical_kitten->assumptions, ilit); +} + +void cadical_kitten_assume_signed (cadical_kitten *cadical_kitten, int elit) { + unsigned kelit = int2u (elit); + cadical_kitten_assume (cadical_kitten, kelit); +} + +void cadical_kitten_clause_with_id_and_exception (cadical_kitten *cadical_kitten, unsigned id, + size_t size, + const unsigned *elits, + unsigned except) { + REQUIRE_INITIALIZED (); + if (cadical_kitten->status) + reset_incremental (cadical_kitten); + CADICAL_assert (EMPTY_STACK (cadical_kitten->klause)); + const unsigned *const end = elits + size; + for (const unsigned *p = elits; p != end; p++) { + const unsigned elit = *p; + if (elit == except) + continue; + const unsigned ilit = import_literal (cadical_kitten, elit); + CADICAL_assert (ilit < cadical_kitten->lits); + const unsigned iidx = ilit / 2; + if (cadical_kitten->marks[iidx]) + INVALID_API_USAGE ("variable '%u' of literal '%u' occurs twice", + elit / 2, elit); + cadical_kitten->marks[iidx] = true; + PUSH_STACK (cadical_kitten->klause, ilit); + } + for (unsigned *p = cadical_kitten->klause.begin; p != cadical_kitten->klause.end; p++) + cadical_kitten->marks[*p / 2] = false; + new_original_klause (cadical_kitten, id); + CLEAR_STACK (cadical_kitten->klause); +} + +void citten_clause_with_id_and_exception (cadical_kitten *cadical_kitten, unsigned id, + size_t size, const int *elits, + unsigned except) { + REQUIRE_INITIALIZED (); + if (cadical_kitten->status) + reset_incremental (cadical_kitten); + CADICAL_assert (EMPTY_STACK (cadical_kitten->klause)); + const int *const end = elits + size; + for (const int *p = elits; p != end; p++) { + const unsigned elit = int2u (*p); // this is the conversion + if (elit == except) + continue; + const unsigned ilit = import_literal (cadical_kitten, elit); + CADICAL_assert (ilit < cadical_kitten->lits); + const unsigned iidx = ilit / 2; + if (cadical_kitten->marks[iidx]) + INVALID_API_USAGE ("variable '%u' of literal '%u' occurs twice", + elit / 2, elit); + cadical_kitten->marks[iidx] = true; + PUSH_STACK (cadical_kitten->klause, ilit); + } + for (unsigned *p = cadical_kitten->klause.begin; p != cadical_kitten->klause.end; p++) + cadical_kitten->marks[*p / 2] = false; + new_original_klause (cadical_kitten, id); + CLEAR_STACK (cadical_kitten->klause); +} + +void citten_clause_with_id_and_equivalence (cadical_kitten *cadical_kitten, unsigned id, + size_t size, const int *elits, + unsigned lit, unsigned other) { + REQUIRE_INITIALIZED (); + if (cadical_kitten->status) + reset_incremental (cadical_kitten); + CADICAL_assert (EMPTY_STACK (cadical_kitten->klause)); + bool sat = false; + const int *const end = elits + size; + for (const int *p = elits; p != end; p++) { + const unsigned elit = int2u (*p); // this is the conversion + if (elit == (lit ^ 1u) || elit == (other ^ 1u)) + continue; + if (elit == lit || elit == other) { + sat = true; + break; + } + const unsigned ilit = import_literal (cadical_kitten, elit); + CADICAL_assert (ilit < cadical_kitten->lits); + const unsigned iidx = ilit / 2; + if (cadical_kitten->marks[iidx]) + INVALID_API_USAGE ("variable '%u' of literal '%u' occurs twice", + elit / 2, elit); + cadical_kitten->marks[iidx] = true; + PUSH_STACK (cadical_kitten->klause, ilit); + } + for (unsigned *p = cadical_kitten->klause.begin; p != cadical_kitten->klause.end; p++) + cadical_kitten->marks[*p / 2] = false; + if (!sat) + new_original_klause (cadical_kitten, id); + CLEAR_STACK (cadical_kitten->klause); +} + +void cadical_kitten_clause (cadical_kitten *cadical_kitten, size_t size, unsigned *elits) { + cadical_kitten_clause_with_id_and_exception (cadical_kitten, INVALID, size, elits, + INVALID); +} + +void citten_clause_with_id (cadical_kitten *cadical_kitten, unsigned id, size_t size, + int *elits) { + citten_clause_with_id_and_exception (cadical_kitten, id, size, elits, INVALID); +} + +void cadical_kitten_unit (cadical_kitten *cadical_kitten, unsigned lit) { + cadical_kitten_clause (cadical_kitten, 1, &lit); +} + +void cadical_kitten_binary (cadical_kitten *cadical_kitten, unsigned a, unsigned b) { + unsigned clause[2] = {a, b}; + cadical_kitten_clause (cadical_kitten, 2, clause); +} + +int cadical_kitten_solve (cadical_kitten *cadical_kitten) { + REQUIRE_INITIALIZED (); + if (cadical_kitten->status) + reset_incremental (cadical_kitten); + else // if (cadical_kitten->level) + cadical_completely_backtrack_to_root_level (cadical_kitten); + + LOG ("starting solving under %zu assumptions", + SIZE_STACK (cadical_kitten->assumptions)); + + INC (cadical_kitten_solved); + + int res = propagate_units (cadical_kitten); + while (!res) { + const unsigned conflict = propagate (cadical_kitten); + if (cadical_kitten->terminator && + cadical_kitten->terminator (cadical_kitten->terminator_data)) { + LOG ("terminator requested termination"); + res = -1; + break; + } + if (conflict != INVALID) { + if (cadical_kitten->level) + analyze (cadical_kitten, conflict); + else { + inconsistent (cadical_kitten, conflict); + res = 20; + } + } else + res = decide (cadical_kitten); + } + + if (res < 0) + res = 0; + + if (!res && !EMPTY_STACK (cadical_kitten->assumptions)) + reset_assumptions (cadical_kitten); + + UPDATE_STATUS (res); + + if (res == 10) + INC (cadical_kitten_sat); + else if (res == 20) + INC (cadical_kitten_unsat); + else + INC (cadical_kitten_unknown); + + LOG ("finished solving with result %d", res); + + return res; +} + +int cadical_kitten_status (cadical_kitten *cadical_kitten) { return cadical_kitten->status; } + +unsigned cadical_kitten_compute_clausal_core (cadical_kitten *cadical_kitten, + uint64_t *learned_ptr) { + REQUIRE_STATUS (20); + + if (!cadical_kitten->antecedents) + INVALID_API_USAGE ("antecedents not tracked"); + + LOG ("computing clausal core"); + + unsigneds *resolved = &cadical_kitten->resolved; + CADICAL_assert (EMPTY_STACK (*resolved)); + + unsigned original = 0; + uint64_t learned = 0; + + unsigned reason_ref = cadical_kitten->inconsistent; + + if (reason_ref == INVALID) { + CADICAL_assert (!EMPTY_STACK (cadical_kitten->assumptions)); + reason_ref = cadical_kitten->failing; + if (reason_ref == INVALID) { + LOG ("assumptions mutually inconsistent"); + //goto DONE; + if (learned_ptr) + *learned_ptr = learned; + + LOG ("clausal core of %u original clauses", original); + LOG ("clausal core of %" PRIu64 " learned clauses", learned); + cadical_kitten->statistics.original = original; + cadical_kitten->statistics.learned = 0; + UPDATE_STATUS (21); + + return original; + } + } + + PUSH_STACK (*resolved, reason_ref); + unsigneds *core = &cadical_kitten->core; + CADICAL_assert (EMPTY_STACK (*core)); + + while (!EMPTY_STACK (*resolved)) { + const unsigned c_ref = POP_STACK (*resolved); + if (c_ref == INVALID) { + const unsigned d_ref = POP_STACK (*resolved); + ROG (d_ref, "core[%zu]", SIZE_STACK (*core)); + PUSH_STACK (*core, d_ref); + klause *d = dereference_klause (cadical_kitten, d_ref); + CADICAL_assert (!is_core_klause (d)); + set_core_klause (d); + if (is_learned_klause (d)) + learned++; + else + original++; + } else { + klause *c = dereference_klause (cadical_kitten, c_ref); + if (is_core_klause (c)) + continue; + PUSH_STACK (*resolved, c_ref); + PUSH_STACK (*resolved, INVALID); + ROG (c_ref, "analyzing antecedent core"); + if (!is_learned_klause (c)) + continue; + for (all_antecedents (d_ref, c)) { + klause *d = dereference_klause (cadical_kitten, d_ref); + if (!is_core_klause (d)) + PUSH_STACK (*resolved, d_ref); + } + } + } + + //DONE: + + if (learned_ptr) + *learned_ptr = learned; + + LOG ("clausal core of %u original clauses", original); + LOG ("clausal core of %" PRIu64 " learned clauses", learned); + cadical_kitten->statistics.original = original; + cadical_kitten->statistics.learned = 0; + UPDATE_STATUS (21); + + return original; +} + +void cadical_kitten_traverse_core_ids (cadical_kitten *cadical_kitten, void *state, + void (*traverse) (void *, unsigned)) { + REQUIRE_STATUS (21); + + LOG ("traversing core of original clauses"); + + unsigned traversed = 0; + + for (all_original_klauses (c)) { + // only happens for 'true' incremental calls, i.e. if add happens after + // solve + if (is_learned_klause (c)) + continue; + if (!is_core_klause (c)) + continue; + ROG (reference_klause (cadical_kitten, c), "traversing"); + traverse (state, c->aux); + traversed++; + } + + LOG ("traversed %u original core clauses", traversed); + (void) traversed; + + CADICAL_assert (cadical_kitten->status == 21); +} + +void cadical_kitten_traverse_core_clauses (cadical_kitten *cadical_kitten, void *state, + void (*traverse) (void *, bool, size_t, + const unsigned *)) { + REQUIRE_STATUS (21); + + LOG ("traversing clausal core"); + + unsigned traversed = 0; + + for (all_stack (unsigned, c_ref, cadical_kitten->core)) { + klause *c = dereference_klause (cadical_kitten, c_ref); + CADICAL_assert (is_core_klause (c)); + const bool learned = is_learned_klause (c); + unsigneds *eclause = &cadical_kitten->eclause; + CADICAL_assert (EMPTY_STACK (*eclause)); + for (all_literals_in_klause (ilit, c)) { + const unsigned elit = export_literal (cadical_kitten, ilit); + PUSH_STACK (*eclause, elit); + } + const size_t size = SIZE_STACK (*eclause); + const unsigned *elits = eclause->begin; + ROG (reference_klause (cadical_kitten, c), "traversing"); + traverse (state, learned, size, elits); + CLEAR_STACK (*eclause); + traversed++; + } + + LOG ("traversed %u core clauses", traversed); + (void) traversed; + + CADICAL_assert (cadical_kitten->status == 21); +} + +void cadical_kitten_traverse_core_clauses_with_id ( + cadical_kitten *cadical_kitten, void *state, + void (*traverse) (void *state, unsigned, bool learned, size_t, + const unsigned *)) { + REQUIRE_STATUS (21); + + LOG ("traversing clausal core"); + + unsigned traversed = 0; + + for (all_stack (unsigned, c_ref, cadical_kitten->core)) { + klause *c = dereference_klause (cadical_kitten, c_ref); + CADICAL_assert (is_core_klause (c)); + const bool learned = is_learned_klause (c); + unsigneds *eclause = &cadical_kitten->eclause; + CADICAL_assert (EMPTY_STACK (*eclause)); + for (all_literals_in_klause (ilit, c)) { + const unsigned elit = export_literal (cadical_kitten, ilit); + PUSH_STACK (*eclause, elit); + } + const size_t size = SIZE_STACK (*eclause); + const unsigned *elits = eclause->begin; + ROG (reference_klause (cadical_kitten, c), "traversing"); + unsigned ctag = learned ? 0 : c->aux; + traverse (state, ctag, learned, size, elits); + CLEAR_STACK (*eclause); + traversed++; + } + + LOG ("traversed %u core clauses", traversed); + (void) traversed; + + CADICAL_assert (cadical_kitten->status == 21); +} + +void cadical_kitten_trace_core (cadical_kitten *cadical_kitten, void *state, + void (*trace) (void *, unsigned, unsigned, bool, + size_t, const unsigned *, size_t, + const unsigned *)) { + REQUIRE_STATUS (21); + + LOG ("tracing clausal core"); + + unsigned traced = 0; + + for (all_stack (unsigned, c_ref, cadical_kitten->core)) { + klause *c = dereference_klause (cadical_kitten, c_ref); + CADICAL_assert (is_core_klause (c)); + const bool learned = is_learned_klause (c); + unsigneds *eclause = &cadical_kitten->eclause; + CADICAL_assert (EMPTY_STACK (*eclause)); + for (all_literals_in_klause (ilit, c)) { + const unsigned elit = export_literal (cadical_kitten, ilit); + PUSH_STACK (*eclause, elit); + } + const size_t size = SIZE_STACK (*eclause); + const unsigned *elits = eclause->begin; + + unsigneds *resolved = &cadical_kitten->resolved; + CADICAL_assert (EMPTY_STACK (*resolved)); + if (learned) { + for (all_antecedents (ref, c)) { + PUSH_STACK (*resolved, ref); + } + } + const size_t rsize = SIZE_STACK (*resolved); + const unsigned *rids = resolved->begin; + + unsigned cid = reference_klause (cadical_kitten, c); + unsigned ctag = learned ? 0 : c->aux; + ROG (cid, "tracing"); + trace (state, cid, ctag, learned, size, elits, rsize, rids); + CLEAR_STACK (*eclause); + CLEAR_STACK (*resolved); + traced++; + } + + LOG ("traced %u core clauses", traced); + (void) traced; + + CADICAL_assert (cadical_kitten->status == 21); +} + +void cadical_kitten_shrink_to_clausal_core (cadical_kitten *cadical_kitten) { + REQUIRE_STATUS (21); + + LOG ("shrinking formula to core of original clauses"); + + CLEAR_STACK (cadical_kitten->trail); + + cadical_kitten->unassigned = cadical_kitten->lits / 2; + cadical_kitten->propagated = 0; + cadical_kitten->level = 0; + + update_search (cadical_kitten, cadical_kitten->queue.last); + + memset (cadical_kitten->values, 0, cadical_kitten->lits); + + for (all_kits (lit)) + CLEAR_STACK (KATCHES (lit)); + + CADICAL_assert (cadical_kitten->inconsistent != INVALID); + klause *inconsistent = dereference_klause (cadical_kitten, cadical_kitten->inconsistent); + if (is_learned_klause (inconsistent) || inconsistent->size) { + ROG (cadical_kitten->inconsistent, "resetting inconsistent"); + cadical_kitten->inconsistent = INVALID; + } else + ROG (cadical_kitten->inconsistent, "keeping inconsistent"); + + CLEAR_STACK (cadical_kitten->units); + + klause *begin = begin_klauses (cadical_kitten), *q = begin; + klause const *const end = end_original_klauses (cadical_kitten); +#ifdef LOGGING + unsigned original = 0; +#endif + for (klause *c = begin, *next; c != end; c = next) { + next = next_klause (cadical_kitten, c); + // CADICAL_assert (!is_learned_klause (c)); not necessarily true + if (is_learned_klause (c)) + continue; + if (!is_core_klause (c)) + continue; + unset_core_klause (c); + const unsigned dst = (unsigned *) q - (unsigned *) begin; + const unsigned size = c->size; + if (!size) { + if (cadical_kitten->inconsistent != INVALID) + cadical_kitten->inconsistent = dst; + } else if (size == 1) { + PUSH_STACK (cadical_kitten->units, dst); + ROG (dst, "keeping"); + } else { + watch_klause (cadical_kitten, c->lits[0], dst); + watch_klause (cadical_kitten, c->lits[1], dst); + } + if (c == q) + q = next; + else { + const size_t bytes = (char *) next - (char *) c; + memmove (q, c, bytes); + q = (klause *) ((char *) q + bytes); + } +#ifdef LOGGING + original++; +#endif + } + SET_END_OF_STACK (cadical_kitten->klauses, (unsigned *) q); + cadical_kitten->end_original_ref = SIZE_STACK (cadical_kitten->klauses); + LOG ("end of original clauses at %zu", cadical_kitten->end_original_ref); + LOG ("%u original clauses left", original); + + CLEAR_STACK (cadical_kitten->core); + + UPDATE_STATUS (0); +} + +signed char cadical_kitten_signed_value (cadical_kitten *cadical_kitten, int selit) { + REQUIRE_STATUS (10); + const unsigned elit = int2u (selit); + const unsigned eidx = elit / 2; + if (eidx >= cadical_kitten->evars) + return 0; + unsigned iidx = cadical_kitten->import[eidx]; + if (!iidx) + return 0; + const unsigned ilit = 2 * (iidx - 1) + (elit & 1); + return cadical_kitten->values[ilit]; +} + +signed char cadical_kitten_value (cadical_kitten *cadical_kitten, unsigned elit) { + REQUIRE_STATUS (10); + const unsigned eidx = elit / 2; + if (eidx >= cadical_kitten->evars) + return 0; + unsigned iidx = cadical_kitten->import[eidx]; + if (!iidx) + return 0; + const unsigned ilit = 2 * (iidx - 1) + (elit & 1); + return cadical_kitten->values[ilit]; +} + +signed char cadical_kitten_fixed (cadical_kitten *cadical_kitten, unsigned elit) { + const unsigned eidx = elit / 2; + if (eidx >= cadical_kitten->evars) + return 0; + unsigned iidx = cadical_kitten->import[eidx]; + if (!iidx) + return 0; + iidx--; + const unsigned ilit = 2 * iidx + (elit & 1); + signed char res = cadical_kitten->values[ilit]; + if (!res) + return 0; + kar *v = cadical_kitten->vars + iidx; + if (v->level) + return 0; + return res; +} + +signed char cadical_kitten_fixed_signed (cadical_kitten *cadical_kitten, int elit) { + unsigned kelit = int2u (elit); + return cadical_kitten_fixed (cadical_kitten, kelit); +} + +bool cadical_kitten_flip_literal (cadical_kitten *cadical_kitten, unsigned elit) { + REQUIRE_STATUS (10); + const unsigned eidx = elit / 2; + if (eidx >= cadical_kitten->evars) + return false; + unsigned iidx = cadical_kitten->import[eidx]; + if (!iidx) + return false; + const unsigned ilit = 2 * (iidx - 1) + (elit & 1); + if (cadical_kitten_fixed (cadical_kitten, elit)) + return false; + return flip_literal (cadical_kitten, ilit); +} + +bool cadical_kitten_flip_signed_literal (cadical_kitten *cadical_kitten, int elit) { + REQUIRE_STATUS (10); + unsigned kelit = int2u (elit); + return cadical_kitten_flip_literal (cadical_kitten, kelit); +} + +bool cadical_kitten_failed (cadical_kitten *cadical_kitten, unsigned elit) { + REQUIRE_STATUS (20); + const unsigned eidx = elit / 2; + if (eidx >= cadical_kitten->evars) + return false; + unsigned iidx = cadical_kitten->import[eidx]; + if (!iidx) + return false; + const unsigned ilit = 2 * (iidx - 1) + (elit & 1); + return cadical_kitten->failed[ilit]; +} + +// checks both watches for clauses with only one literal positively +// assigned. if such a clause is found, return false. Otherwise fix watch +// invariant and return true +static bool prime_propagate (cadical_kitten *cadical_kitten, const unsigned idx, + void *state, const bool ignoring, + bool (*ignore) (void *, unsigned)) { + unsigned lit = 2 * idx; + unsigned conflict = INVALID; + value *values = cadical_kitten->values; + for (int i = 0; i < 2; i++) { + if (conflict != INVALID) + break; + lit = lit ^ i; + const unsigned not_lit = lit ^ 1; + katches *watches = cadical_kitten->watches + not_lit; + unsigned *q = BEGIN_STACK (*watches); + const unsigned *const end_watches = END_STACK (*watches); + unsigned const *p = q; + uint64_t ticks = (((char *) end_watches - (char *) q) >> 7) + 1; + while (p != end_watches) { + const unsigned ref = *q++ = *p++; + klause *c = dereference_klause (cadical_kitten, ref); + if (is_learned_klause (c) || ignore (state, c->aux) == ignoring) + continue; + CADICAL_assert (c->size > 1); + unsigned *lits = c->lits; + const unsigned other = lits[0] ^ lits[1] ^ not_lit; + const value other_value = values[other]; + ticks++; + if (other_value > 0) + continue; + value replacement_value = -1; + unsigned replacement = INVALID; + const unsigned *const end_lits = lits + c->size; + unsigned *r; + for (r = lits + 2; r != end_lits; r++) { + replacement = *r; + replacement_value = values[replacement]; + if (replacement_value > 0) + break; + } + if (replacement_value > 0) { + CADICAL_assert (replacement != INVALID); + ROG (ref, "unwatching %u in", not_lit); + lits[0] = other; + lits[1] = replacement; + *r = not_lit; + watch_klause (cadical_kitten, replacement, ref); + q--; + } else { + ROG (ref, "idx %u forced prime by", idx); + conflict = ref; + break; + } + } + while (p != end_watches) + *q++ = *p++; + SET_END_OF_STACK (*watches, q); + ADD (cadical_kitten_ticks, ticks); + } + return conflict == INVALID; +} + +void cadical_kitten_add_prime_implicant (cadical_kitten *cadical_kitten, void *state, int side, + void (*add_implicant) (void *, int, size_t, + const unsigned *)) { + REQUIRE_STATUS (11); + // might be possible in some edge cases + unsigneds *prime = &cadical_kitten->prime[side]; + unsigneds *prime2 = &cadical_kitten->prime[!side]; + CADICAL_assert (!EMPTY_STACK (*prime) || !EMPTY_STACK (*prime2)); + CLEAR_STACK (*prime2); + + for (all_stack (unsigned, lit, *prime)) { + const unsigned not_lit = lit ^ 1; + const unsigned elit = export_literal (cadical_kitten, not_lit); + PUSH_STACK (*prime2, elit); + } + + // adds a clause which will reset cadical_kitten status and backtrack + add_implicant (state, side, SIZE_STACK (*prime2), BEGIN_STACK (*prime2)); + CLEAR_STACK (*prime); + CLEAR_STACK (*prime2); +} + +// computes two prime implicants, only considering clauses based on ignore +// return -1 if no prime implicant has been computed, otherwise returns +// index of shorter implicant. +// TODO does not work if flip has been called beforehand +int cadical_kitten_compute_prime_implicant (cadical_kitten *cadical_kitten, void *state, + bool (*ignore) (void *, unsigned)) { + REQUIRE_STATUS (10); + + value *values = cadical_kitten->values; + kar *vars = cadical_kitten->vars; + unsigneds unassigned; + INIT_STACK (unassigned); + bool limit_hit = 0; + CADICAL_assert (EMPTY_STACK (cadical_kitten->prime[0]) && EMPTY_STACK (cadical_kitten->prime[1])); + for (int i = 0; i < 2; i++) { + const bool ignoring = i; + for (all_stack (unsigned, lit, cadical_kitten->trail)) { + if (KITTEN_TICKS >= cadical_kitten->limits.ticks) { + LOG ("ticks limit %" PRIu64 " hit after %" PRIu64 " ticks", + cadical_kitten->limits.ticks, KITTEN_TICKS); + limit_hit = 1; + break; + } + CADICAL_assert (values[lit] > 0); + const unsigned idx = lit / 2; + const unsigned ref = vars[idx].reason; + CADICAL_assert (vars[idx].level); + klause *c = 0; + if (ref != INVALID) + c = dereference_klause (cadical_kitten, ref); + if (ref == INVALID || is_learned_klause (c) || + ignore (state, c->aux) == ignoring) { + LOG ("non-prime candidate var %d", idx); + if (prime_propagate (cadical_kitten, idx, state, ignoring, ignore)) { + values[lit] = 0; + values[lit ^ 1] = 0; + PUSH_STACK (unassigned, lit); + } else + CADICAL_assert (values[lit] > 0); + } + } + unsigneds *prime = &cadical_kitten->prime[i]; + // push on prime implicant stack. + for (all_kits (lit)) { + if (values[lit] > 0) + PUSH_STACK (*prime, lit); + } + // reassign all literals on + for (all_stack (unsigned, lit, unassigned)) { + CADICAL_assert (!values[lit]); + values[lit] = 1; + values[lit ^ 1] = -1; + } + CLEAR_STACK (unassigned); + } + RELEASE_STACK (unassigned); + + if (limit_hit) { + CLEAR_STACK (cadical_kitten->prime[0]); + CLEAR_STACK (cadical_kitten->prime[1]); + return -1; + } + // the only case when one of the prime implicants is allowed to be empty + // is if ignore returns always true or always false. + CADICAL_assert (!EMPTY_STACK (cadical_kitten->prime[0]) || + !EMPTY_STACK (cadical_kitten->prime[1])); + UPDATE_STATUS (11); + + int res = SIZE_STACK (cadical_kitten->prime[0]) > SIZE_STACK (cadical_kitten->prime[1]); + return res; +} + +static bool contains_blit (cadical_kitten *cadical_kitten, klause *c, const unsigned blit) { + for (all_literals_in_klause (lit, c)) { + if (lit == blit) + return true; + } + return false; +} + +static bool prime_propagate_blit (cadical_kitten *cadical_kitten, const unsigned idx, + const unsigned blit) { + unsigned lit = 2 * idx; + unsigned conflict = INVALID; + value *values = cadical_kitten->values; + LOG ("prime propagating idx %u for blit %u", idx, blit); + for (int i = 0; i < 2; i++) { + if (conflict != INVALID) + break; + lit = lit ^ i; + if (lit == blit) + continue; + const unsigned not_lit = lit ^ 1; + katches *watches = cadical_kitten->watches + not_lit; + unsigned *q = BEGIN_STACK (*watches); + const unsigned *const end_watches = END_STACK (*watches); + unsigned const *p = q; + uint64_t ticks = (((char *) end_watches - (char *) q) >> 7) + 1; + while (p != end_watches) { + const unsigned ref = *q++ = *p++; + klause *c = dereference_klause (cadical_kitten, ref); + if (is_learned_klause (c)) + continue; + ROG (ref, "checking with blit %u", blit); + CADICAL_assert (c->size > 1); + unsigned *lits = c->lits; + const unsigned other = lits[0] ^ lits[1] ^ not_lit; + const value other_value = values[other]; + ticks++; + bool use = other == blit || not_lit == blit; + if (other_value > 0) + continue; + value replacement_value = -1; + unsigned replacement = INVALID; + const unsigned *const end_lits = lits + c->size; + unsigned *r; + for (r = lits + 2; r != end_lits; r++) { + replacement = *r; + replacement_value = values[replacement]; + use = use || replacement == blit; + if (replacement_value > 0) + break; + } + if (replacement_value > 0) { + CADICAL_assert (replacement != INVALID); + ROG (ref, "unwatching %u in", not_lit); + lits[0] = other; + lits[1] = replacement; + *r = not_lit; + watch_klause (cadical_kitten, replacement, ref); + q--; + } else if (!use) { + continue; + } else { + ROG (ref, "idx %u forced prime by", idx); + conflict = ref; + break; + } + } + while (p != end_watches) + *q++ = *p++; + SET_END_OF_STACK (*watches, q); + ADD (cadical_kitten_ticks, ticks); + } + return conflict == INVALID; +} + +static int compute_prime_implicant_for (cadical_kitten *cadical_kitten, unsigned blit) { + value *values = cadical_kitten->values; + kar *vars = cadical_kitten->vars; + unsigneds unassigned; + INIT_STACK (unassigned); + bool limit_hit = false; + CADICAL_assert (EMPTY_STACK (cadical_kitten->prime[0]) && EMPTY_STACK (cadical_kitten->prime[1])); + for (int i = 0; i < 2; i++) { + const unsigned block = blit ^ i; + const bool ignoring = i; + if (prime_propagate_blit (cadical_kitten, block / 2, block)) { + value tmp = values[blit]; + CADICAL_assert (tmp); + values[blit] = 0; + values[blit ^ 1] = 0; + PUSH_STACK (unassigned, tmp > 0 ? blit : blit ^ 1); + PUSH_STACK (cadical_kitten->prime[i], block); // will be negated! + } else + CADICAL_assert (false); + for (all_stack (unsigned, lit, cadical_kitten->trail)) { + if (KITTEN_TICKS >= cadical_kitten->limits.ticks) { + LOG ("ticks limit %" PRIu64 " hit after %" PRIu64 " ticks", + cadical_kitten->limits.ticks, KITTEN_TICKS); + limit_hit = true; + break; + } + if (!values[lit]) + continue; + CADICAL_assert (values[lit]); // not true when flipping is involved + const unsigned idx = lit / 2; + const unsigned ref = vars[idx].reason; + CADICAL_assert (vars[idx].level); + LOG ("non-prime candidate var %d", idx); + if (prime_propagate_blit (cadical_kitten, idx, block)) { + value tmp = values[lit]; + CADICAL_assert (tmp); + values[lit] = 0; + values[lit ^ 1] = 0; + PUSH_STACK (unassigned, tmp > 0 ? lit : lit ^ 1); + } + } + unsigneds *prime = &cadical_kitten->prime[i]; + // push on prime implicant stack. + for (all_kits (lit)) { + if (values[lit] > 0) + PUSH_STACK (*prime, lit); + } + // reassign all literals on + for (all_stack (unsigned, lit, unassigned)) { + CADICAL_assert (!values[lit]); + values[lit] = 1; + values[lit ^ 1] = -1; + } + CLEAR_STACK (unassigned); + } + RELEASE_STACK (unassigned); + + if (limit_hit) { + CLEAR_STACK (cadical_kitten->prime[0]); + CLEAR_STACK (cadical_kitten->prime[1]); + return -1; + } + // the only case when one of the prime implicants is allowed to be empty + // is if ignore returns always true or always false. + CADICAL_assert (!EMPTY_STACK (cadical_kitten->prime[0]) || + !EMPTY_STACK (cadical_kitten->prime[1])); + LOGLITS (BEGIN_STACK (cadical_kitten->prime[0]), SIZE_STACK (cadical_kitten->prime[0]), + "first implicant %u", blit); + LOGLITS (BEGIN_STACK (cadical_kitten->prime[1]), SIZE_STACK (cadical_kitten->prime[1]), + "second implicant %u", blit ^ 1); + UPDATE_STATUS (11); + + int res = SIZE_STACK (cadical_kitten->prime[0]) > SIZE_STACK (cadical_kitten->prime[1]); + return res; +} + +int cadical_kitten_flip_and_implicant_for_signed_literal (cadical_kitten *cadical_kitten, + int elit) { + REQUIRE_STATUS (10); + unsigned kelit = int2u (elit); + if (!cadical_kitten_flip_literal (cadical_kitten, kelit)) { + return -2; + } + const unsigned eidx = kelit / 2; + unsigned iidx = cadical_kitten->import[eidx]; + CADICAL_assert (iidx); + const unsigned ilit = 2 * (iidx - 1) + (kelit & 1); + return compute_prime_implicant_for (cadical_kitten, ilit); +} + +ABC_NAMESPACE_IMPL_END diff --git a/src/sat/cadical/cadical_lidruptracer.cpp b/src/sat/cadical/cadical_lidruptracer.cpp new file mode 100644 index 0000000000..db07e36e6f --- /dev/null +++ b/src/sat/cadical/cadical_lidruptracer.cpp @@ -0,0 +1,662 @@ +#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 diff --git a/src/sat/cadical/cadical_limit.cpp b/src/sat/cadical/cadical_limit.cpp new file mode 100644 index 0000000000..69417a4730 --- /dev/null +++ b/src/sat/cadical/cadical_limit.cpp @@ -0,0 +1,135 @@ +#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 diff --git a/src/sat/cadical/cadical_logging.cpp b/src/sat/cadical/cadical_logging.cpp new file mode 100644 index 0000000000..589bf097e0 --- /dev/null +++ b/src/sat/cadical/cadical_logging.cpp @@ -0,0 +1,221 @@ +#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 diff --git a/src/sat/cadical/cadical_lookahead.cpp b/src/sat/cadical/cadical_lookahead.cpp new file mode 100644 index 0000000000..de84858cca --- /dev/null +++ b/src/sat/cadical/cadical_lookahead.cpp @@ -0,0 +1,526 @@ +#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 diff --git a/src/sat/cadical/cadical_lratchecker.cpp b/src/sat/cadical/cadical_lratchecker.cpp new file mode 100644 index 0000000000..5f342a3706 --- /dev/null +++ b/src/sat/cadical/cadical_lratchecker.cpp @@ -0,0 +1,835 @@ +#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 diff --git a/src/sat/cadical/cadical_lrattracer.cpp b/src/sat/cadical/cadical_lrattracer.cpp new file mode 100644 index 0000000000..4da728e18c --- /dev/null +++ b/src/sat/cadical/cadical_lrattracer.cpp @@ -0,0 +1,206 @@ +#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 diff --git a/src/sat/cadical/cadical_lucky.cpp b/src/sat/cadical/cadical_lucky.cpp new file mode 100644 index 0000000000..d67782db4d --- /dev/null +++ b/src/sat/cadical/cadical_lucky.cpp @@ -0,0 +1,440 @@ +#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 diff --git a/src/sat/cadical/cadical_message.cpp b/src/sat/cadical/cadical_message.cpp new file mode 100644 index 0000000000..42d8c0e482 --- /dev/null +++ b/src/sat/cadical/cadical_message.cpp @@ -0,0 +1,218 @@ +#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 diff --git a/src/sat/cadical/cadical_minimize.cpp b/src/sat/cadical/cadical_minimize.cpp new file mode 100644 index 0000000000..97af5fbe6b --- /dev/null +++ b/src/sat/cadical/cadical_minimize.cpp @@ -0,0 +1,230 @@ +#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 diff --git a/src/sat/cadical/cadical_occs.cpp b/src/sat/cadical/cadical_occs.cpp new file mode 100644 index 0000000000..f79d7a6905 --- /dev/null +++ b/src/sat/cadical/cadical_occs.cpp @@ -0,0 +1,58 @@ +#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 diff --git a/src/sat/cadical/cadical_options.cpp b/src/sat/cadical/cadical_options.cpp new file mode 100644 index 0000000000..88eba53ab3 --- /dev/null +++ b/src/sat/cadical/cadical_options.cpp @@ -0,0 +1,365 @@ +#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 diff --git a/src/sat/cadical/cadical_parse.cpp b/src/sat/cadical/cadical_parse.cpp new file mode 100644 index 0000000000..0668aca7d5 --- /dev/null +++ b/src/sat/cadical/cadical_parse.cpp @@ -0,0 +1,442 @@ +#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 diff --git a/src/sat/cadical/cadical_phases.cpp b/src/sat/cadical/cadical_phases.cpp new file mode 100644 index 0000000000..f7e4eaffd8 --- /dev/null +++ b/src/sat/cadical/cadical_phases.cpp @@ -0,0 +1,50 @@ +#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 diff --git a/src/sat/cadical/cadical_probe.cpp b/src/sat/cadical/cadical_probe.cpp new file mode 100644 index 0000000000..19ff0961f7 --- /dev/null +++ b/src/sat/cadical/cadical_probe.cpp @@ -0,0 +1,993 @@ +#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 diff --git a/src/sat/cadical/cadical_profile.cpp b/src/sat/cadical/cadical_profile.cpp new file mode 100644 index 0000000000..a95c97661d --- /dev/null +++ b/src/sat/cadical/cadical_profile.cpp @@ -0,0 +1,113 @@ +#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 diff --git a/src/sat/cadical/cadical_proof.cpp b/src/sat/cadical/cadical_proof.cpp new file mode 100644 index 0000000000..13c9a1a5d5 --- /dev/null +++ b/src/sat/cadical/cadical_proof.cpp @@ -0,0 +1,663 @@ +#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 diff --git a/src/sat/cadical/cadical_propagate.cpp b/src/sat/cadical/cadical_propagate.cpp new file mode 100644 index 0000000000..a4f21ee6a7 --- /dev/null +++ b/src/sat/cadical/cadical_propagate.cpp @@ -0,0 +1,586 @@ +#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 diff --git a/src/sat/cadical/cadical_queue.cpp b/src/sat/cadical/cadical_queue.cpp new file mode 100644 index 0000000000..c8b5b8030f --- /dev/null +++ b/src/sat/cadical/cadical_queue.cpp @@ -0,0 +1,96 @@ +#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 diff --git a/src/sat/cadical/cadical_random.cpp b/src/sat/cadical/cadical_random.cpp new file mode 100644 index 0000000000..85d022e8f9 --- /dev/null +++ b/src/sat/cadical/cadical_random.cpp @@ -0,0 +1,233 @@ +#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 diff --git a/src/sat/cadical/cadical_reap.cpp b/src/sat/cadical/cadical_reap.cpp new file mode 100644 index 0000000000..4625c44dbf --- /dev/null +++ b/src/sat/cadical/cadical_reap.cpp @@ -0,0 +1,142 @@ +#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 diff --git a/src/sat/cadical/cadical_reduce.cpp b/src/sat/cadical/cadical_reduce.cpp new file mode 100644 index 0000000000..cc6f352285 --- /dev/null +++ b/src/sat/cadical/cadical_reduce.cpp @@ -0,0 +1,284 @@ +#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 diff --git a/src/sat/cadical/cadical_rephase.cpp b/src/sat/cadical/cadical_rephase.cpp new file mode 100644 index 0000000000..3b2e625266 --- /dev/null +++ b/src/sat/cadical/cadical_rephase.cpp @@ -0,0 +1,342 @@ +#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 diff --git a/src/sat/cadical/cadical_report.cpp b/src/sat/cadical/cadical_report.cpp new file mode 100644 index 0000000000..18271d17e1 --- /dev/null +++ b/src/sat/cadical/cadical_report.cpp @@ -0,0 +1,302 @@ +#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 diff --git a/src/sat/cadical/cadical_resources.cpp b/src/sat/cadical/cadical_resources.cpp new file mode 100644 index 0000000000..9a2b421e5b --- /dev/null +++ b/src/sat/cadical/cadical_resources.cpp @@ -0,0 +1,166 @@ +#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 diff --git a/src/sat/cadical/cadical_restart.cpp b/src/sat/cadical/cadical_restart.cpp new file mode 100644 index 0000000000..1c6dc44c80 --- /dev/null +++ b/src/sat/cadical/cadical_restart.cpp @@ -0,0 +1,165 @@ +#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 diff --git a/src/sat/cadical/cadical_restore.cpp b/src/sat/cadical/cadical_restore.cpp new file mode 100644 index 0000000000..f9551fb5e9 --- /dev/null +++ b/src/sat/cadical/cadical_restore.cpp @@ -0,0 +1,273 @@ +#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 diff --git a/src/sat/cadical/cadical_score.cpp b/src/sat/cadical/cadical_score.cpp new file mode 100644 index 0000000000..420357929f --- /dev/null +++ b/src/sat/cadical/cadical_score.cpp @@ -0,0 +1,57 @@ +#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 diff --git a/src/sat/cadical/cadical_shrink.cpp b/src/sat/cadical/cadical_shrink.cpp new file mode 100644 index 0000000000..201c220e78 --- /dev/null +++ b/src/sat/cadical/cadical_shrink.cpp @@ -0,0 +1,513 @@ +#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 diff --git a/src/sat/cadical/cadical_signal.cpp b/src/sat/cadical/cadical_signal.cpp new file mode 100644 index 0000000000..099277f035 --- /dev/null +++ b/src/sat/cadical/cadical_signal.cpp @@ -0,0 +1,144 @@ +#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 diff --git a/src/sat/cadical/cadical_solution.cpp b/src/sat/cadical/cadical_solution.cpp new file mode 100644 index 0000000000..1d7514e42f --- /dev/null +++ b/src/sat/cadical/cadical_solution.cpp @@ -0,0 +1,56 @@ +#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 diff --git a/src/sat/cadical/cadical_solver.cpp b/src/sat/cadical/cadical_solver.cpp new file mode 100644 index 0000000000..df051f4dea --- /dev/null +++ b/src/sat/cadical/cadical_solver.cpp @@ -0,0 +1,1764 @@ +#include "global.h" + +#include "internal.hpp" + +ABC_NAMESPACE_IMPL_START + +/*------------------------------------------------------------------------*/ + +namespace CaDiCaL { + +/*------------------------------------------------------------------------*/ + +// See corresponding header file 'cadical.hpp' (!) for more information. +// +// Again, to avoid confusion, note that, 'cadical.hpp' is the header file of +// this file 'solver.cpp', since we want to call the application and main +// file 'cadical.cpp', while at the same time using 'cadical.hpp' as the +// main header file of the library (and not 'solver.hpp'). + +/*------------------------------------------------------------------------*/ +#ifdef LOGGING + +// Needs to be kept in sync with the color schemes used in 'logging.cpp'. +// +#define api_code blue_code // API call color +#define log_code magenta_code // standard/default logging color +#define emph_code bright_magenta_code // emphasized logging color + +#endif +/*------------------------------------------------------------------------*/ + +// Log state transitions. + +#define STATE(S) \ + do { \ + CADICAL_assert (is_power_of_two (S)); \ + if (_state == S) \ + break; \ + _state = S; \ + LOG ("API enters state %s" #S "%s", tout.emph_code (), \ + tout.normal_code ()); \ + } while (0) + +void Solver::transition_to_steady_state () { + if (state () == CONFIGURING) { + LOG ("API leaves state %sCONFIGURING%s", tout.emph_code (), + tout.normal_code ()); + if (internal->opts.check && internal->opts.checkproof) { + internal->check (); + } + } else if (state () == SATISFIED) { + LOG ("API leaves state %sSATISFIED%s", tout.emph_code (), + tout.normal_code ()); + external->reset_assumptions (); + external->reset_concluded (); + external->reset_constraint (); + } else if (state () == UNSATISFIED) { + LOG ("API leaves state %sUNSATISFIED%s", tout.emph_code (), + tout.normal_code ()); + external->reset_assumptions (); + external->reset_concluded (); + external->reset_constraint (); + } else if (state() == INCONCLUSIVE) { + external->reset_assumptions (); + external->reset_concluded (); + external->reset_constraint (); + } + if (state () != STEADY) + STATE (STEADY); +} + +/*------------------------------------------------------------------------*/ +#ifdef LOGGING +/*------------------------------------------------------------------------*/ + +// The following logging code is useful for debugging mostly (or trying to +// understand what the solver is actually doing). It needs to be enabled +// during configuration using the '-l' option for './configure', which +// forces 'LOGGING' to be defined during compilation. This includes all the +// logging code, which then still needs to enabled during run-time with the +// '-l' or 'log' option. + +static void log_api_call (Internal *internal, const char *name, + const char *suffix) { + Logger::log (internal, "API call %s'%s ()'%s %s", tout.api_code (), name, + tout.log_code (), suffix); +} + +static void log_api_call (Internal *internal, const char *name, int arg, + const char *suffix) { + Logger::log (internal, "API call %s'%s (%d)'%s %s", tout.api_code (), + name, arg, tout.log_code (), suffix); +} + +static void log_api_call (Internal *internal, const char *name, + const char *arg, const char *suffix) { + Logger::log (internal, "API call %s'%s (\"%s\")'%s %s", tout.api_code (), + name, arg, tout.log_code (), suffix); +} + +static void log_api_call (Internal *internal, const char *name, + const char *a1, int a2, const char *s) { + Logger::log (internal, "API call %s'%s (\"%s\", %d)'%s %s", + tout.api_code (), name, a1, a2, tout.log_code (), s); +} + +/*------------------------------------------------------------------------*/ + +// We factored out API call begin/end logging and use overloaded functions. + +static void log_api_call_begin (Internal *internal, const char *name) { + Logger::log_empty_line (internal); + log_api_call (internal, name, "started"); +} + +static void log_api_call_begin (Internal *internal, const char *name, + int arg) { + Logger::log_empty_line (internal); + log_api_call (internal, name, arg, "started"); +} + +static void log_api_call_begin (Internal *internal, const char *name, + const char *arg) { + Logger::log_empty_line (internal); + log_api_call (internal, name, arg, "started"); +} + +static void log_api_call_begin (Internal *internal, const char *name, + const char *arg1, int arg2) { + Logger::log_empty_line (internal); + log_api_call (internal, name, arg1, arg2, "started"); +} + +/*------------------------------------------------------------------------*/ + +static void log_api_call_end (Internal *internal, const char *name) { + log_api_call (internal, name, "succeeded"); +} + +static void log_api_call_end (Internal *internal, const char *name, + int lit) { + log_api_call (internal, name, lit, "succeeded"); +} + +static void log_api_call_end (Internal *internal, const char *name, + const char *arg) { + Logger::log_empty_line (internal); + log_api_call (internal, name, arg, "succeeded"); +} + +static void log_api_call_end (Internal *internal, const char *name, + const char *arg, bool res) { + log_api_call (internal, name, arg, res ? "succeeded" : "failed"); +} + +static void log_api_call_end (Internal *internal, const char *name, + const char *arg, int val, bool res) { + log_api_call (internal, name, arg, val, res ? "succeeded" : "failed"); +} + +static void log_api_call_returns (Internal *internal, const char *name, + bool res) { + log_api_call (internal, name, res ? "returns 'true'" : "returns 'false'"); +} + +static void log_api_call_returns (Internal *internal, const char *name, + int res) { + char fmt[32]; + snprintf (fmt, sizeof fmt, "returns '%d'", res); + log_api_call (internal, name, fmt); +} + +static void log_api_call_returns (Internal *internal, const char *name, + int64_t res) { + char fmt[32]; + snprintf (fmt, sizeof fmt, "returns '%" PRId64 "'", res); + log_api_call (internal, name, fmt); +} + +static void log_api_call_returns (Internal *internal, const char *name, + int lit, int res) { + char fmt[32]; + snprintf (fmt, sizeof fmt, "returns '%d'", res); + log_api_call (internal, name, lit, fmt); +} + +static void log_api_call_returns (Internal *internal, const char *name, + const char *arg, bool res) { + log_api_call (internal, name, arg, + res ? "returns 'true'" : "returns 'false'"); +} + +static void log_api_call_returns (Internal *internal, const char *name, + int lit, bool res) { + log_api_call (internal, name, lit, + res ? "returns 'true'" : "returns 'false'"); +} + +static void log_api_call_returns (Internal *internal, const char *name, + const char *arg, const char *res) { + Logger::log (internal, "API call %s'%s (\"%s\")'%s returns '%s'", + tout.api_code (), name, arg, tout.log_code (), + res ? res : "<null>"); +} + +static void log_api_call_returns (Internal *internal, const char *name, + const char *arg1, int arg2, + const char *res) { + Logger::log (internal, "API call %s'%s (\"%s\", %d)'%s returns '%s'", + tout.api_code (), name, arg1, arg2, tout.log_code (), + res ? res : "<null>"); +} + +/*------------------------------------------------------------------------*/ + +#define LOG_API_CALL_BEGIN(...) \ + do { \ + if (!internal->opts.log) \ + break; \ + log_api_call_begin (internal, __VA_ARGS__); \ + } while (0) + +#define LOG_API_CALL_END(...) \ + do { \ + if (!internal->opts.log) \ + break; \ + log_api_call_end (internal, __VA_ARGS__); \ + } while (0) + +#define LOG_API_CALL_RETURNS(...) \ + do { \ + if (!internal->opts.log) \ + break; \ + log_api_call_returns (internal, __VA_ARGS__); \ + } while (0) + +/*------------------------------------------------------------------------*/ +#else // end of 'then' part of 'ifdef LOGGING' +/*------------------------------------------------------------------------*/ + +#define LOG_API_CALL_BEGIN(...) \ + do { \ + } while (0) +#define LOG_API_CALL_END(...) \ + do { \ + } while (0) +#define LOG_API_CALL_RETURNS(...) \ + do { \ + } while (0) + +/*------------------------------------------------------------------------*/ +#endif // end of 'else' part of 'ifdef LOGGING' +/*------------------------------------------------------------------------*/ + +/*------------------------------------------------------------------------*/ +#ifndef CADICAL_NTRACING +/*------------------------------------------------------------------------*/ + +#define TRACE(...) \ + do { \ + /*if ((this == 0)) break; */ /* gcc-12 produces warning */ \ + if ((internal == 0)) \ + break; \ + LOG_API_CALL_BEGIN (__VA_ARGS__); \ + if (!trace_api_file) \ + break; \ + trace_api_call (__VA_ARGS__); \ + } while (0) + +void Solver::trace_api_call (const char *s0) const { + CADICAL_assert (trace_api_file); + LOG ("TRACE %s", s0); + fprintf (trace_api_file, "%s\n", s0); + fflush (trace_api_file); +} + +void Solver::trace_api_call (const char *s0, int i1) const { + CADICAL_assert (trace_api_file); + LOG ("TRACE %s %d", s0, i1); + fprintf (trace_api_file, "%s %d\n", s0, i1); + fflush (trace_api_file); +} + +void Solver::trace_api_call (const char *s0, const char *s1) const { + CADICAL_assert (trace_api_file); + LOG ("TRACE %s %s", s0, s1); + fprintf (trace_api_file, "%s %s\n", s0, s1); + fflush (trace_api_file); +} + +void Solver::trace_api_call (const char *s0, const char *s1, int i2) const { + CADICAL_assert (trace_api_file); + LOG ("TRACE %s %s %d", s0, s1, i2); + fprintf (trace_api_file, "%s %s %d\n", s0, s1, i2); + fflush (trace_api_file); +} + +/*------------------------------------------------------------------------*/ + +// The global 'tracing_api_calls_through_environment_variable_method' flag +// is used to ensure that only one solver traces to a file. Otherwise the +// method to use an environment variable to point to the trace file is +// bogus, since those different solver instances would all write to the same +// file producing garbage. A more sophisticated solution would use a +// different mechanism to tell the solver to which file to trace to, but in +// our experience it is quite convenient to get traces out of applications +// which use the solver as library by just setting an environment variable +// without requiring to change any application code. +// +static bool tracing_api_calls_through_environment_variable_method; + +/*------------------------------------------------------------------------*/ +#else // CADICAL_NTRACING +/*------------------------------------------------------------------------*/ + +#define TRACE(...) \ + do { \ + } while (0) + +/*------------------------------------------------------------------------*/ +#endif +/*------------------------------------------------------------------------*/ + +static bool tracing_nb_lidrup_env_var_method = false; + +Solver::Solver () { + +#ifndef CADICAL_NTRACING + const char *path = getenv ("CADICAL_API_TRACE"); + if (!path) + path = getenv ("CADICALAPITRACE"); + if (path) { + if (tracing_api_calls_through_environment_variable_method) + FATAL ("can not trace API calls of two solver instances " + "using environment variable 'CADICAL_API_TRACE'"); + if (!(trace_api_file = fopen (path, "w"))) + FATAL ("failed to open file '%s' to trace API calls " + "using environment variable 'CADICAL_API_TRACE'", + path); + close_trace_api_file = true; + tracing_api_calls_through_environment_variable_method = true; + } else { + tracing_api_calls_through_environment_variable_method = false; + close_trace_api_file = false; + trace_api_file = 0; + } +#endif + + adding_clause = false; + adding_constraint = false; + _state = INITIALIZING; + internal = new Internal (); + DeferDeletePtr<Internal> delete_internal (internal); + TRACE ("init"); + external = new External (internal); + DeferDeletePtr<External> delete_external (external); + STATE (CONFIGURING); +#ifndef CADICAL_NTRACING + if (tracing_api_calls_through_environment_variable_method) + message ("tracing API calls to '%s'", path); +#endif + + const char *lidrup_path = getenv ("CADICAL_LIDRUP_TRACE"); + if (!lidrup_path) + lidrup_path = getenv ("CADICALLIDRUPTRACE"); + if (lidrup_path) { + + // if (tracing_nb_lidrup_env_var_method) + // FATAL ("can not trace LIDRUP of two solver instances " + // "using environment variable 'CADICAL_LIDRUP_TRACE'"); + // Here we use the solver interface to setup non-binary IDRUP tracing to + // the defined file. Options set by the user can and will overwrite + // these settings if neeed be. + set ("lidrup", 1); + set ("binary", 0); + trace_proof (lidrup_path); + tracing_nb_lidrup_env_var_method = true; + } else { + tracing_nb_lidrup_env_var_method = false; + } + + delete_internal.release (); + delete_external.release (); +} + +Solver::~Solver () { + + TRACE ("reset"); + REQUIRE_VALID_OR_SOLVING_STATE (); + STATE (DELETING); + + tracing_nb_lidrup_env_var_method = false; + +#ifdef LOGGING + // + // After deleting 'internal' logging does not work anymore. + // + bool logging = internal->opts.log; + int level = internal->level; + string prefix = internal->prefix; +#endif + + delete internal; + delete external; + +#ifndef CADICAL_NTRACING + if (close_trace_api_file) { + close_trace_api_file = false; + CADICAL_assert (trace_api_file); + CADICAL_assert (tracing_api_calls_through_environment_variable_method); + fclose (trace_api_file); + tracing_api_calls_through_environment_variable_method = false; + } +#endif + +#ifdef LOGGING + // + // Need to log success of this API call manually. + // + if (logging) { + printf ("%s%sLOG %s%d%s API call %s'reset ()'%s succeeded%s\n", + prefix.c_str (), tout.log_code (), tout.emph_code (), level, + tout.log_code (), tout.api_code (), tout.log_code (), + tout.normal_code ()); + fflush (stdout); + } +#endif +} + +/*------------------------------------------------------------------------*/ + +int Solver::vars () { + TRACE ("vars"); + REQUIRE_VALID_OR_SOLVING_STATE (); + int res = external->max_var; + LOG_API_CALL_RETURNS ("vars", res); + return res; +} + +void Solver::reserve (int min_max_var) { + TRACE ("reserve", min_max_var); + REQUIRE_VALID_STATE (); + transition_to_steady_state (); + external->reset_extended (); + external->init (min_max_var); + LOG_API_CALL_END ("reserve", min_max_var); +} + +int Solver::reserve_difference (int number_of_vars) { + TRACE ("reserve_difference", number_of_vars); + REQUIRE_VALID_STATE (); + transition_to_steady_state (); + external->reset_extended (); + int new_max_var = external->max_var + number_of_vars; + external->init (new_max_var); + LOG_API_CALL_END ("reserve_difference", number_of_vars); + return new_max_var; +} + +/*------------------------------------------------------------------------*/ +#ifndef CADICAL_NTRACING + +void Solver::trace_api_calls (FILE *file) { + LOG_API_CALL_BEGIN ("trace_api_calls"); + REQUIRE_VALID_STATE (); + REQUIRE (file != 0, "invalid zero file argument"); + REQUIRE (!tracing_api_calls_through_environment_variable_method, + "already tracing API calls " + "using environment variable 'CADICAL_API_TRACE'"); + REQUIRE (!trace_api_file, "called twice"); + trace_api_file = file; + LOG_API_CALL_END ("trace_api_calls"); + trace_api_call ("init"); +} + +#endif +/*------------------------------------------------------------------------*/ + +bool Solver::is_valid_option (const char *name) { + return Options::has (name); +} + +bool Solver::is_preprocessing_option (const char *name) { + return Options::is_preprocessing_option (name); +} + +bool Solver::is_valid_long_option (const char *arg) { + string name; + int tmp; + return Options::parse_long_option (arg, name, tmp); +} + +int Solver::get (const char *arg) { + REQUIRE_VALID_OR_SOLVING_STATE (); + return internal->opts.get (arg); +} + +bool Solver::set (const char *arg, int val) { + TRACE ("set", arg, val); + REQUIRE_VALID_STATE (); + if (strcmp (arg, "log") && strcmp (arg, "quiet") && + strcmp (arg, "report") && strcmp (arg, "verbose")) { + REQUIRE ( + state () == CONFIGURING, + "can only set option 'set (\"%s\", %d)' right after initialization", + arg, val); + } + bool res = internal->opts.set (arg, val); + LOG_API_CALL_END ("set", arg, val, res); + + return res; +} + +bool Solver::set_long_option (const char *arg) { + LOG_API_CALL_BEGIN ("set", arg); + REQUIRE_VALID_STATE (); + REQUIRE (state () == CONFIGURING, + "can only set option '%s' right after initialization", arg); + bool res; + if (arg[0] != '-' || arg[1] != '-') + res = false; + else { + int val; + string name; + res = Options::parse_long_option (arg, name, val); + if (res) + set (name.c_str (), val); + } + LOG_API_CALL_END ("set", arg, res); + return res; +} + +void Solver::optimize (int arg) { + LOG_API_CALL_BEGIN ("optimize", arg); + REQUIRE_VALID_STATE (); + internal->opts.optimize (arg); + LOG_API_CALL_END ("optimize", arg); +} + +bool Solver::limit (const char *arg, int val) { + TRACE ("limit", arg, val); + REQUIRE_VALID_STATE (); + bool res = internal->limit (arg, val); + LOG_API_CALL_END ("limit", arg, val, res); + return res; +} + +bool Solver::is_valid_limit (const char *arg) { + return Internal::is_valid_limit (arg); +} + +void Solver::prefix (const char *str) { + LOG_API_CALL_BEGIN ("prefix", str); + REQUIRE_VALID_OR_SOLVING_STATE (); + internal->prefix = str; + LOG_API_CALL_END ("prefix", str); +} + +bool Solver::is_valid_configuration (const char *name) { + return Config::has (name); +} + +bool Solver::configure (const char *name) { + TRACE ("configure", name); + LOG_API_CALL_BEGIN ("configure", name); + REQUIRE_VALID_STATE (); + REQUIRE (state () == CONFIGURING, + "can only set configuration '%s' right after initialization", + name); + bool res = Config::set (internal->opts, name); + LOG_API_CALL_END ("configure", name, res); + return res; +} + +/*===== IPASIR BEGIN =====================================================*/ + +void Solver::add (int lit) { + TRACE ("add", lit); + REQUIRE_VALID_STATE (); + if (lit) + REQUIRE_VALID_LIT (lit); + transition_to_steady_state (); + external->add (lit); + adding_clause = lit; + if (adding_clause) + STATE (ADDING); + else if (!adding_constraint) + STATE (STEADY); + LOG_API_CALL_END ("add", lit); +} + +void Solver::clause (int a) { + REQUIRE_VALID_LIT (a); + add (a), add (0); +} + +void Solver::clause (int a, int b) { + REQUIRE_VALID_LIT (a); + REQUIRE_VALID_LIT (b); + add (a), add (b), add (0); +} + +void Solver::clause (int a, int b, int c) { + REQUIRE_VALID_LIT (a); + REQUIRE_VALID_LIT (b); + REQUIRE_VALID_LIT (c); + add (a), add (b), add (c), add (0); +} + +void Solver::clause (int a, int b, int c, int d) { + REQUIRE_VALID_LIT (a); + REQUIRE_VALID_LIT (b); + REQUIRE_VALID_LIT (c); + REQUIRE_VALID_LIT (d); + add (a), add (b), add (c), add (d), add (0); +} + +void Solver::clause (int a, int b, int c, int d, int e) { + REQUIRE_VALID_LIT (a); + REQUIRE_VALID_LIT (b); + REQUIRE_VALID_LIT (c); + REQUIRE_VALID_LIT (d); + REQUIRE_VALID_LIT (e); + add (a), add (b), add (c), add (d), add (e), add (0); +} + +void Solver::clause (const int *lits, size_t size) { + REQUIRE (!size || lits, + "first argument 'lits' zero while second argument 'size' not"); + const int *end = lits + size; + for (const int *p = lits; p != end; p++) { + const int lit = *p; + REQUIRE_VALID_LIT (lit); + add (lit); + } + add (0); +} + +void Solver::clause (const std::vector<int> &lits) { + for (auto lit : lits) { + REQUIRE_VALID_LIT (lit); + add (lit); + } + add (0); +} + +bool Solver::inconsistent () { return internal->unsat; } + +void Solver::constrain (int lit) { + TRACE ("constrain", lit); + REQUIRE_VALID_STATE (); + if (lit) + REQUIRE_VALID_LIT (lit); + transition_to_steady_state (); + external->constrain (lit); + adding_constraint = lit; + if (adding_constraint) + STATE (ADDING); + else if (!adding_clause) + STATE (STEADY); + LOG_API_CALL_END ("constrain", lit); +} + +void Solver::assume (int lit) { + TRACE ("assume", lit); + REQUIRE_VALID_STATE (); + REQUIRE_VALID_LIT (lit); + transition_to_steady_state (); + external->assume (lit); + LOG_API_CALL_END ("assume", lit); +} + +int Solver::lookahead () { + TRACE ("lookahead"); + REQUIRE_VALID_OR_SOLVING_STATE (); + int lit = external->lookahead (); + TRACE ("lookahead"); + return lit; +} + +Solver::CubesWithStatus Solver::generate_cubes (int depth, int min_depth) { + TRACE ("lookahead_cubes"); + REQUIRE_VALID_OR_SOLVING_STATE (); + auto cubes = external->generate_cubes (depth, min_depth); + TRACE ("lookahead_cubes"); + + CubesWithStatus cubes2; + cubes2.status = cubes.status; + cubes2.cubes = cubes.cubes; + return cubes2; +} + +void Solver::reset_assumptions () { + TRACE ("reset_assumptions"); + REQUIRE_VALID_STATE (); + transition_to_steady_state (); + external->reset_assumptions (); + external->reset_concluded (); + LOG_API_CALL_END ("reset_assumptions"); +} + +void Solver::reset_constraint () { + TRACE ("reset_constraint"); + REQUIRE_VALID_STATE (); + transition_to_steady_state (); + external->reset_constraint (); + external->reset_concluded (); + LOG_API_CALL_END ("reset_constraint"); +} + +/*------------------------------------------------------------------------*/ + +int Solver::propagate () { + TRACE ("propagate_assumptions"); + REQUIRE_VALID_STATE (); + transition_to_steady_state (); + const int res = external->propagate_assumptions (); + if (tracing_nb_lidrup_env_var_method) + flush_proof_trace (true); + LOG_API_CALL_RETURNS ("propagate_assumptions", res); + if (res == 10) + STATE (SATISFIED); + else if (res == 20) + STATE (UNSATISFIED); + else + STATE (INCONCLUSIVE); + return res; +} + +void Solver::implied (std::vector<int> &entrailed) { + TRACE ("implied"); + REQUIRE_VALID_STATE (); + REQUIRE (state () == INCONCLUSIVE, + "can only get implied literals only in unknown state"); + external->conclude_unknown (); + external->implied (entrailed); + if (tracing_nb_lidrup_env_var_method) + flush_proof_trace (true); + LOG_API_CALL_RETURNS ("implied", (int) entrailed.size ()); +} + +/*------------------------------------------------------------------------*/ + +int Solver::call_external_solve_and_check_results (bool preprocess_only) { + transition_to_steady_state (); + CADICAL_assert (state () & READY); + STATE (SOLVING); + const int res = external->solve (preprocess_only); + if (res == 10) + STATE (SATISFIED); + else if (res == 20) + STATE (UNSATISFIED); + else + STATE (INCONCLUSIVE); +#if 0 // EXPENSIVE ALTERNATIVE ASSUMPTION CHECKING + // This checks that the set of failed assumptions form a core using the + // external 'copy (...)' function to copy the solver, which can be trusted + // less, since it involves copying the extension stack too. The + // 'External::check_assumptions_failing' is a better alternative and can + // be enabled by options too. We keep this code though to have an + // alternative failed assumption checking available for debugging. + // + if (res == 20 && !external->assumptions.empty ()) { + Solver checker; + // checking restored clauses does not work (because the clauses are not added) + checker.set("checkproof", 1); + checker.set("lrat", 0); + checker.prefix ("checker "); + copy (checker); + checker.set("log", 1); + for (const auto & lit : external->assumptions) + if (failed (lit)) + checker.add (lit), checker.add (0); + if (checker.solve () != 20) + FATAL ("copying assumption checker failed"); + } +#endif + if (!res) { + external->reset_assumptions (); + external->reset_constraint (); + external->reset_concluded (); + } + return res; +} + +int Solver::solve () { + TRACE ("solve"); + REQUIRE_READY_STATE (); + const int res = call_external_solve_and_check_results (false); + LOG_API_CALL_RETURNS ("solve", res); + if (tracing_nb_lidrup_env_var_method) + flush_proof_trace (true); + return res; +} + +int Solver::simplify (int rounds) { + TRACE ("simplify", rounds); + REQUIRE_READY_STATE (); + REQUIRE (rounds >= 0, "negative number of simplification rounds '%d'", + rounds); + internal->limit ("preprocessing", rounds); + const int res = call_external_solve_and_check_results (true); + LOG_API_CALL_RETURNS ("simplify", rounds, res); + return res; +} + +/*------------------------------------------------------------------------*/ + +int Solver::val (int lit) { + TRACE ("val", lit); + REQUIRE_VALID_STATE (); + REQUIRE_VALID_LIT (lit); + REQUIRE (state () == SATISFIED, "can only get value in satisfied state"); + if (!external->extended) + external->extend (); + external->conclude_sat (); + int res = external->ival (lit); + LOG_API_CALL_RETURNS ("val", lit, res); + CADICAL_assert (state () == SATISFIED); + CADICAL_assert (res == lit || res == -lit); + return res; +} + +bool Solver::flip (int lit) { + TRACE ("flip", lit); + REQUIRE_VALID_STATE (); + REQUIRE_VALID_LIT (lit); + REQUIRE (state () == SATISFIED, "can only flip value in satisfied state"); + REQUIRE (!external->propagator, + "can only flip when no external propagator is present"); + bool res = external->flip (lit); + LOG_API_CALL_RETURNS ("flip", lit, res); + CADICAL_assert (state () == SATISFIED); + return res; +} + +bool Solver::flippable (int lit) { + TRACE ("flippable", lit); + REQUIRE_VALID_STATE (); + REQUIRE_VALID_LIT (lit); + REQUIRE (state () == SATISFIED, "can only flip value in satisfied state"); + REQUIRE (!external->propagator, + "can only flip when no external propagator is present"); + bool res = external->flippable (lit); + LOG_API_CALL_RETURNS ("flippable", lit, res); + CADICAL_assert (state () == SATISFIED); + return res; +} + +bool Solver::failed (int lit) { + TRACE ("failed", lit); + REQUIRE_VALID_STATE (); + REQUIRE_VALID_LIT (lit); + REQUIRE (state () == UNSATISFIED, + "can only get failed assumptions in unsatisfied state"); + bool res = external->failed (lit); + LOG_API_CALL_RETURNS ("failed", lit, res); + CADICAL_assert (state () == UNSATISFIED); + return res; +} + +bool Solver::constraint_failed () { + TRACE ("constraint_failed"); + REQUIRE_VALID_STATE (); + REQUIRE (state () == UNSATISFIED, + "can only determine if constraint failed in unsatisfied state"); + bool res = external->failed_constraint (); + LOG_API_CALL_RETURNS ("constraint_failed", res); + CADICAL_assert (state () == UNSATISFIED); + return res; +} + +int Solver::fixed (int lit) const { + TRACE ("fixed", lit); + REQUIRE_VALID_STATE (); + REQUIRE_VALID_LIT (lit); + int res = external->fixed (lit); + LOG_API_CALL_RETURNS ("fixed", lit, res); + return res; +} + +void Solver::phase (int lit) { + TRACE ("phase", lit); + REQUIRE_VALID_OR_SOLVING_STATE (); + REQUIRE_VALID_LIT (lit); + external->phase (lit); + LOG_API_CALL_END ("phase", lit); +} + +void Solver::unphase (int lit) { + TRACE ("unphase", lit); + REQUIRE_VALID_OR_SOLVING_STATE (); + REQUIRE_VALID_LIT (lit); + external->unphase (lit); + LOG_API_CALL_END ("unphase", lit); +} + +/*------------------------------------------------------------------------*/ + +void Solver::terminate () { + LOG_API_CALL_BEGIN ("terminate"); + REQUIRE_VALID_OR_SOLVING_STATE (); + external->terminate (); + LOG_API_CALL_END ("terminate"); +} + +void Solver::connect_terminator (Terminator *terminator) { + LOG_API_CALL_BEGIN ("connect_terminator"); + REQUIRE_VALID_STATE (); + REQUIRE (terminator, "can not connect zero terminator"); +#ifdef LOGGING + if (external->terminator) + LOG ("connecting new terminator (disconnecting previous one)"); + else + LOG ("connecting new terminator (no previous one)"); +#endif + external->terminator = terminator; + LOG_API_CALL_END ("connect_terminator"); +} + +void Solver::disconnect_terminator () { + LOG_API_CALL_BEGIN ("disconnect_terminator"); + REQUIRE_VALID_STATE (); +#ifdef LOGGING + if (external->terminator) + LOG ("disconnecting previous terminator"); + else + LOG ("ignoring to disconnect terminator (no previous one)"); +#endif + external->terminator = 0; + LOG_API_CALL_END ("disconnect_terminator"); +} + +/*------------------------------------------------------------------------*/ + +void Solver::connect_learner (Learner *learner) { + LOG_API_CALL_BEGIN ("connect_learner"); + REQUIRE_VALID_STATE (); + REQUIRE (learner, "can not connect zero learner"); +#ifdef LOGGING + if (external->learner) + LOG ("connecting new learner (disconnecting previous one)"); + else + LOG ("connecting new learner (no previous one)"); +#endif + external->learner = learner; + LOG_API_CALL_END ("connect_learner"); +} + +void Solver::disconnect_learner () { + LOG_API_CALL_BEGIN ("disconnect_learner"); + REQUIRE_VALID_STATE (); +#ifdef LOGGING + if (external->learner) + LOG ("disconnecting previous learner"); + else + LOG ("ignoring to disconnect learner (no previous one)"); +#endif + external->learner = 0; + LOG_API_CALL_END ("disconnect_learner"); +} + +/*===== IPASIR END =======================================================*/ + +void Solver::connect_fixed_listener ( + FixedAssignmentListener *fixed_listener) { + LOG_API_CALL_BEGIN ("connect_fixed_listener"); + REQUIRE_VALID_STATE (); + REQUIRE (fixed_listener, "can not connect zero fixed listener"); + +#ifdef LOGGING + if (external->fixed_listener) + LOG ("connecting new listener of fixed assignments (disconnecting " + "previous one)"); + else + LOG ("connecting new listener of fixed assigments (no previous one)"); +#endif + if (external->fixed_listener) + disconnect_fixed_listener (); + external->fixed_listener = fixed_listener; + // Listeners are treated as real-time listeners, thus previously found + // fixed assignments are not sent out (would be rather expensive to + // recover it retrospect, see external_propagate.cpp/get_fixed_literals () + // function). + LOG_API_CALL_END ("connect_fixed_listener"); +} + +void Solver::disconnect_fixed_listener () { + LOG_API_CALL_BEGIN ("disconnect_fixed_listener"); + REQUIRE_VALID_STATE (); +#ifdef LOGGING + if (external->fixed_listener) + LOG ("disconnecting previous listener of fixed assignments"); + else + LOG ("ignoring to disconnect listener of fixed assignments (no " + "previous one)"); +#endif + external->fixed_listener = 0; + LOG_API_CALL_END ("disconnect_fixed_listener"); +} + +/*===== IPASIR-UP BEGIN ==================================================*/ + +void Solver::connect_external_propagator (ExternalPropagator *propagator) { + LOG_API_CALL_BEGIN ("connect_external_propagator"); + REQUIRE_VALID_STATE (); + REQUIRE (propagator, "can not connect zero propagator"); +#ifdef LOGGING + if (external->propagator) + LOG ("connecting new external propagator (disconnecting previous one)"); + else + LOG ("connecting new external propagator (no previous one)"); +#endif + if (external->propagator) + disconnect_external_propagator (); + + external->propagator = propagator; + internal->connect_propagator (); + internal->external_prop = true; + internal->external_prop_is_lazy = propagator->is_lazy; + LOG_API_CALL_END ("connect_external_propagator"); +} + +void Solver::disconnect_external_propagator () { + LOG_API_CALL_BEGIN ("disconnect_external_propagator"); + REQUIRE_VALID_STATE (); + +#ifdef LOGGING + if (external->propagator) + LOG ("disconnecting previous external propagator"); + else + LOG ("ignoring to disconnect external propagator (no previous one)"); +#endif + if (external->propagator) + external->reset_observed_vars (); + + external->propagator = 0; + internal->set_tainted_literal (); + internal->external_prop = false; + internal->external_prop_is_lazy = true; + LOG_API_CALL_END ("disconnect_external_propagator"); +} + +void Solver::add_observed_var (int idx) { + TRACE ("observe", idx); + REQUIRE_VALID_OR_SOLVING_STATE (); + REQUIRE_VALID_LIT (idx); + external->add_observed_var (idx); + LOG_API_CALL_END ("observe", idx); +} + +void Solver::remove_observed_var (int idx) { + TRACE ("unobserve", idx); + REQUIRE_VALID_STATE (); + REQUIRE_VALID_LIT (idx); + external->remove_observed_var (idx); + LOG_API_CALL_END ("unobserve", idx); +} + +void Solver::reset_observed_vars () { + TRACE ("reset_observed_vars"); + REQUIRE_VALID_OR_SOLVING_STATE (); + external->reset_observed_vars (); + LOG_API_CALL_END ("reset_observed_vars"); +} + +/*===== IPASIR-UP END ====================================================*/ + +int Solver::active () const { + TRACE ("active"); + REQUIRE_VALID_STATE (); + int res = internal->active (); + LOG_API_CALL_RETURNS ("active", res); + return res; +} + +int64_t Solver::redundant () const { + TRACE ("redundant"); + REQUIRE_VALID_STATE (); + int64_t res = internal->redundant (); + LOG_API_CALL_RETURNS ("redundant", res); + return res; +} + +int64_t Solver::irredundant () const { + TRACE ("irredundant"); + REQUIRE_VALID_STATE (); + int64_t res = internal->irredundant (); + LOG_API_CALL_RETURNS ("irredundant", res); + return res; +} + +/*------------------------------------------------------------------------*/ + +void Solver::freeze (int lit) { + TRACE ("freeze", lit); + REQUIRE_VALID_STATE (); + REQUIRE_VALID_LIT (lit); + external->freeze (lit); + LOG_API_CALL_END ("freeze", lit); +} + +void Solver::melt (int lit) { + TRACE ("melt", lit); + REQUIRE_VALID_STATE (); + REQUIRE_VALID_LIT (lit); + REQUIRE (external->frozen (lit), + "can not melt completely melted literal '%d'", lit); + external->melt (lit); + LOG_API_CALL_END ("melt", lit); +} + +bool Solver::frozen (int lit) const { + TRACE ("frozen", lit); + REQUIRE_VALID_STATE (); + REQUIRE_VALID_LIT (lit); + bool res = external->frozen (lit); + LOG_API_CALL_RETURNS ("frozen", lit, res); + return res; +} + +/*------------------------------------------------------------------------*/ + +bool Solver::trace_proof (FILE *external_file, const char *name) { + TRACE ("trace_proof", name); + REQUIRE_VALID_STATE (); + REQUIRE ( + state () == CONFIGURING, + "can only start proof tracing to '%s' right after initialization", + name); + File *internal_file = File::write (internal, external_file, name); + CADICAL_assert (internal_file); + internal->trace (internal_file); + LOG_API_CALL_RETURNS ("trace_proof", name, true); + return true; +} + +bool Solver::trace_proof (const char *path) { + TRACE ("trace_proof", path); + REQUIRE_VALID_STATE (); + REQUIRE ( + state () == CONFIGURING, + "can only start proof tracing to '%s' right after initialization", + path); + File *internal_file = File::write (internal, path); + bool res = (internal_file != 0); + internal->trace (internal_file); + LOG_API_CALL_RETURNS ("trace_proof", path, res); + return res; +} + +void Solver::flush_proof_trace (bool print_statistics_unless_quiet) { + TRACE ("flush_proof_trace"); + REQUIRE_VALID_STATE (); + REQUIRE (!internal->file_tracers.empty (), "proof is not traced"); + REQUIRE (!internal->file_tracers.back ()->closed (), + "proof trace already closed"); + internal->flush_trace (print_statistics_unless_quiet); + LOG_API_CALL_END ("flush_proof_trace"); +} + +void Solver::close_proof_trace (bool print_statistics_unless_quiet) { + TRACE ("close_proof_trace"); + REQUIRE_VALID_STATE (); + REQUIRE (!internal->file_tracers.empty (), "proof is not traced"); + REQUIRE (!internal->file_tracers.back ()->closed (), + "proof trace already closed"); + internal->close_trace (print_statistics_unless_quiet); + LOG_API_CALL_END ("close_proof_trace"); +} + +/*------------------------------------------------------------------------*/ + +void Solver::connect_proof_tracer (Tracer *tracer, bool antecedents, + bool finalize_clauses) { + LOG_API_CALL_BEGIN ("connect proof tracer"); + REQUIRE_VALID_STATE (); + REQUIRE (state () == CONFIGURING, + "can only start proof tracing to right after initialization"); + REQUIRE (tracer, "can not connect zero tracer"); + internal->connect_proof_tracer (tracer, antecedents, finalize_clauses); + LOG_API_CALL_END ("connect proof tracer"); +} + +void Solver::connect_proof_tracer (InternalTracer *tracer, bool antecedents, + bool finalize_clauses) { + LOG_API_CALL_BEGIN ("connect proof tracer"); + REQUIRE_VALID_STATE (); + REQUIRE (state () == CONFIGURING, + "can only start proof tracing to right after initialization"); + REQUIRE (tracer, "can not connect zero tracer"); + internal->connect_proof_tracer (tracer, antecedents, finalize_clauses); + LOG_API_CALL_END ("connect proof tracer"); +} + +void Solver::connect_proof_tracer (StatTracer *tracer, bool antecedents, + bool finalize_clauses) { + LOG_API_CALL_BEGIN ("connect proof tracer with stats"); + REQUIRE_VALID_STATE (); + REQUIRE (state () == CONFIGURING, + "can only start proof tracing to right after initialization"); + REQUIRE (tracer, "can not connect zero tracer"); + internal->connect_proof_tracer (tracer, antecedents, finalize_clauses); + LOG_API_CALL_END ("connect proof tracer with stats"); +} + +void Solver::connect_proof_tracer (FileTracer *tracer, bool antecedents, + bool finalize_clauses) { + LOG_API_CALL_BEGIN ("connect proof tracer with file"); + REQUIRE_VALID_STATE (); + REQUIRE (state () == CONFIGURING, + "can only start proof tracing right after initialization"); + REQUIRE (tracer, "can not connect zero tracer"); + internal->connect_proof_tracer (tracer, antecedents, finalize_clauses); + LOG_API_CALL_END ("connect proof tracer with file"); +} + +bool Solver::disconnect_proof_tracer (Tracer *tracer) { + LOG_API_CALL_BEGIN ("disconnect proof tracer"); + REQUIRE_VALID_STATE (); + REQUIRE (tracer, "can not disconnect zero tracer"); + bool res = internal->disconnect_proof_tracer (tracer); + LOG_API_CALL_RETURNS ("connect proof tracer", res); + return res; +} + +bool Solver::disconnect_proof_tracer (StatTracer *tracer) { + LOG_API_CALL_BEGIN ("disconnect proof tracer"); + REQUIRE_VALID_STATE (); + REQUIRE (tracer, "can not disconnect zero tracer"); + bool res = internal->disconnect_proof_tracer (tracer); + LOG_API_CALL_RETURNS ("disconnect proof tracer", res); + return res; +} + +bool Solver::disconnect_proof_tracer (FileTracer *tracer) { + LOG_API_CALL_BEGIN ("disconnect proof tracer"); + REQUIRE_VALID_STATE (); + REQUIRE (tracer, "can not disconnect zero tracer"); + bool res = internal->disconnect_proof_tracer (tracer); + LOG_API_CALL_RETURNS ("disconnect proof tracer", res); + return res; +} + +/*------------------------------------------------------------------------*/ + +void Solver::conclude () { + TRACE ("conclude"); + REQUIRE_VALID_STATE (); + REQUIRE (state () == UNSATISFIED || state () == SATISFIED || + state () == INCONCLUSIVE, + "can only conclude in satisfied, unsatisfied or inconclusive state"); + if (state () == UNSATISFIED) + internal->conclude_unsat (); + else if (state () == SATISFIED) + external->conclude_sat (); + else if (state () == INCONCLUSIVE) + external->conclude_unknown (); + CADICAL_assert (state () == UNSATISFIED || state () == SATISFIED || + state () == INCONCLUSIVE); + LOG_API_CALL_END ("conclude"); +} + +/*------------------------------------------------------------------------*/ + +void Solver::build (FILE *file, const char *prefix) { + + CADICAL_assert (file == stdout || file == stderr); + + Terminal *terminal; + + if (file == stdout) + terminal = &tout; + else if (file == stderr) + terminal = &terr; + else + terminal = 0; + + const char *v = CaDiCaL::version (); + const char *i = identifier (); + const char *c = compiler (); + const char *b = date (); + const char *f = flags (); + + CADICAL_assert (v); + + fputs (prefix, file); + if (terminal) + terminal->magenta (); + fputs ("Version ", file); + if (terminal) + terminal->normal (); + fputs (v, file); + if (i) { + if (terminal) + terminal->magenta (); + fputc (' ', file); + fputs (i, file); + if (terminal) + terminal->normal (); + } + fputc ('\n', file); + + if (c) { + fputs (prefix, file); + if (terminal) + terminal->magenta (); + fputs (c, file); + if (f) { + fputc (' ', file); + fputs (f, file); + } + if (terminal) + terminal->normal (); + fputc ('\n', file); + } + + if (b) { + fputs (prefix, file); + if (terminal) + terminal->magenta (); + fputs (b, file); + if (terminal) + terminal->normal (); + fputc ('\n', file); + } + + fflush (file); +} + +const char *Solver::version () { return CaDiCaL::version (); } + +const char *Solver::signature () { return CaDiCaL::signature (); } + +void Solver::options () { + REQUIRE_VALID_STATE (); + internal->opts.print (); +} + +void Solver::usage () { Options::usage (); } + +void Solver::configurations () { Config::usage (); } + +void Solver::statistics () { + if (state () == DELETING) + return; + TRACE ("stats"); + REQUIRE_VALID_OR_SOLVING_STATE (); + internal->print_statistics (); + LOG_API_CALL_END ("stats"); +} + +void Solver::resources () { + if (state () == DELETING) + return; + TRACE ("resources"); + REQUIRE_VALID_OR_SOLVING_STATE (); + internal->print_resource_usage (); + LOG_API_CALL_END ("resources"); +} + +/*------------------------------------------------------------------------*/ + +const char *Solver::read_dimacs (File *file, int &vars, int strict, + bool *incremental, vector<int> *cubes) { + REQUIRE_VALID_STATE (); + REQUIRE (state () == CONFIGURING, + "can only read DIMACS file right after initialization"); + Parser *parser = new Parser (this, file, incremental, cubes); + const char *err = parser->parse_dimacs (vars, strict); + delete parser; + return err; +} + +const char *Solver::read_dimacs (FILE *external_file, const char *name, + int &vars, int strict) { + LOG_API_CALL_BEGIN ("read_dimacs", name); + REQUIRE_VALID_STATE (); + REQUIRE (state () == CONFIGURING, + "can only read DIMACS file right after initialization"); + File *file = File::read (internal, external_file, name); + CADICAL_assert (file); + const char *err = read_dimacs (file, vars, strict); + delete file; + LOG_API_CALL_RETURNS ("read_dimacs", name, err); + return err; +} + +const char *Solver::read_dimacs (const char *path, int &vars, int strict) { + LOG_API_CALL_BEGIN ("read_dimacs", path); + REQUIRE_VALID_STATE (); + REQUIRE (state () == CONFIGURING, + "can only read DIMACS file right after initialization"); + File *file = File::read (internal, path); + if (!file) + return internal->error_message.init ("failed to read DIMACS file '%s'", + path); + const char *err = read_dimacs (file, vars, strict); + delete file; + LOG_API_CALL_RETURNS ("read_dimacs", path, err); + return err; +} + +const char *Solver::read_dimacs (FILE *external_file, const char *name, + int &vars, int strict, bool &incremental, + vector<int> &cubes) { + LOG_API_CALL_BEGIN ("read_dimacs", name); + REQUIRE_VALID_STATE (); + REQUIRE (state () == CONFIGURING, + "can only read DIMACS file right after initialization"); + File *file = File::read (internal, external_file, name); + CADICAL_assert (file); + const char *err = read_dimacs (file, vars, strict, &incremental, &cubes); + delete file; + LOG_API_CALL_RETURNS ("read_dimacs", name, err); + return err; +} + +const char *Solver::read_dimacs (const char *path, int &vars, int strict, + bool &incremental, vector<int> &cubes) { + LOG_API_CALL_BEGIN ("read_dimacs", path); + REQUIRE_VALID_STATE (); + REQUIRE (state () == CONFIGURING, + "can only read DIMACS file right after initialization"); + File *file = File::read (internal, path); + if (!file) + return internal->error_message.init ("failed to read DIMACS file '%s'", + path); + const char *err = read_dimacs (file, vars, strict, &incremental, &cubes); + delete file; + LOG_API_CALL_RETURNS ("read_dimacs", path, err); + return err; +} + +const char *Solver::read_solution (const char *path) { + LOG_API_CALL_BEGIN ("solution", path); + REQUIRE_VALID_STATE (); + File *file = File::read (internal, path); + if (!file) + return internal->error_message.init ( + "failed to read solution file '%s'", path); + Parser *parser = new Parser (this, file, 0, 0); + const char *err = parser->parse_solution (); + delete parser; + delete file; + if (!err) + external->check_assignment (&External::sol); + LOG_API_CALL_RETURNS ("read_solution", path, err); + return err; +} + +/*------------------------------------------------------------------------*/ + +void Solver::dump_cnf () { + TRACE ("dump"); + REQUIRE_INITIALIZED (); + internal->dump (); + LOG_API_CALL_END ("dump"); +} + +/*------------------------------------------------------------------------*/ + +ExternalPropagator *Solver::get_propagator () { + return external->propagator; +} + +bool Solver::observed (int lit) { + TRACE ("observed", lit); + REQUIRE_VALID_OR_SOLVING_STATE (); + REQUIRE_VALID_LIT (lit); + bool res = external->observed (lit); + LOG_API_CALL_RETURNS ("observed", lit, res); + return res; +} + +bool Solver::is_witness (int lit) { + TRACE ("is_witness", lit); + REQUIRE_VALID_OR_SOLVING_STATE (); + REQUIRE_VALID_LIT (lit); + bool res = external->is_witness (lit); + LOG_API_CALL_RETURNS ("is_witness", lit, res); + return res; +} + +bool Solver::is_decision (int lit) { + TRACE ("is_decision", lit); + REQUIRE_VALID_OR_SOLVING_STATE (); + REQUIRE_VALID_LIT (lit); + bool res = external->is_decision (lit); + LOG_API_CALL_RETURNS ("is_decision", lit, res); + return res; +} + +void Solver::force_backtrack (size_t new_level) { + TRACE ("force_backtrack", new_level); + REQUIRE_VALID_OR_SOLVING_STATE (); + external->force_backtrack (new_level); +} + +/*------------------------------------------------------------------------*/ + +bool Solver::traverse_clauses (ClauseIterator &it) const { + LOG_API_CALL_BEGIN ("traverse_clauses"); + REQUIRE_VALID_STATE (); + bool res = external->traverse_all_frozen_units_as_clauses (it) && + internal->traverse_clauses (it) && + internal->traverse_constraint (it); + LOG_API_CALL_RETURNS ("traverse_clauses", res); + return res; +} + +bool Solver::traverse_witnesses_backward (WitnessIterator &it) const { + LOG_API_CALL_BEGIN ("traverse_witnesses_backward"); + REQUIRE_VALID_STATE (); + bool res = external->traverse_all_non_frozen_units_as_witnesses (it) && + external->traverse_witnesses_backward (it); + LOG_API_CALL_RETURNS ("traverse_witnesses_backward", res); + return res; +} + +bool Solver::traverse_witnesses_forward (WitnessIterator &it) const { + LOG_API_CALL_BEGIN ("traverse_witnesses_forward"); + REQUIRE_VALID_STATE (); + bool res = external->traverse_witnesses_forward (it) && + external->traverse_all_non_frozen_units_as_witnesses (it); + LOG_API_CALL_RETURNS ("traverse_witnesses_forward", res); + return res; +} + +/*------------------------------------------------------------------------*/ + +class ClauseCounter : public ClauseIterator { +public: + int vars; + int64_t clauses; + ClauseCounter () : vars (0), clauses (0) {} + bool clause (const vector<int> &c) { + for (const auto &lit : c) { + CADICAL_assert (lit != INT_MIN); + int idx = abs (lit); + if (idx > vars) + vars = idx; + } + clauses++; + return true; + } +}; + +class ClauseWriter : public ClauseIterator { + File *file; + +public: + ClauseWriter (File *f) : file (f) {} + bool clause (const vector<int> &c) { + for (const auto &lit : c) { + if (!file->put (lit)) + return false; + if (!file->put (' ')) + return false; + } + return file->put ("0\n"); + } +}; + +const char *Solver::write_dimacs (const char *path, int min_max_var) { + LOG_API_CALL_BEGIN ("write_dimacs", path, min_max_var); + REQUIRE_VALID_STATE (); +#ifndef CADICAL_QUIET + const double start = internal->time (); +#endif + internal->restore_clauses (); + ClauseCounter counter; + (void) traverse_clauses (counter); + LOG ("found maximal variable %d and %" PRId64 " clauses", counter.vars, + counter.clauses); + File *file = File::write (internal, path); + const char *res = 0; + if (file) { + int actual_max_vars = max (min_max_var, counter.vars); + MSG ("writing %s'p cnf %d %" PRId64 "'%s header", tout.green_code (), + actual_max_vars, counter.clauses, tout.normal_code ()); + file->put ("p cnf "); + file->put (actual_max_vars); + file->put (' '); + file->put (counter.clauses); + file->put ('\n'); + ClauseWriter writer (file); + if (!traverse_clauses (writer)) + res = internal->error_message.init ( + "writing to DIMACS file '%s' failed", path); + delete file; + } else + res = internal->error_message.init ( + "failed to open DIMACS file '%s' for writing", path); +#ifndef CADICAL_QUIET + if (!res) { + const double end = internal->time (); + MSG ("wrote %" PRId64 " clauses in %.2f seconds %s time", + counter.clauses, end - start, + internal->opts.realtime ? "real" : "process"); + } +#endif + LOG_API_CALL_RETURNS ("write_dimacs", path, min_max_var, res); + return res; +} + +/*------------------------------------------------------------------------*/ + +struct WitnessWriter : public WitnessIterator { + File *file; + int64_t witnesses; + WitnessWriter (File *f) : file (f), witnesses (0) {} + bool write (const vector<int> &a) { + for (const auto &lit : a) { + if (!file->put (lit)) + return false; + if (!file->put (' ')) + return false; + } + return file->put ('0'); + } + bool witness (const vector<int> &c, const vector<int> &w, int64_t) { + if (!write (c)) + return false; + if (!file->put (' ')) + return false; + if (!write (w)) + return false; + if (!file->put ('\n')) + return false; + witnesses++; + return true; + } +}; + +const char *Solver::write_extension (const char *path) { + LOG_API_CALL_BEGIN ("write_extension", path); + REQUIRE_VALID_STATE (); + const char *res = 0; +#ifndef CADICAL_QUIET + const double start = internal->time (); +#endif + File *file = File::write (internal, path); + WitnessWriter writer (file); + if (file) { + if (!traverse_witnesses_backward (writer)) + res = internal->error_message.init ( + "writing to DIMACS file '%s' failed", path); + delete file; + } else + res = internal->error_message.init ( + "failed to open extension file '%s' for writing", path); +#ifndef CADICAL_QUIET + if (!res) { + const double end = internal->time (); + MSG ("wrote %" PRId64 " witnesses in %.2f seconds %s time", + writer.witnesses, end - start, + internal->opts.realtime ? "real" : "process"); + } +#endif + LOG_API_CALL_RETURNS ("write_extension", path, res); + return res; +} + +/*------------------------------------------------------------------------*/ + +struct ClauseCopier : public ClauseIterator { + Solver &dst; + +public: + ClauseCopier (Solver &d) : dst (d) {} + bool clause (const vector<int> &c) { + for (const auto &lit : c) + dst.add (lit); + dst.add (0); + return true; + } +}; + +struct WitnessCopier : public WitnessIterator { + External *dst; + +public: + WitnessCopier (External *d) : dst (d) {} + bool witness (const vector<int> &c, const vector<int> &w, int64_t id) { + dst->push_external_clause_and_witness_on_extension_stack (c, w, id); + return true; + } +}; + +void Solver::copy (Solver &other) const { + REQUIRE_READY_STATE (); + REQUIRE (other.state () & CONFIGURING, "target solver already modified"); + internal->opts.copy (other.internal->opts); + ClauseCopier clause_copier (other); + traverse_clauses (clause_copier); + WitnessCopier witness_copier (other.external); + traverse_witnesses_forward (witness_copier); + external->copy_flags (*other.external); +} + +/*------------------------------------------------------------------------*/ + +void Solver::section (const char *title) { + if (state () == DELETING) + return; +#ifdef CADICAL_QUIET + (void) title; +#endif + REQUIRE_INITIALIZED (); + SECTION (title); +} + +void Solver::message (const char *fmt, ...) { + if (state () == DELETING) + return; +#ifdef CADICAL_QUIET + (void) fmt; +#else + REQUIRE_INITIALIZED (); + va_list ap; + va_start (ap, fmt); + internal->vmessage (fmt, ap); + va_end (ap); +#endif +} + +void Solver::message () { + if (state () == DELETING) + return; + REQUIRE_INITIALIZED (); +#ifndef CADICAL_QUIET + internal->message (); +#endif +} + +void Solver::verbose (int level, const char *fmt, ...) { + if (state () == DELETING) + return; + REQUIRE_VALID_OR_SOLVING_STATE (); +#ifdef CADICAL_QUIET + (void) level; + (void) fmt; +#else + va_list ap; + va_start (ap, fmt); + internal->vverbose (level, fmt, ap); + va_end (ap); +#endif +} + +void Solver::error (const char *fmt, ...) { + if (state () == DELETING) + return; + REQUIRE_INITIALIZED (); + va_list ap; + va_start (ap, fmt); + internal->verror (fmt, ap); + va_end (ap); +} + +} // namespace CaDiCaL + +ABC_NAMESPACE_IMPL_END diff --git a/src/sat/cadical/cadical_stable.cpp b/src/sat/cadical/cadical_stable.cpp new file mode 100644 index 0000000000..f177139ac0 --- /dev/null +++ b/src/sat/cadical/cadical_stable.cpp @@ -0,0 +1,39 @@ +#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 diff --git a/src/sat/cadical/cadical_stats.cpp b/src/sat/cadical/cadical_stats.cpp new file mode 100644 index 0000000000..8b07745d68 --- /dev/null +++ b/src/sat/cadical/cadical_stats.cpp @@ -0,0 +1,826 @@ +// 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 diff --git a/src/sat/cadical/cadical_subsume.cpp b/src/sat/cadical/cadical_subsume.cpp new file mode 100644 index 0000000000..b75040ca8b --- /dev/null +++ b/src/sat/cadical/cadical_subsume.cpp @@ -0,0 +1,652 @@ +#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 diff --git a/src/sat/cadical/cadical_sweep.cpp b/src/sat/cadical/cadical_sweep.cpp new file mode 100644 index 0000000000..4dca2b4bc6 --- /dev/null +++ b/src/sat/cadical/cadical_sweep.cpp @@ -0,0 +1,1959 @@ +#include "global.h" + +#include "internal.hpp" + +ABC_NAMESPACE_IMPL_START + +namespace CaDiCaL { + +Sweeper::Sweeper (Internal *i) + : internal (i), random (internal->opts.seed) { + random += internal->stats.sweep; // different seed every time + internal->init_sweeper (*this); +} + +Sweeper::~Sweeper () { + // this is already called actively + // internal->release_sweeper (this); + return; +} + +#define INVALID64 INT64_MAX +#define INVALID UINT_MAX + +int Internal::sweep_solve () { + START (sweepsolve); + cadical_kitten_randomize_phases (citten); + stats.sweep_solved++; + int res = cadical_kitten_solve (citten); + if (res == 10) + stats.sweep_sat++; + if (res == 20) + stats.sweep_unsat++; + STOP (sweepsolve); + return res; +} + +bool Internal::sweep_flip (int lit) { + START (sweepflip); + bool res = cadical_kitten_flip_signed_literal (citten, lit); + STOP (sweepflip); + return res; +} + +int Internal::sweep_flip_and_implicant (int lit) { + START (sweepimplicant); + int res = cadical_kitten_flip_and_implicant_for_signed_literal (citten, lit); + STOP (sweepimplicant); + return res; +} + +void Internal::sweep_set_cadical_kitten_ticks_limit (Sweeper &sweeper) { + uint64_t remaining = 0; + const uint64_t current = sweeper.current_ticks; + if (current < sweeper.limit.ticks) + remaining = sweeper.limit.ticks - current; + LOG ("'cadical_kitten_ticks' remaining %" PRIu64, remaining); + cadical_kitten_set_ticks_limit (citten, remaining); +} + +void Internal::sweep_update_noccs (Clause *c) { + if (c->redundant) + return; + for (const auto &lit : *c) { + noccs (lit)--; + } +} + +bool Internal::can_sweep_clause (Clause *c) { + if (c->garbage) + return false; + if (!c->redundant) + return true; + return c->size == 2; // && !c->hyper; // could ignore hyper +} + +// essentially do full occurence list as in elim.cpp +void Internal::sweep_dense_mode_and_watch_irredundant () { + reset_watches (); + + init_noccs (); + + // mark satisfied irredundant clauses as garbage + for (const auto &c : clauses) { + if (!can_sweep_clause (c)) + continue; + bool satisfied = false; + for (const auto &lit : *c) { + const signed char tmp = val (lit); + if (tmp <= 0) + continue; + if (tmp > 0) { + satisfied = true; + break; + } + } + if (satisfied) + mark_garbage (c); // forces more precise counts + else { + for (const auto &lit : *c) + noccs (lit)++; + } + } + + init_occs (); + + // Connect irredundant clauses. + // + for (const auto &c : clauses) { + if (!c->garbage) { + for (const auto &lit : *c) + if (active (lit)) + occs (lit).push_back (c); + } else if (c->size == 2) { + if (!c->flushed) { + if (proof) { + c->flushed = true; + proof->delete_clause (c); + } + } + } + } +} + +// go back to two watch scheme +void Internal::sweep_sparse_mode () { + reset_occs (); + reset_noccs (); + init_watches (); + connect_watches (); +} + +// propagate without watches but full occurence list +void Internal::sweep_dense_propagate (Sweeper &sweeper) { + vector<int> &work = sweeper.propagate; + size_t i = 0; + uint64_t &ticks = sweeper.current_ticks; + while (i < work.size ()) { + int lit = work[i++]; + LOG ("sweeping propagation of %d", lit); + CADICAL_assert (val (lit) > 0); + ticks += 1 + cache_lines (occs (-lit).size (), sizeof (Clause *)); + const Occs &ns = occs (-lit); + for (const auto &c : ns) { + ticks++; + if (!can_sweep_clause (c)) + continue; + int unit = 0, satisfied = 0; + for (const auto &other : *c) { + const signed char tmp = val (other); + if (tmp < 0) + continue; + if (tmp > 0) { + satisfied = other; + break; + } + if (unit) + unit = INT_MIN; + else + unit = other; + } + if (satisfied) { + LOG (c, "sweeping propagation of %d finds %d satisfied", lit, + satisfied); + mark_garbage (c); + sweep_update_noccs (c); + } else if (!unit) { + LOG ("empty clause during sweeping propagation of %d", lit); + // need to set conflict = c for lrat + conflict = c; + learn_empty_clause (); + conflict = 0; + break; + } else if (unit != INT_MIN) { + LOG ("new unit %d during sweeping propagation of %d", unit, lit); + build_chain_for_units (unit, c, 0); + assign_unit (unit); + work.push_back (unit); + ticks++; + } + } + if (unsat) + break; + + // not necessary but should help + ticks += 1 + cache_lines (occs (lit).size (), sizeof (Clause *)); + const Occs &ps = occs (lit); + for (const auto &c : ps) { + ticks++; + if (c->garbage) + continue; + // if (c->redundant) // TODO I assume it does not hurt to mark + // everything here continue; + LOG (c, "sweeping propagation of %d produces satisfied", lit); + mark_garbage (c); + sweep_update_noccs (c); + } + } + work.clear (); +} + +bool Internal::cadical_kitten_ticks_limit_hit (Sweeper &sweeper, const char *when) { + const uint64_t current = + cadical_kitten_current_ticks (citten) + sweeper.current_ticks; + if (current >= sweeper.limit.ticks) { + LOG ("'cadical_kitten_ticks' limit of %" PRIu64 " ticks hit after %" PRIu64 + " ticks during %s", + sweeper.limit.ticks, current, when); + return true; + } +#ifndef LOGGING + (void) when; +#endif + return false; +} + +void Internal::init_sweeper (Sweeper &sweeper) { + sweeper.encoded = 0; + enlarge_zero (sweeper.depths, max_var + 1); + sweeper.reprs = new int[2 * max_var + 1]; + sweeper.reprs += max_var; + enlarge_zero (sweeper.prev, max_var + 1); + enlarge_zero (sweeper.next, max_var + 1); + for (const auto &lit : lits) + sweeper.reprs[lit] = lit; + sweeper.first = sweeper.last = 0; + sweeper.current_ticks = + 2 * + clauses + .size (); // initialize with the cost of building full occ list. + sweeper.current_ticks += + 2 + 2 * cache_lines (clauses.size (), sizeof (Clause *)); + CADICAL_assert (!citten); + citten = cadical_kitten_init (); + citten_clear_track_log_terminate (); + + sweep_dense_mode_and_watch_irredundant (); // full occurence list + + if (lrat) { + sweeper.prev_units.push_back (0); + for (const auto &idx : vars) { + sweeper.prev_units.push_back (val (idx) != 0); + } + } + + unsigned completed = stats.sweep_completed; + const unsigned max_completed = 32; + if (completed > max_completed) + completed = max_completed; + + uint64_t vars_limit = opts.sweepvars; + vars_limit <<= completed; + const unsigned max_vars_limit = opts.sweepmaxvars; + if (vars_limit > max_vars_limit) + vars_limit = max_vars_limit; + sweeper.limit.vars = vars_limit; + VERBOSE (3, "sweeper variable limit %u", sweeper.limit.vars); + + uint64_t depth_limit = stats.sweep_completed; + depth_limit += opts.sweepdepth; + const unsigned max_depth = opts.sweepmaxdepth; + if (depth_limit > max_depth) + depth_limit = max_depth; + sweeper.limit.depth = depth_limit; + VERBOSE (3, "sweeper depth limit %u", sweeper.limit.depth); + + uint64_t clause_limit = opts.sweepclauses; + clause_limit <<= completed; + const unsigned max_clause_limit = opts.sweepmaxclauses; + if (clause_limit > max_clause_limit) + clause_limit = max_clause_limit; + sweeper.limit.clauses = clause_limit; + VERBOSE (3, "sweeper clause limit %u", sweeper.limit.clauses); +} + +void Internal::release_sweeper (Sweeper &sweeper) { + + sweeper.reprs -= max_var; + delete[] sweeper.reprs; + + erase_vector (sweeper.depths); + erase_vector (sweeper.prev); + erase_vector (sweeper.next); + erase_vector (sweeper.vars); + erase_vector (sweeper.clause); + erase_vector (sweeper.backbone); + erase_vector (sweeper.partition); + erase_vector (sweeper.prev_units); + for (unsigned i = 0; i < 2; i++) + erase_vector (sweeper.core[i]); + + cadical_kitten_release (citten); + citten = 0; + stats.ticks.sweep += sweeper.current_ticks; + sweep_sparse_mode (); + return; +} + +void Internal::clear_sweeper (Sweeper &sweeper) { + LOG ("clearing sweeping environment"); + sweeper.current_ticks += cadical_kitten_current_ticks (citten); + + citten_clear_track_log_terminate (); + for (auto &idx : sweeper.vars) { + CADICAL_assert (sweeper.depths[idx]); + sweeper.depths[idx] = 0; + } + sweeper.vars.clear (); + for (auto c : sweeper.clauses) { + CADICAL_assert (c->swept); + c->swept = false; + } + sweeper.clauses.clear (); + sweeper.backbone.clear (); + sweeper.partition.clear (); + sweeper.encoded = 0; + sweep_set_cadical_kitten_ticks_limit (sweeper); +} + +int Internal::sweep_repr (Sweeper &sweeper, int lit) { + int res; + { + int prev = lit; + while ((res = sweeper.reprs[prev]) != prev) + prev = res; + } + if (res == lit) + return res; + LOG ("sweeping repr[%d] = %d", lit, res); + { + const int not_res = -res; + int next, prev = lit; + while ((next = sweeper.reprs[prev]) != res) { + const int not_prev = -prev; + sweeper.reprs[not_prev] = not_res; + sweeper.reprs[prev] = res; + prev = next; + } + CADICAL_assert (sweeper.reprs[-prev] == not_res); + } + return res; +} + +void Internal::add_literal_to_environment (Sweeper &sweeper, unsigned depth, + int lit) { + const int repr = sweep_repr (sweeper, lit); + if (repr != lit) + return; + const int idx = abs (lit); + if (sweeper.depths[idx]) + return; + CADICAL_assert (depth < UINT_MAX); + sweeper.depths[idx] = depth + 1; + CADICAL_assert (idx); + sweeper.vars.push_back (idx); + LOG ("sweeping[%u] adding literal %d", depth, lit); +} + +void Internal::sweep_add_clause (Sweeper &sweeper, unsigned depth) { + // TODO: CADICAL_assertion fails, check if this an issue or can be avoided + // CADICAL_assert (sweeper.clause.size () > 1); + for (const auto &lit : sweeper.clause) + add_literal_to_environment (sweeper, depth, lit); + citten_clause_with_id (citten, sweeper.clauses.size (), + sweeper.clause.size (), sweeper.clause.data ()); + sweeper.clause.clear (); + if (opts.sweepcountbinary || sweeper.clause.size () > 2) + sweeper.encoded++; +} + +void Internal::sweep_clause (Sweeper &sweeper, unsigned depth, Clause *c) { + if (c->swept) + return; + CADICAL_assert (can_sweep_clause (c)); + LOG (c, "sweeping[%u]", depth); + CADICAL_assert (sweeper.clause.empty ()); + for (const auto &lit : *c) { + const signed char tmp = val (lit); + if (tmp > 0) { + mark_garbage (c); + sweep_update_noccs (c); + sweeper.clause.clear (); + return; + } + if (tmp < 0) { + if (lrat) + sweeper.prev_units[abs (lit)] = true; + continue; + } + sweeper.clause.push_back (lit); + } + c->swept = true; + sweep_add_clause (sweeper, depth); + sweeper.clauses.push_back (c); +} + +extern "C" { +static void save_core_clause (void *state, unsigned id, bool learned, + size_t size, const unsigned *lits) { + Sweeper *sweeper = (Sweeper *) state; + Internal *internal = sweeper->internal; + if (internal->unsat) + return; + vector<sweep_proof_clause> &core = sweeper->core[sweeper->save]; + sweep_proof_clause pc; + if (learned) { + pc.sweep_id = INVALID; // necessary + pc.cad_id = INVALID64; // delay giving ids + } else { + pc.sweep_id = id; // necessary + CADICAL_assert (id < sweeper->clauses.size ()); + pc.cad_id = sweeper->clauses[id]->id; + } + pc.kit_id = 0; + pc.learned = learned; + const unsigned *end = lits + size; + for (const unsigned *p = lits; p != end; p++) { + pc.literals.push_back (internal->citten2lit (*p)); // conversion + } +#ifdef LOGGING + LOG (pc.literals, "traced %s", + pc.learned == true ? "learned" : "original"); +#endif + core.push_back (pc); +} + +static void save_core_clause_with_lrat (void *state, unsigned cid, + unsigned id, bool learned, + size_t size, const unsigned *lits, + size_t chain_size, + const unsigned *chain) { + Sweeper *sweeper = (Sweeper *) state; + Internal *internal = sweeper->internal; + if (internal->unsat) + return; + vector<sweep_proof_clause> &core = sweeper->core[sweeper->save]; + vector<Clause *> &clauses = sweeper->clauses; + sweep_proof_clause pc; + pc.kit_id = cid; + pc.learned = learned; + pc.sweep_id = INVALID; + pc.cad_id = INVALID64; + if (!learned) { + CADICAL_assert (size); + CADICAL_assert (!chain_size); + CADICAL_assert (id < clauses.size ()); + pc.sweep_id = id; + CADICAL_assert (id < clauses.size ()); + pc.cad_id = clauses[id]->id; + for (const auto &lit : *clauses[id]) { + pc.literals.push_back (lit); + } + } else { + CADICAL_assert (chain_size); + pc.sweep_id = INVALID; + pc.cad_id = INVALID64; // delay giving ids + 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--) { + pc.chain.push_back (*(p - 1)); + } + } +#ifdef LOGGING + if (learned) + LOG (pc.literals, "traced %s", + pc.learned == true ? "learned" : "original"); + else { + CADICAL_assert (id < clauses.size ()); + LOG (clauses[id], "traced"); + } +#endif + core.push_back (pc); +} + +static int citten_terminate (void *data) { + return ((Internal *) data)->terminated_asynchronously (); +} + +} // end extern C + +void Internal::citten_clear_track_log_terminate () { + CADICAL_assert (citten); + cadical_kitten_clear (citten); + cadical_kitten_track_antecedents (citten); + if (external->terminator) + cadical_kitten_set_terminator (citten, internal, citten_terminate); +#ifdef LOGGING + if (opts.log) + cadical_kitten_set_logging (citten); +#endif +} + +void Internal::add_core (Sweeper &sweeper, unsigned core_idx) { + if (unsat) + return; + LOG ("check and add extracted core[%u] lemmas to proof", core_idx); + CADICAL_assert (core_idx == 0 || core_idx == 1); + vector<sweep_proof_clause> &core = sweeper.core[core_idx]; + + CADICAL_assert (!lrat || proof); + + unsigned unsat_size = 0; + for (auto &pc : core) { + unsat_size++; + + if (!pc.learned) { + LOG (pc.literals, + "not adding already present core[%u] cadical_kitten[%u] clause", + core_idx, pc.kit_id); + continue; + } + + LOG (pc.literals, "adding extracted core[%u] cadical_kitten[%u] lemma", + core_idx, pc.kit_id); + + const unsigned new_size = pc.literals.size (); + + if (lrat) { + CADICAL_assert (pc.cad_id == INVALID64); + for (auto &cid : pc.chain) { + int64_t id = 0; + for (const auto &cpc : core) { + if (cpc.kit_id == cid) { + if (cpc.learned) + id = cpc.cad_id; + else { + id = cpc.cad_id; + CADICAL_assert (cpc.cad_id == sweeper.clauses[cpc.sweep_id]->id); + CADICAL_assert (!sweeper.clauses[cpc.sweep_id]->garbage); + // avoid duplicate ids of units with seen flags + for (const auto &lit : cpc.literals) { + if (val (lit) >= 0) + continue; + if (flags (lit).seen) + continue; + const int idx = abs (lit); + if (sweeper.prev_units[idx]) { + int64_t uid = unit_id (-lit); + lrat_chain.push_back (uid); + analyzed.push_back (lit); + flags (lit).seen = true; + } + } + } + break; + } + } + CADICAL_assert (id); + if (id != INVALID64) + lrat_chain.push_back (id); + } + clear_analyzed_literals (); + } + + if (!new_size) { + LOG ("sweeping produced empty clause"); + learn_empty_clause (); + core.resize (unsat_size); + return; + } + + if (new_size == 1) { + int unit = pc.literals[0]; + if (val (unit) > 0) { + LOG ("already assigned sweeping unit %d", unit); + lrat_chain.clear (); + } else if (val (unit) < 0) { + LOG ("falsified sweeping unit %d leads to empty clause", unit); + if (lrat) { + int64_t id = unit_id (-unit); + lrat_chain.push_back (id); + } + learn_empty_clause (); + core.resize (unsat_size); + return; + } else { + LOG ("sweeping produced unit %d", unit); + assign_unit (unit); + stats.sweep_units++; + sweeper.propagate.push_back (unit); + } + if (proof && lrat) + pc.cad_id = unit_id (unit); + continue; + } + + CADICAL_assert (new_size > 1); + CADICAL_assert (pc.learned); + + if (proof) { + pc.cad_id = ++clause_id; + proof->add_derived_clause (pc.cad_id, true, pc.literals, lrat_chain); + lrat_chain.clear (); + } + } +} + +void Internal::save_core (Sweeper &sweeper, unsigned core) { + LOG ("saving extracted core[%u] lemmas", core); + CADICAL_assert (core == 0 || core == 1); + CADICAL_assert (sweeper.core[core].empty ()); + sweeper.save = core; + cadical_kitten_compute_clausal_core (citten, 0); + if (lrat) + cadical_kitten_trace_core (citten, &sweeper, save_core_clause_with_lrat); + else + cadical_kitten_traverse_core_clauses_with_id (citten, &sweeper, + save_core_clause); +} + +void Internal::clear_core (Sweeper &sweeper, unsigned core_idx) { + CADICAL_assert (core_idx == 0 || core_idx == 1); + LOG ("clearing core[%u] lemmas", core_idx); + vector<sweep_proof_clause> &core = sweeper.core[core_idx]; + if (proof) { + LOG ("deleting sub-solver core clauses"); + for (auto &pc : core) { + if (pc.learned && pc.literals.size () > 1) + proof->delete_clause (pc.cad_id, true, pc.literals); + } + } + core.clear (); +} + +void Internal::save_add_clear_core (Sweeper &sweeper) { + save_core (sweeper, 0); + add_core (sweeper, 0); + clear_core (sweeper, 0); +} + +void Internal::init_backbone_and_partition (Sweeper &sweeper) { + LOG ("initializing backbone and equivalent literals candidates"); + sweeper.backbone.clear (); + sweeper.partition.clear (); + for (const auto &idx : sweeper.vars) { + if (!active (idx)) + continue; + CADICAL_assert (idx > 0); + const int lit = idx; + const int not_lit = -lit; + const signed char tmp = cadical_kitten_signed_value (citten, lit); + const int candidate = (tmp < 0) ? not_lit : lit; + LOG ("sweeping candidate %d", candidate); + sweeper.backbone.push_back (candidate); + sweeper.partition.push_back (candidate); + } + sweeper.partition.push_back (0); + + LOG (sweeper.backbone, "initialized backbone candidates"); + LOG (sweeper.partition, "initialized equivalence candidates"); +} + +void Internal::sweep_empty_clause (Sweeper &sweeper) { + CADICAL_assert (!unsat); + save_add_clear_core (sweeper); + CADICAL_assert (unsat); +} + +void Internal::sweep_refine_partition (Sweeper &sweeper) { + LOG ("refining partition"); + vector<int> &old_partition = sweeper.partition; + vector<int> new_partition; + auto old_begin = old_partition.begin (); + const auto old_end = old_partition.end (); +#ifdef LOGGING + unsigned old_classes = 0; + unsigned new_classes = 0; +#endif + for (auto p = old_begin, q = p; p != old_end; p = q + 1) { + unsigned assigned_true = 0; + int other; + for (q = p; (other = *q) != 0; q++) { + if (sweep_repr (sweeper, other) != other) + continue; + if (val (other)) + continue; + signed char value = cadical_kitten_signed_value (citten, other); + if (!value) + LOG ("dropping sub-solver unassigned %d", other); + else if (value > 0) { + new_partition.push_back (other); + assigned_true++; + } + } +#ifdef LOGGING + LOG ("refining class %u of size %zu", old_classes, (size_t) (q - p)); + old_classes++; +#endif + if (assigned_true == 0) + LOG ("no positive literal in class"); + else if (assigned_true == 1) { +#ifdef LOGGING + other = +#else + (void) +#endif + new_partition.back (); + new_partition.pop_back (); + LOG ("dropping singleton class %d", other); + } else { + LOG ("%u positive literal in class", assigned_true); + new_partition.push_back (0); +#ifdef LOGGING + new_classes++; +#endif + } + + unsigned assigned_false = 0; + for (q = p; (other = *q) != 0; q++) { + if (sweep_repr (sweeper, other) != other) + continue; + if (val (other)) + continue; + signed char value = cadical_kitten_signed_value (citten, other); + if (value < 0) { + new_partition.push_back (other); + assigned_false++; + } + } + + if (assigned_false == 0) + LOG ("no negative literal in class"); + else if (assigned_false == 1) { +#ifdef LOGGING + other = +#else + (void) +#endif + new_partition.back (); + new_partition.pop_back (); + LOG ("dropping singleton class %d", other); + } else { + LOG ("%u negative literal in class", assigned_false); + new_partition.push_back (0); +#ifdef LOGGING + new_classes++; +#endif + } + } + old_partition.swap (new_partition); + LOG ("refined %u classes into %u", old_classes, new_classes); +} + +void Internal::sweep_refine_backbone (Sweeper &sweeper) { + LOG ("refining backbone candidates"); + const auto end = sweeper.backbone.end (); + auto q = sweeper.backbone.begin (); + for (auto p = q; p != end; p++) { + const int lit = *p; + if (val (lit)) + continue; + signed char value = cadical_kitten_signed_value (citten, lit); + if (!value) + LOG ("dropping sub-solver unassigned %d", lit); + else if (value > 0) + *q++ = lit; + } + sweeper.backbone.resize (q - sweeper.backbone.begin ()); +} + +void Internal::sweep_refine (Sweeper &sweeper) { + CADICAL_assert (cadical_kitten_status (citten) == 10); + if (sweeper.backbone.empty ()) + LOG ("no need to refine empty backbone candidates"); + else + sweep_refine_backbone (sweeper); + if (sweeper.partition.empty ()) + LOG ("no need to refine empty partition candidates"); + else + sweep_refine_partition (sweeper); +} + +void Internal::flip_backbone_literals (Sweeper &sweeper) { + const unsigned max_rounds = opts.sweepfliprounds; + if (!max_rounds) + return; + CADICAL_assert (sweeper.backbone.size ()); + if (cadical_kitten_status (citten) != 10) + return; +#ifdef LOGGING + unsigned total_flipped = 0; +#endif + unsigned flipped, round = 0; + do { + round++; + flipped = 0; + bool refine = false; + auto begin = sweeper.backbone.begin (), q = begin, p = q; + const auto end = sweeper.backbone.end (); + bool limit_hit = false; + while (p != end) { + const int lit = *p++; + stats.sweep_flip_backbone++; + if (limit_hit || terminated_asynchronously ()) { + break; + } else if (sweep_flip (lit)) { + LOG ("flipping backbone candidate %d succeeded", lit); +#ifdef LOGGING + total_flipped++; +#endif + stats.sweep_flipped_backbone++; + flipped++; + } else { + LOG ("flipping backbone candidate %d failed", lit); + *q++ = lit; + } + } + while (p != end) + *q++ = *p++; + sweeper.backbone.resize (q - sweeper.backbone.begin ()); + LOG ("flipped %u backbone candidates in round %u", flipped, round); + + if (limit_hit) + break; + if (terminated_asynchronously ()) + break; + if (cadical_kitten_ticks_limit_hit (sweeper, "backbone flipping")) + break; + if (refine) + sweep_refine (sweeper); + } while (flipped && round < max_rounds); + LOG ("flipped %u backbone candidates in total in %u rounds", + total_flipped, round); +} + +bool Internal::sweep_extract_fixed (Sweeper &sweeper, int lit) { + const int not_lit = -lit; + stats.sweep_solved_backbone++; + cadical_kitten_assume_signed (citten, not_lit); + int res = sweep_solve (); + if (!res) { + stats.sweep_unknown_backbone++; + return false; + } + CADICAL_assert (res == 20); + LOG ("sweep unit %d", lit); + save_add_clear_core (sweeper); + stats.sweep_unsat_backbone++; + return true; +} + +bool Internal::sweep_backbone_candidate (Sweeper &sweeper, int lit) { + LOG ("trying backbone candidate %d", lit); + signed char value = cadical_kitten_fixed_signed (citten, lit); + if (value) { + stats.sweep_fixed_backbone++; + CADICAL_assert (value > 0); + if (val (lit) <= 0) { + return sweep_extract_fixed (sweeper, lit); + } else + LOG ("literal %d already fixed", lit); + return false; + } + + int res = cadical_kitten_status (citten); + if (res != 10) { + LOG ("not flipping due to status %d != 10", res); + } + stats.sweep_flip_backbone++; + if (res == 10 && sweep_flip (lit)) { + stats.sweep_flipped_backbone++; + LOG ("flipping %d succeeded", lit); + // LOGBACKBONE ("refined backbone candidates"); + return false; + } + + LOG ("flipping %d failed", lit); + const int not_lit = -lit; + stats.sweep_solved_backbone++; + cadical_kitten_assume_signed (citten, not_lit); + res = sweep_solve (); + if (res == 10) { + LOG ("sweeping backbone candidate %d failed", lit); + sweep_refine (sweeper); + stats.sweep_sat_backbone++; + return false; + } + + if (res == 20) { + LOG ("sweep unit %d", lit); + save_add_clear_core (sweeper); + CADICAL_assert (val (lit)); + stats.sweep_unsat_backbone++; + return true; + } + + stats.sweep_unknown_backbone++; + + LOG ("sweeping backbone candidate %d failed", lit); + return false; +} + +// at this point the binary (lit or other) is already present +// in the proof via 'add_core'. +// We just copy it as an irredundant clause, call weaken minus +// and push it on the extension stack. +// +int64_t Internal::add_sweep_binary (sweep_proof_clause pc, int lit, + int other) { + CADICAL_assert (!unsat); + if (unsat) + return 0; // sanity check, should be fuzzed + + CADICAL_assert (!val (lit) && !val (other)); + if (val (lit) || val (other)) + return 0; + + if (lrat) { + for (const auto &plit : pc.literals) { + if (val (plit)) { + int64_t id = unit_id (-plit); + lrat_chain.push_back (id); + } + } + lrat_chain.push_back (pc.cad_id); + } + clause.push_back (lit); + clause.push_back (other); + const int64_t id = ++clause_id; + if (proof) { + proof->add_derived_clause (id, false, clause, lrat_chain); + proof->weaken_minus (id, clause); + } + external->push_binary_clause_on_extension_stack (id, lit, other); + clause.clear (); + lrat_chain.clear (); + return id; +} + +void Internal::delete_sweep_binary (const sweep_binary &sb) { + if (unsat) + return; + if (!proof) + return; + vector<int> bin; + bin.push_back (sb.lit); + bin.push_back (sb.other); + proof->delete_clause (sb.id, false, bin); +} + +bool Internal::scheduled_variable (Sweeper &sweeper, int idx) { + return sweeper.prev[idx] != 0 || sweeper.first == idx; +} + +void Internal::schedule_inner (Sweeper &sweeper, int idx) { + CADICAL_assert (idx); + if (!active (idx)) + return; + const int next = sweeper.next[idx]; + if (next != 0) { + LOG ("rescheduling inner %d as last", idx); + const unsigned prev = sweeper.prev[idx]; + CADICAL_assert (sweeper.prev[next] == idx); + sweeper.prev[next] = prev; + if (prev == 0) { + CADICAL_assert (sweeper.first == idx); + sweeper.first = next; + } else { + CADICAL_assert (sweeper.next[prev] == idx); + sweeper.next[prev] = next; + } + const unsigned last = sweeper.last; + if (last == 0) { + CADICAL_assert (sweeper.first == 0); + sweeper.first = idx; + } else { + CADICAL_assert (sweeper.next[last] == 0); + sweeper.next[last] = idx; + } + sweeper.prev[idx] = last; + sweeper.next[idx] = 0; + sweeper.last = idx; + } else if (sweeper.last != idx) { + LOG ("scheduling inner %d as last", idx); + const unsigned last = sweeper.last; + if (last == 0) { + CADICAL_assert (sweeper.first == 0); + sweeper.first = idx; + } else { + CADICAL_assert (sweeper.next[last] == 0); + sweeper.next[last] = idx; + } + CADICAL_assert (sweeper.next[idx] == 0); + sweeper.prev[idx] = last; + sweeper.last = idx; + } else + LOG ("keeping inner %d scheduled as last", idx); +} + +void Internal::schedule_outer (Sweeper &sweeper, int idx) { + CADICAL_assert (!scheduled_variable (sweeper, idx)); + CADICAL_assert (active (idx)); + const int first = sweeper.first; + if (first == 0) { + CADICAL_assert (sweeper.last == 0); + sweeper.last = idx; + } else { + CADICAL_assert (sweeper.prev[first] == 0); + sweeper.prev[first] = idx; + } + CADICAL_assert (sweeper.prev[idx] == 0); + sweeper.next[idx] = first; + sweeper.first = idx; + LOG ("scheduling outer %d as first", idx); +} + +int Internal::next_scheduled (Sweeper &sweeper) { + int res = sweeper.last; + if (res == 0) { + LOG ("no more scheduled variables left"); + return 0; + } + CADICAL_assert (res > 0); + LOG ("dequeuing next scheduled %d", res); + const unsigned prev = sweeper.prev[res]; + CADICAL_assert (sweeper.next[res] == 0); + sweeper.prev[res] = 0; + if (prev == 0) { + CADICAL_assert (sweeper.first == res); + sweeper.first = 0; + } else { + CADICAL_assert (sweeper.next[prev] == res); + sweeper.next[prev] = 0; + } + sweeper.last = prev; + return res; +} + +void Internal::sweep_substitute_lrat (Clause *c, int64_t id) { + if (!lrat) + return; + for (const auto &lit : *c) { + CADICAL_assert (val (lit) <= 0); + if (val (lit) < 0) { + int64_t id = unit_id (-lit); + lrat_chain.push_back (id); + } + } + lrat_chain.push_back (id); + lrat_chain.push_back (c->id); +} + +#define all_scheduled(IDX) \ + int IDX = sweeper.first, NEXT_##IDX; \ + IDX != 0 && (NEXT_##IDX = sweeper.next[IDX], true); \ + IDX = NEXT_##IDX + +// Substitute equivalences in clauses (see +// 'sweep_substitute_new_equivalences' for explanation) +void Internal::substitute_connected_clauses (Sweeper &sweeper, int lit, + int repr, int64_t id) { + if (unsat) + return; + if (val (lit)) + return; + if (val (repr)) + return; + LOG ("substituting %d with %d in all irredundant clauses", lit, repr); + + CADICAL_assert (lit != repr); + CADICAL_assert (lit != -repr); + + CADICAL_assert (active (lit)); + CADICAL_assert (active (repr)); + + uint64_t &ticks = sweeper.current_ticks; + + { + ticks += 1 + cache_lines (occs (lit).size (), sizeof (Clause *)); + Occs &ns = occs (lit); + auto const begin = ns.begin (); + const auto end = ns.end (); + auto q = begin; + auto p = q; + while (p != end) { + Clause *c = *q++ = *p++; + ticks++; + if (c->garbage) + continue; + CADICAL_assert (clause.empty ()); + bool satisfied = false; + bool repr_already_watched = false; + const int not_repr = -repr; +#ifndef CADICAL_NDEBUG + bool found = false; +#endif + for (const auto &other : *c) { + if (other == lit) { +#ifndef CADICAL_NDEBUG + CADICAL_assert (!found); + found = true; +#endif + clause.push_back (repr); + continue; + } + CADICAL_assert (other != -lit); + if (other == repr) { + CADICAL_assert (!repr_already_watched); + repr_already_watched = true; + continue; + } + if (other == not_repr) { + satisfied = true; + break; + } + const signed char tmp = val (other); + if (tmp < 0) + continue; + if (tmp > 0) { + satisfied = true; + break; + } + clause.push_back (other); + } + if (satisfied) { + clause.clear (); + mark_garbage (c); + sweep_update_noccs (c); + continue; + } + CADICAL_assert (found); + const unsigned new_size = clause.size (); + sweep_substitute_lrat (c, id); + if (new_size == 0) { + LOG (c, "substituted empty clause"); + CADICAL_assert (!unsat); + learn_empty_clause (); + break; + } + ticks++; + if (new_size == 1) { + LOG (c, "reduces to unit"); + const int unit = clause[0]; + clause.clear (); + assign_unit (unit); + sweeper.propagate.push_back (unit); + mark_garbage (c); + sweep_update_noccs (c); + stats.sweep_units++; + break; + } + CADICAL_assert (c->size >= 2); + if (!c->redundant) + mark_removed (c); + uint64_t new_id = ++clause_id; + if (proof) { + proof->add_derived_clause (new_id, c->redundant, clause, + lrat_chain); + proof->delete_clause (c); + } + c->id = new_id; + lrat_chain.clear (); + size_t l; + int *literals = c->literals; + for (l = 0; l < clause.size (); l++) + literals[l] = clause[l]; + int flushed = c->size - (int) l; + if (flushed) { + LOG ("flushed %d literals", flushed); + (void) shrink_clause (c, l); + } else if (likely_to_be_kept_clause (c)) + mark_added (c); + LOG (c, "substituted"); + if (!repr_already_watched) { + occs (repr).push_back (c); + noccs (repr)++; + } + clause.clear (); + q--; + } + while (p != end) + *q++ = *p++; + ns.resize (q - ns.begin ()); + } +} + +// In contrast to kissat we substitute the equivalences explicitely after +// every successful round of sweeping. This is necessary in order to extract +// valid LRAT proofs for subsequent rounds of sweeping. +void Internal::sweep_substitute_new_equivalences (Sweeper &sweeper) { + if (unsat) + return; + + unsigned count = 0; + CADICAL_assert (lrat_chain.empty ()); + + for (const auto &sb : sweeper.binaries) { + count++; + const auto lit = sb.lit; + const auto other = sb.other; + if (abs (lit) < abs (other)) { + substitute_connected_clauses (sweeper, -other, lit, sb.id); + } else { + substitute_connected_clauses (sweeper, -lit, other, sb.id); + } + CADICAL_assert (lrat_chain.empty ()); + if (val (lit) < 0) { + if (lrat) { + const int64_t lid = unit_id (-lit); + lrat_chain.push_back (lid); + } + if (!val (other)) { + if (lrat) + lrat_chain.push_back (sb.id); + assign_unit (other); + } else if (val (other) < 0) { + if (lrat) { + const int64_t oid = unit_id (-other); + lrat_chain.push_back (oid); + lrat_chain.push_back (sb.id); + } + learn_empty_clause (); + return; + } + } else if (val (other) < 0) { + if (!val (lit)) { + if (lrat) { + const int64_t oid = unit_id (-other); + lrat_chain.push_back (oid); + lrat_chain.push_back (sb.id); + } + assign_unit (lit); + } else + CADICAL_assert (val (lit) > 0); + } + lrat_chain.clear (); + delete_sweep_binary (sb); + if (count == 2) { + if (!val (lit) && !val (other)) { + const auto idx = abs (lit) < abs (other) ? abs (other) : abs (lit); + if (!flags (idx).fixed ()) + mark_substituted (idx); + } + count = 0; + } + CADICAL_assert (lrat_chain.empty ()); + } + sweeper.binaries.clear (); +} + +void Internal::sweep_remove (Sweeper &sweeper, int lit) { + CADICAL_assert (sweeper.reprs[lit] != lit); + vector<int> &partition = sweeper.partition; + const auto begin_partition = partition.begin (); + auto p = begin_partition; + const auto end_partition = partition.end (); + for (; *p != lit; p++) + CADICAL_assert (p + 1 != end_partition); + auto begin_class = p; + while (begin_class != begin_partition && begin_class[-1] != 0) + begin_class--; + auto end_class = p; + while (*end_class != 0) + end_class++; + const unsigned size = end_class - begin_class; + LOG ("removing non-representative %d from equivalence class of size %u", + lit, size); + CADICAL_assert (size > 1); + auto q = begin_class; + if (size == 2) { + LOG ("completely squashing equivalence class of %d", lit); + for (auto r = end_class + 1; r != end_partition; r++) + *q++ = *r; + } else { + for (auto r = begin_class; r != end_partition; r++) + if (r != p) + *q++ = *r; + } + partition.resize (q - partition.begin ()); +} + +void Internal::flip_partition_literals (Sweeper &sweeper) { + const unsigned max_rounds = opts.sweepfliprounds; + if (!max_rounds) + return; + CADICAL_assert (sweeper.partition.size ()); + if (cadical_kitten_status (citten) != 10) + return; +#ifdef LOGGING + unsigned total_flipped = 0; +#endif + unsigned flipped, round = 0; + do { + round++; + flipped = 0; + bool refine = false; + bool limit_hit = false; + auto begin = sweeper.partition.begin (), dst = begin, src = dst; + const auto end = sweeper.partition.end (); + while (src != end) { + auto end_src = src; + while (CADICAL_assert (end_src != end), *end_src != 0) + end_src++; + unsigned size = end_src - src; + CADICAL_assert (size > 1); + auto q = dst; + for (auto p = src; p != end_src; p++) { + const int lit = *p; + if (limit_hit) { + *q++ = lit; + continue; + } else if (cadical_kitten_ticks_limit_hit (sweeper, "partition flipping")) { + *q++ = lit; + limit_hit = true; + continue; + } else if (sweep_flip (lit)) { + LOG ("flipping equivalence candidate %d succeeded", lit); +#ifdef LOGGING + total_flipped++; +#endif + flipped++; + if (--size < 2) + break; + } else { + LOG ("flipping equivalence candidate %d failed", lit); + *q++ = lit; + } + stats.sweep_flip_equivalences++; + } + if (size > 1) { + *q++ = 0; + dst = q; + } + src = end_src + 1; + } + stats.sweep_flipped_equivalences += flipped; + sweeper.partition.resize (dst - sweeper.partition.begin ()); + LOG ("flipped %u equivalence candidates in round %u", flipped, round); + if (terminated_asynchronously ()) + break; + if (cadical_kitten_ticks_limit_hit (sweeper, "partition flipping")) + break; + if (refine) + sweep_refine (sweeper); + } while (flipped && round < max_rounds); + LOG ("flipped %u equivalence candidates in total in %u rounds", + total_flipped, round); +} + +bool Internal::sweep_equivalence_candidates (Sweeper &sweeper, int lit, + int other) { + LOG ("trying equivalence candidates %d = %d", lit, other); + const auto begin = sweeper.partition.begin (); + auto const end = sweeper.partition.end (); + CADICAL_assert (begin + 3 <= end); + CADICAL_assert (end[-3] == lit); + CADICAL_assert (end[-2] == other); + const int third = (end - begin == 3) ? 0 : end[-4]; + int res = cadical_kitten_status (citten); + if (res == 10) { + stats.sweep_flip_equivalences++; + if (sweep_flip (lit)) { + stats.sweep_flipped_equivalences++; + LOG ("flipping %d succeeded", lit); + if (third == 0) { + LOG ("squashing equivalence class of %d", lit); + sweeper.partition.resize (sweeper.partition.size () - 3); + } else { + LOG ("removing %d from equivalence class of %d", lit, other); + end[-3] = other; + end[-2] = 0; + sweeper.partition.resize (sweeper.partition.size () - 1); + } + return false; + } + stats.sweep_flip_equivalences++; + if (sweep_flip (other)) { + stats.sweep_flipped_equivalences++; + LOG ("flipping %d succeeded", other); + if (third == 0) { + LOG ("squashing equivalence class of %d", lit); + sweeper.partition.resize (sweeper.partition.size () - 3); + } else { + LOG ("removing %d from equivalence class of %d", other, lit); + end[-2] = 0; + sweeper.partition.resize (sweeper.partition.size () - 1); + } + return false; + } + } + // frozen variables are not allowed to be eliminated. + // It might still be beneficial to learn the binaries, if they + // really are equivalent, but we avoid the issue by not trying + // for equivalence at all if the non-representative is frozen. + // i.e., the higher absolute value + if (abs (lit) > abs (other) && frozen (lit)) { + if (third == 0) { + LOG ("squashing equivalence class of %d", lit); + sweeper.partition.resize (sweeper.partition.size () - 3); + } else { + LOG ("removing %d from equivalence class of %d", lit, other); + end[-3] = other; + end[-2] = 0; + sweeper.partition.resize (sweeper.partition.size () - 1); + } + return false; + } else if (abs (other) > abs (lit) && frozen (other)) { + if (third == 0) { + LOG ("squashing equivalence class of %d", lit); + sweeper.partition.resize (sweeper.partition.size () - 3); + } else { + LOG ("removing %d from equivalence class of %d", lit, other); + end[-2] = 0; + sweeper.partition.resize (sweeper.partition.size () - 1); + } + return false; + } + + const int not_other = -other; + const int not_lit = -lit; + LOG ("flipping %d and %d both failed", lit, other); + cadical_kitten_assume_signed (citten, not_lit); + cadical_kitten_assume_signed (citten, other); + stats.sweep_solved_equivalences++; + res = sweep_solve (); + if (res == 10) { + stats.sweep_sat_equivalences++; + LOG ("first sweeping implication %d -> %d failed", other, lit); + sweep_refine (sweeper); + } else if (!res) { + stats.sweep_unknown_equivalences++; + LOG ("first sweeping implication %d -> %d hit ticks limit", other, lit); + } + + if (res != 20) + return false; + + stats.sweep_unsat_equivalences++; + LOG ("first sweeping implication %d -> %d succeeded", other, lit); + + save_core (sweeper, 0); + + cadical_kitten_assume_signed (citten, lit); + cadical_kitten_assume_signed (citten, not_other); + res = sweep_solve (); + stats.sweep_solved_equivalences++; + if (res == 10) { + stats.sweep_sat_equivalences++; + LOG ("second sweeping implication %d <- %d failed", other, lit); + sweep_refine (sweeper); + } else if (!res) { + stats.sweep_unknown_equivalences++; + LOG ("second sweeping implication %d <- %d hit ticks limit", other, + lit); + } + + if (res != 20) { + sweeper.core[0].clear (); + return false; + } + + CADICAL_assert (res == 20); + + stats.sweep_unsat_equivalences++; + LOG ("second sweeping implication %d <- %d succeeded too", other, lit); + + save_core (sweeper, 1); + + LOG ("sweep equivalence %d = %d", lit, other); + + // If cadical_kitten behaves as expected, the two binaries of the equivalence + // should be stored at sweeper.core[i].back () for i in {0, 1}. + // We pick the smaller absolute valued literal as representative and + // store the equivalence . + add_core (sweeper, 0); + add_core (sweeper, 1); + if (!val (lit) && !val (other)) { + CADICAL_assert (sweeper.core[0].size ()); + CADICAL_assert (sweeper.core[1].size ()); + stats.sweep_equivalences++; + sweep_binary bin1; + sweep_binary bin2; + if (abs (lit) > abs (other)) { + bin1.lit = lit; + bin1.other = not_other; + bin2.lit = not_lit; + bin2.other = other; + bin1.id = add_sweep_binary (sweeper.core[0].back (), lit, not_other); + bin2.id = add_sweep_binary (sweeper.core[1].back (), not_lit, other); + } else { + bin1.lit = not_other; + bin1.other = lit; + bin2.lit = other; + bin2.other = not_lit; + bin1.id = add_sweep_binary (sweeper.core[0].back (), not_other, lit); + bin2.id = add_sweep_binary (sweeper.core[1].back (), other, not_lit); + } + if (bin1.id && bin2.id) { + sweeper.binaries.push_back (bin1); + sweeper.binaries.push_back (bin2); + } + } + + int repr; + if (abs (lit) < abs (other)) { + repr = sweeper.reprs[other] = lit; + sweeper.reprs[not_other] = not_lit; + sweep_remove (sweeper, other); + } else { + repr = sweeper.reprs[lit] = other; + sweeper.reprs[not_lit] = not_other; + sweep_remove (sweeper, lit); + } + clear_core (sweeper, 0); + clear_core (sweeper, 1); + + const int repr_idx = abs (repr); + schedule_inner (sweeper, repr_idx); + + return true; +} + +const char *Internal::sweep_variable (Sweeper &sweeper, int idx) { + CADICAL_assert (!unsat); + if (!active (idx)) + return "inactive variable"; + const int start = idx; + if (sweeper.reprs[start] != start) + return "non-representative variable"; + CADICAL_assert (sweeper.vars.empty ()); + CADICAL_assert (sweeper.clauses.empty ()); + CADICAL_assert (sweeper.backbone.empty ()); + CADICAL_assert (sweeper.partition.empty ()); + CADICAL_assert (!sweeper.encoded); + + stats.sweep_variables++; + + LOG ("sweeping %d", idx); + CADICAL_assert (!val (start)); + LOG ("starting sweeping[0]"); + add_literal_to_environment (sweeper, 0, start); + LOG ("finished sweeping[0]"); + LOG ("starting sweeping[1]"); + + bool limit_reached = false; + size_t expand = 0, next = 1; + bool success = false; + unsigned depth = 1; + + uint64_t &ticks = sweeper.current_ticks; + while (!limit_reached) { + if (sweeper.encoded >= sweeper.limit.clauses) { + LOG ("environment clause limit reached"); + limit_reached = true; + break; + } + if (expand == next) { + LOG ("finished sweeping[%u]", depth); + if (depth >= sweeper.limit.depth) { + LOG ("environment depth limit reached"); + break; + } + next = sweeper.vars.size (); + if (expand == next) { + LOG ("completely copied all clauses"); + break; + } + depth++; + LOG ("starting sweeping[%u]", depth); + } + const unsigned choices = next - expand; + if (opts.sweeprand && choices > 1) { + const unsigned swaps = sweeper.random.pick_int (0, choices - 1); + if (swaps) { + CADICAL_assert (expand + swaps < sweeper.vars.size ()); + swap (sweeper.vars[expand], sweeper.vars[expand + swaps]); + } + } + const int idx = sweeper.vars[expand]; + LOG ("traversing and adding clauses of %d", idx); + for (unsigned sign = 0; sign < 2; sign++) { + const int lit = sign ? -idx : idx; + ticks += 1 + cache_lines (occs (lit).size (), sizeof (Clause *)); + Occs &ns = occs (lit); + for (auto c : ns) { + ticks++; + if (!can_sweep_clause (c)) + continue; + sweep_clause (sweeper, depth, c); + if (sweeper.vars.size () >= sweeper.limit.vars) { + LOG ("environment variable limit reached"); + limit_reached = true; + break; + } + } + if (limit_reached) + break; + } + expand++; + } + stats.sweep_depth += depth; + stats.sweep_clauses += sweeper.encoded; + stats.sweep_environment += sweeper.vars.size (); + VERBOSE (3, + "sweeping variable %d environment of " + "%zu variables %u clauses depth %u", + externalize (idx), sweeper.vars.size (), sweeper.encoded, depth); + + int res; + if (sweeper.vars.size () == 1) { + LOG ("not sweeping literal %d with environment size 1", idx); + goto DONE; + } + res = sweep_solve (); + LOG ("sub-solver returns '%d'", res); + if (res == 10) { + init_backbone_and_partition (sweeper); +#ifndef CADICAL_QUIET + uint64_t units = stats.sweep_units; + uint64_t solved = stats.sweep_solved; +#endif + START (sweepbackbone); + while (sweeper.backbone.size ()) { + if (unsat || terminated_asynchronously () || + cadical_kitten_ticks_limit_hit (sweeper, "backbone refinement")) { + limit_reached = true; + STOP_SWEEP_BACKBONE: + STOP (sweepbackbone); + goto DONE; + } + flip_backbone_literals (sweeper); + if (terminated_asynchronously () || + cadical_kitten_ticks_limit_hit (sweeper, "backbone refinement")) { + limit_reached = true; + goto STOP_SWEEP_BACKBONE; + } + if (sweeper.backbone.empty ()) + break; + const int lit = sweeper.backbone.back (); + sweeper.backbone.pop_back (); + if (!active (lit)) + continue; + if (sweep_backbone_candidate (sweeper, lit)) + success = true; + } + STOP (sweepbackbone); +#ifndef CADICAL_QUIET + units = stats.sweep_units - units; + solved = stats.sweep_solved - solved; +#endif + VERBOSE (3, + "complete swept variable %d backbone with %" PRIu64 + " units in %" PRIu64 " solver calls", + externalize (idx), units, solved); + CADICAL_assert (sweeper.backbone.empty ()); +#ifndef CADICAL_QUIET + uint64_t equivalences = stats.sweep_equivalences; + solved = stats.sweep_solved; +#endif + START (sweepequivalences); + while (sweeper.partition.size ()) { + if (unsat || terminated_asynchronously () || + cadical_kitten_ticks_limit_hit (sweeper, "partition refinement")) { + limit_reached = true; + STOP_SWEEP_EQUIVALENCES: + STOP (sweepequivalences); + goto DONE; + } + flip_partition_literals (sweeper); + if (terminated_asynchronously () || + cadical_kitten_ticks_limit_hit (sweeper, "backbone refinement")) { + limit_reached = true; + goto STOP_SWEEP_EQUIVALENCES; + } + if (sweeper.partition.empty ()) + break; + if (sweeper.partition.size () > 2) { + const auto end = sweeper.partition.end (); + CADICAL_assert (end[-1] == 0); + int lit = end[-3]; + int other = end[-2]; + if (sweep_equivalence_candidates (sweeper, lit, other)) + success = true; + } else + sweeper.partition.clear (); + } + STOP (sweepequivalences); +#ifndef CADICAL_QUIET + equivalences = stats.sweep_equivalences - equivalences; + solved = stats.sweep_solved - solved; + if (equivalences) + VERBOSE (3, + "complete swept variable %d partition with %" PRIu64 + " equivalences in %" PRIu64 " solver calls", + externalize (idx), equivalences, solved); +#endif + } else if (res == 20) + sweep_empty_clause (sweeper); + +DONE: + clear_sweeper (sweeper); + + if (!unsat) + sweep_substitute_new_equivalences (sweeper); + if (!unsat) + sweep_dense_propagate (sweeper); + + if (success && limit_reached) + return "successfully despite reaching limit"; + if (!success && !limit_reached) + return "unsuccessfully without reaching limit"; + else if (success && !limit_reached) + return "successfully without reaching limit"; + CADICAL_assert (!success && limit_reached); + return "unsuccessfully and reached limit"; +} + +struct sweep_candidate { + unsigned rank; + int idx; +}; + +struct rank_sweep_candidate { + bool operator() (sweep_candidate a, sweep_candidate b) const { + CADICAL_assert (a.rank && b.rank); + CADICAL_assert (a.idx > 0 && b.idx > 0); + if (a.rank < b.rank) + return true; + if (b.rank < a.rank) + return false; + return a.idx < b.idx; + } +}; + +bool Internal::scheduable_variable (Sweeper &sweeper, int idx, + size_t *occ_ptr) { + const int lit = idx; + const size_t pos = noccs (lit); + if (!pos) + return false; + const unsigned max_occurrences = sweeper.limit.clauses; + if (pos > max_occurrences) + return false; + const int not_lit = -lit; + const size_t neg = noccs (not_lit); + if (!neg) + return false; + if (neg > max_occurrences) + return false; + *occ_ptr = pos + neg; + return true; +} + +unsigned Internal::schedule_all_other_not_scheduled_yet (Sweeper &sweeper) { + vector<sweep_candidate> fresh; + for (const auto &idx : vars) { + Flags &f = flags (idx); + if (!f.active ()) + continue; + if (sweep_incomplete && !f.sweep) + continue; + if (scheduled_variable (sweeper, idx)) + continue; + size_t occ; + if (!scheduable_variable (sweeper, idx, &occ)) { + f.sweep = false; + continue; + } + sweep_candidate cand; + cand.rank = occ; + cand.idx = idx; + fresh.push_back (cand); + } + const size_t size = fresh.size (); + CADICAL_assert (size <= UINT_MAX); + sort (fresh.begin (), fresh.end (), rank_sweep_candidate ()); + for (auto &cand : fresh) + schedule_outer (sweeper, cand.idx); + return size; +} + +unsigned Internal::reschedule_previously_remaining (Sweeper &sweeper) { + unsigned rescheduled = 0; + for (const auto &idx : sweep_schedule) { + Flags &f = flags (idx); + if (!f.active ()) + continue; + if (scheduled_variable (sweeper, idx)) + continue; + size_t occ; + if (!scheduable_variable (sweeper, idx, &occ)) { + f.sweep = false; + continue; + } + schedule_inner (sweeper, idx); + rescheduled++; + } + sweep_schedule.clear (); + return rescheduled; +} + +unsigned Internal::incomplete_variables () { + unsigned res = 0; + for (const auto &idx : vars) { + Flags &f = flags (idx); + if (!f.active ()) + continue; + if (f.sweep) + res++; + } + return res; +} + +void Internal::mark_incomplete (Sweeper &sweeper) { + unsigned marked = 0; + for (all_scheduled (idx)) { + if (!flags (idx).sweep) { + flags (idx).sweep = true; + marked++; + } + } + sweep_incomplete = true; +#ifndef CADICAL_QUIET + VERBOSE (2, "marked %u scheduled sweeping variables as incomplete", + marked); +#else + (void) marked; +#endif +} + +unsigned Internal::schedule_sweeping (Sweeper &sweeper) { + const unsigned rescheduled = reschedule_previously_remaining (sweeper); + const unsigned fresh = schedule_all_other_not_scheduled_yet (sweeper); + const unsigned scheduled = fresh + rescheduled; + const unsigned incomplete = incomplete_variables (); +#ifndef CADICAL_QUIET + PHASE ("sweep", stats.sweep, + "scheduled %u variables %.0f%% " + "(%u rescheduled %.0f%%, %u incomplete %.0f%%)", + scheduled, percent (scheduled, active ()), rescheduled, + percent (rescheduled, scheduled), incomplete, + percent (incomplete, scheduled)); +#endif + if (incomplete) + CADICAL_assert (sweep_incomplete); + else { + if (sweep_incomplete) + stats.sweep_completed++; + mark_incomplete (sweeper); + } + return scheduled; +} + +void Internal::unschedule_sweeping (Sweeper &sweeper, unsigned swept, + unsigned scheduled) { +#ifdef CADICAL_QUIET + (void) scheduled, (void) swept; +#endif + CADICAL_assert (sweep_schedule.empty ()); + CADICAL_assert (sweep_incomplete); + for (all_scheduled (idx)) + if (active (idx)) { + sweep_schedule.push_back (idx); + LOG ("untried scheduled %d", idx); + } +#ifndef CADICAL_QUIET + const unsigned retained = sweep_schedule.size (); +#endif + VERBOSE (3, "retained %u variables %.0f%% to be swept next time", + retained, percent (retained, active ())); + const unsigned incomplete = incomplete_variables (); + if (incomplete) + VERBOSE (3, "need to sweep %u more variables %.0f%% for completion", + incomplete, percent (incomplete, active ())); + else { + VERBOSE (3, "no more variables needed to complete sweep"); + sweep_incomplete = false; + stats.sweep_completed++; + } + PHASE ("sweep", stats.sweep, "swept %u variables (%u remain %.0f%%)", + swept, incomplete, percent (incomplete, scheduled)); +} + +bool Internal::sweep () { + if (!opts.sweep) + return false; + if (unsat) + return false; + if (terminated_asynchronously ()) + return false; + if (delaying_sweep.bumpreasons.delay ()) { // TODO need to fix Delay + last.sweep.ticks = stats.ticks.search[0] + stats.ticks.search[1]; + return false; + } + delaying_sweep.bumpreasons.bypass_delay (); + SET_EFFORT_LIMIT (tickslimit, sweep, !opts.sweepcomplete); + delaying_sweep.bumpreasons.unbypass_delay (); + + CADICAL_assert (!level); + START_SIMPLIFIER (sweep, SWEEP); + stats.sweep++; + uint64_t equivalences = stats.sweep_equivalences; + uint64_t units = stats.sweep_units; + Sweeper sweeper = Sweeper (this); + if (opts.sweepcomplete) + sweeper.limit.ticks = INT64_MAX; + else + sweeper.limit.ticks = tickslimit - stats.ticks.sweep; + sweep_set_cadical_kitten_ticks_limit (sweeper); + const unsigned scheduled = schedule_sweeping (sweeper); + uint64_t swept = 0, limit = 10; + for (;;) { + if (unsat) + break; + if (terminated_asynchronously ()) + break; + if (cadical_kitten_ticks_limit_hit (sweeper, "sweeping loop")) + break; + int idx = next_scheduled (sweeper); + if (idx == 0) + break; + flags (idx).sweep = false; +#ifndef CADICAL_QUIET + const char *res = +#endif + sweep_variable (sweeper, idx); + VERBOSE (2, "swept[%" PRIu64 "] external variable %d %s", swept, + externalize (idx), res); + if (++swept == limit) { + VERBOSE (2, + "found %" PRIu64 " equivalences and %" PRIu64 + " units after sweeping %" PRIu64 " variables ", + stats.sweep_equivalences - equivalences, + stats.sweep_units - units, swept); + limit *= 10; + } + } + VERBOSE (2, "swept %" PRIu64 " variables", swept); + equivalences = stats.sweep_equivalences - equivalences, + units = stats.sweep_units - units; + PHASE ("sweep", stats.sweep, + "found %" PRIu64 " equivalences and %" PRIu64 " units", + equivalences, units); + unschedule_sweeping (sweeper, swept, scheduled); + release_sweeper (sweeper); + + if (!unsat) { + propagated = 0; + if (!propagate ()) { + learn_empty_clause (); + } + } + + uint64_t eliminated = equivalences + units; + report ('=', !eliminated); + + if (relative (eliminated, swept) < 0.001) { + delaying_sweep.bumpreasons.bump_delay (); + } else { + delaying_sweep.bumpreasons.reduce_delay (); + } + STOP_SIMPLIFIER (sweep, SWEEP); + return eliminated; +} + +} // namespace CaDiCaL + +ABC_NAMESPACE_IMPL_END diff --git a/src/sat/cadical/cadical_terminal.cpp b/src/sat/cadical/cadical_terminal.cpp new file mode 100644 index 0000000000..c8aeea2ab5 --- /dev/null +++ b/src/sat/cadical/cadical_terminal.cpp @@ -0,0 +1,49 @@ +#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 diff --git a/src/sat/cadical/cadical_ternary.cpp b/src/sat/cadical/cadical_ternary.cpp new file mode 100644 index 0000000000..9f99e22791 --- /dev/null +++ b/src/sat/cadical/cadical_ternary.cpp @@ -0,0 +1,456 @@ +#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 diff --git a/src/sat/cadical/cadical_tier.cpp b/src/sat/cadical/cadical_tier.cpp new file mode 100644 index 0000000000..92faf507f1 --- /dev/null +++ b/src/sat/cadical/cadical_tier.cpp @@ -0,0 +1,59 @@ +#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 diff --git a/src/sat/cadical/cadical_transred.cpp b/src/sat/cadical/cadical_transred.cpp new file mode 100644 index 0000000000..82a961deea --- /dev/null +++ b/src/sat/cadical/cadical_transred.cpp @@ -0,0 +1,259 @@ +#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 diff --git a/src/sat/cadical/cadical_unstable.cpp b/src/sat/cadical/cadical_unstable.cpp new file mode 100644 index 0000000000..c3162d70aa --- /dev/null +++ b/src/sat/cadical/cadical_unstable.cpp @@ -0,0 +1,38 @@ +#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 diff --git a/src/sat/cadical/cadical_util.cpp b/src/sat/cadical/cadical_util.cpp new file mode 100644 index 0000000000..fd9b5b29d3 --- /dev/null +++ b/src/sat/cadical/cadical_util.cpp @@ -0,0 +1,135 @@ +#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 diff --git a/src/sat/cadical/cadical_var.cpp b/src/sat/cadical/cadical_var.cpp new file mode 100644 index 0000000000..e2348da7b9 --- /dev/null +++ b/src/sat/cadical/cadical_var.cpp @@ -0,0 +1,45 @@ +#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 diff --git a/src/sat/cadical/cadical_veripbtracer.cpp b/src/sat/cadical/cadical_veripbtracer.cpp new file mode 100644 index 0000000000..b1f7c95a70 --- /dev/null +++ b/src/sat/cadical/cadical_veripbtracer.cpp @@ -0,0 +1,400 @@ +#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 diff --git a/src/sat/cadical/cadical_version.cpp b/src/sat/cadical/cadical_version.cpp new file mode 100644 index 0000000000..a0cceef63d --- /dev/null +++ b/src/sat/cadical/cadical_version.cpp @@ -0,0 +1,113 @@ +#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 diff --git a/src/sat/cadical/cadical_vivify.cpp b/src/sat/cadical/cadical_vivify.cpp new file mode 100644 index 0000000000..8d99c29323 --- /dev/null +++ b/src/sat/cadical/cadical_vivify.cpp @@ -0,0 +1,1962 @@ +#include "global.h" + +#include "vivify.hpp" +#include "internal.hpp" +#include "util.hpp" +#include <algorithm> +#include <limits> +#include <utility> + +ABC_NAMESPACE_IMPL_START + +namespace CaDiCaL { + +/*------------------------------------------------------------------------*/ + +// Vivification is a special case of asymmetric tautology elimination (ATE) +// and asymmetric literal elimination (ALE). It strengthens and removes +// clauses proven redundant through unit propagation. +// +// The original algorithm is due to a paper by Piette, Hamadi and Sais +// published at ECAI'08. We have an inprocessing version, e.g., it does not +// necessarily run-to-completion. Our version also performs conflict +// analysis and uses a new heuristic for selecting clauses to vivify. + +// Our idea is to focus on clauses with many occurrences of its literals in +// other clauses first. This both complements nicely our implementation of +// subsume, which is bounded, e.g., subsumption attempts are skipped for +// very long clauses with literals with many occurrences and also is +// stronger in the sense that it enables to remove more clauses due to unit +// propagation (AT checks). + +// While first focusing on irredundant clause we then added a separate phase +// upfront which focuses on strengthening also redundant clauses in spirit +// of the ideas presented in the IJCAI'17 paper by M. Luo, C.-M. Li, F. +// Xiao, F. Manya, and Z. Lu. + +// There is another very similar approach called 'distilliation' published +// by Han and Somenzi in DAC'07, which reorganizes the CNF in a trie data +// structure to reuse decisions and propagations along the trie. We used +// that as an inspiration but instead of building a trie we simple sort +// clauses and literals in such a way that we get the same effect. If a new +// clause is 'distilled' or 'vivified' we first check how many of the +// decisions (which are only lazily undone) can be reused for that clause. +// Reusing can be improved by picking a global literal order and sorting the +// literals in all clauses with respect to that order. We favor literals +// with more occurrences first. Then we sort clauses lexicographically with +// respect to that literal order. + +/*------------------------------------------------------------------------*/ + +// Candidate clause 'subsumed' is subsumed by 'subsuming'. + +inline void Internal::vivify_subsume_clause (Clause *subsuming, + Clause *subsumed) { + stats.subsumed++; + stats.vivifysubs++; +#ifndef CADICAL_NDEBUG + CADICAL_assert (subsuming); + CADICAL_assert (subsumed); + CADICAL_assert (subsuming != subsumed); + CADICAL_assert (!subsumed->garbage); + // size after removeing units; + int real_size_subsuming = 0, real_size_subsumed = 0; + for (auto lit : *subsuming) { + if (!val (lit) || var (lit).level) + ++real_size_subsuming; + else + CADICAL_assert (val (lit) < 0); + } + for (auto lit : *subsumed) { + if (!val (lit) || var (lit).level) + ++real_size_subsumed; + else + CADICAL_assert (val (lit) < 0); + } + CADICAL_assert (real_size_subsuming <= real_size_subsumed); +#endif + LOG (subsumed, "subsumed"); + if (subsumed->redundant) { + stats.subred++; + ++stats.vivifysubred; + } else { + stats.subirr++; + ++stats.vivifysubirr; + } + if (subsuming->garbage) { + CADICAL_assert (subsuming->size == 2); + LOG (subsuming, + "binary subsuming clause was already deleted, so undeleting"); + subsuming->garbage = false; + subsuming->glue = 1; + ++stats.current.total; + if (subsuming->redundant) + stats.current.redundant++; + else + stats.current.irredundant++, stats.irrlits += subsuming->size; + } + 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); + mark_added (subsuming); + 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'. +} + +// demoting a clause (opposite is promote from subsume.cpp) + +inline void Internal::demote_clause (Clause *c) { + stats.subsumed++; + stats.vivifydemote++; + LOG (c, "demoting"); + CADICAL_assert (!c->redundant); + mark_removed (c); + c->redundant = true; + CADICAL_assert (stats.current.irredundant > 0); + stats.current.irredundant--; + CADICAL_assert (stats.added.irredundant > 0); + stats.added.irredundant--; + stats.irrlits -= c->size; + stats.current.redundant++; + stats.added.redundant++; + c->glue = c->size - 1; + // ... and keep 'stats.added.total'. +} + +/*------------------------------------------------------------------------*/ +// For vivification we have a separate dedicated propagation routine, which +// prefers to propagate binary clauses first. It also uses its own +// assignment procedure 'vivify_assign', which does not mess with phase +// saving during search nor the conflict and other statistics and further +// can be inlined separately here. The propagation routine needs to ignore +// (large) clauses which are currently vivified. + +inline void Internal::vivify_assign (int lit, Clause *reason) { + require_mode (VIVIFY); + const int idx = vidx (lit); + CADICAL_assert (!vals[idx]); + CADICAL_assert (!flags (idx).eliminated () || !reason); + Var &v = var (idx); + v.level = level; // required to reuse decisions + v.trail = (int) trail.size (); // used in 'vivify_better_watch' + CADICAL_assert ((int) num_assigned < max_var); + num_assigned++; + v.reason = level ? reason : 0; // for conflict analysis + if (!level) + learn_unit_clause (lit); + const signed char tmp = sign (lit); + vals[idx] = tmp; + vals[-idx] = -tmp; + CADICAL_assert (val (lit) > 0); + CADICAL_assert (val (-lit) < 0); + trail.push_back (lit); + LOG (reason, "vivify assign %d", lit); +} + +// Assume negated literals in candidate clause. + +void Internal::vivify_assume (int lit) { + require_mode (VIVIFY); + level++; + control.push_back (Level (lit, trail.size ())); + LOG ("vivify decide %d", lit); + CADICAL_assert (level > 0); + CADICAL_assert (propagated == trail.size ()); + vivify_assign (lit, 0); +} + +// Dedicated routine similar to 'propagate' in 'propagate.cpp' and +// 'probe_propagate' with 'probe_propagate2' in 'probe.cpp'. Please refer +// to that code for more explanation on how propagation is implemented. + +bool Internal::vivify_propagate (int64_t &ticks) { + require_mode (VIVIFY); + CADICAL_assert (!unsat); + START (propagate); + int64_t before = propagated2 = propagated; + for (;;) { + if (propagated2 != trail.size ()) { + const int lit = -trail[propagated2++]; + LOG ("vivify propagating %d over binary clauses", -lit); + Watches &ws = watches (lit); + ticks += + 1 + cache_lines (ws.size (), sizeof (const_watch_iterator *)); + for (const auto &w : ws) { + if (!w.binary ()) + continue; + const signed char b = val (w.blit); + if (b > 0) + continue; + if (b < 0) + conflict = w.clause; // but continue + else { + ticks++; + build_chain_for_units (w.blit, w.clause, 0); + vivify_assign (w.blit, w.clause); + lrat_chain.clear (); + } + } + } else if (!conflict && propagated != trail.size ()) { + const int lit = -trail[propagated++]; + LOG ("vivify propagating %d over large clauses", -lit); + Watches &ws = watches (lit); + const const_watch_iterator eow = ws.end (); + const_watch_iterator i = ws.begin (); + ticks += 1 + cache_lines (ws.size (), sizeof (*i)); + watch_iterator j = ws.begin (); + while (i != eow) { + const Watch w = *j++ = *i++; + if (w.binary ()) + continue; + if (val (w.blit) > 0) + continue; + ticks++; + if (w.clause->garbage) { + j--; + continue; + } + literal_iterator lits = w.clause->begin (); + const int other = lits[0] ^ lits[1] ^ lit; + const signed char u = val (other); + if (u > 0) + j[-1].blit = other; + else { + const int size = w.clause->size; + const const_literal_iterator end = lits + size; + const literal_iterator middle = lits + w.clause->pos; + literal_iterator k = middle; + signed char v = -1; + int r = 0; + while (k != end && (v = val (r = *k)) < 0) + k++; + if (v < 0) { + k = lits + 2; + CADICAL_assert (w.clause->pos <= size); + while (k != middle && (v = val (r = *k)) < 0) + k++; + } + w.clause->pos = k - lits; + CADICAL_assert (lits + 2 <= k), CADICAL_assert (k <= w.clause->end ()); + if (v > 0) + j[-1].blit = r; + else if (!v) { + LOG (w.clause, "unwatch %d in", r); + lits[0] = other; + lits[1] = r; + *k = lit; + ticks++; + watch_literal (r, lit, w.clause); + j--; + } else if (!u) { + if (w.clause == ignore) { + LOG ("ignoring propagation due to clause to vivify"); + continue; + } + ticks++; + CADICAL_assert (v < 0); + vivify_chain_for_units (other, w.clause); + vivify_assign (other, w.clause); + lrat_chain.clear (); + } else { + if (w.clause == ignore) { + LOG ("ignoring conflict due to clause to vivify"); + continue; + } + CADICAL_assert (u < 0); + CADICAL_assert (v < 0); + conflict = w.clause; + break; + } + } + } + if (j != i) { + while (i != eow) + *j++ = *i++; + ws.resize (j - ws.begin ()); + } + } else + break; + } + int64_t delta = propagated2 - before; + stats.propagations.vivify += delta; + if (conflict) + LOG (conflict, "conflict"); + STOP (propagate); + return !conflict; +} + +/*------------------------------------------------------------------------*/ + +// Check whether a literal occurs less often. In the implementation below +// (search for 'int64_t score = ...' or '@4') we actually compute a +// weighted occurrence count similar to the Jeroslow Wang heuristic. + +struct vivify_more_noccs { + + Internal *internal; + + vivify_more_noccs (Internal *i) : internal (i) {} + + bool operator() (int a, int b) { + int64_t n = internal->noccs (a); + int64_t m = internal->noccs (b); + if (n > m) + return true; // larger occurrences / score first + if (n < m) + return false; // smaller occurrences / score last + if (a == -b) + return a > 0; // positive literal first + return abs (a) < abs (b); // smaller index first + } +}; + +struct vivify_more_noccs_kissat { + + Internal *internal; + + vivify_more_noccs_kissat (Internal *i) : internal (i) {} + + bool operator() (int a, int b) { + unsigned t = internal->noccs (a); + unsigned s = internal->noccs (b); + return ((t - s) | ((b - a) & ~(s - t))) >> 31; + } +}; + +// Sort candidate clauses by the number of occurrences (actually by their +// score) of their literals, with clauses to be vivified first last. We +// assume that clauses are sorted w.r.t. more occurring (higher score) +// literals first (with respect to 'vivify_more_noccs'). +// +// For example if there are the following (long irredundant) clauses +// +// 1 -3 -4 (A) +// -1 -2 3 4 (B) +// 2 -3 4 (C) +// +// then we have the following literal scores using Jeroslow Wang scores and +// normalizing it with 2^12 (which is the same as 1<<12): +// +// nocc ( 1) = 2^12 * (2^-3 ) = 512 3. +// nocc (-1) = 2^12 * (2^-4 ) = 256 6. +// nocc ( 2) = 2^12 * (2^-3 ) = 512 4. +// nocc (-2) = 2^12 * (2^-4 ) = 256 7. @1 +// nocc ( 3) = 2^12 * (2^-4 ) = 256 8. +// nocc (-3) = 2^12 * (2^-3 + 2^-3) = 1024 1. +// nocc ( 4) = 2^12 * (2^-3 + 2^-4) = 768 2. +// nocc (-4) = 2^12 * (2^-3 ) = 512 5. +// +// which gives the literal order (according to 'vivify_more_noccs') +// +// -3, 4, 1, 2, -4, -1, -2, 3 +// +// Then sorting the literals in each clause gives +// +// -3 1 -4 (A') +// 4 -1 -2 3 (B') @2 +// -3 4 2 (C') +// +// and finally sorting those clauses lexicographically w.r.t. scores is +// +// -3 4 2 (C') +// -3 1 -4 (A') @3 +// 4 -1 -2 3 (B') +// +// This order is defined by 'vivify_clause_later' which returns 'true' if +// the first clause should be vivified later than the second. + +struct vivify_clause_later { + + Internal *internal; + + vivify_clause_later (Internal *i) : internal (i) {} + + bool operator() (Clause *a, Clause *b) const { + + if (a == b) + return false; + + // First focus on clauses scheduled in the last vivify round but not + // checked yet since then. + // + if (!a->vivify && b->vivify) + return true; + if (a->vivify && !b->vivify) + return false; + + // Among redundant clauses (in redundant mode) prefer small glue. + // + if (a->redundant) { + CADICAL_assert (b->redundant); + if (a->glue > b->glue) + return true; + if (a->glue < b->glue) + return false; + } + + // Then prefer shorter size. + // + if (a->size > b->size) + return true; + if (a->size < b->size) + return false; + + // Now compare literals in the clauses lexicographically with respect to + // the literal order 'vivify_more_noccs' assuming literals are sorted + // decreasingly with respect to that order. + // + const auto eoa = a->end (), eob = b->end (); + auto j = b->begin (); + for (auto i = a->begin (); i != eoa && j != eob; i++, j++) + if (*i != *j) + return vivify_more_noccs (internal) (*j, *i); + + return j == eob; // Prefer shorter clauses to be vivified first. + } +}; + +/*------------------------------------------------------------------------*/ + +// Attempting on-the-fly subsumption during sorting when the last line is +// reached in 'vivify_clause_later' above turned out to be trouble some for +// identical clauses. This is the single point where 'vivify_clause_later' +// is not asymmetric and would require 'stable' sorting for determinism. It +// can also not be made 'complete' on-the-fly. Instead of on-the-fly +// subsumption we thus go over the sorted scheduled in a linear scan +// again and remove certain subsumed clauses (the subsuming clause is +// syntactically a prefix of the subsumed clause), which includes +// those troublesome syntactically identical clauses. + +struct vivify_flush_smaller { + + bool operator() (Clause *a, Clause *b) const { + + const auto eoa = a->end (), eob = b->end (); + auto i = a->begin (), j = b->begin (); + for (; i != eoa && j != eob; i++, j++) + if (*i != *j) + return *i < *j; + + return j == eob && i != eoa; + } +}; + +void Internal::flush_vivification_schedule (std::vector<Clause *> &schedule, + int64_t &ticks) { + ticks += 1 + 3 * cache_lines (schedule.size (), sizeof (Clause *)); + stable_sort (schedule.begin (), schedule.end (), vivify_flush_smaller ()); + + const auto end = schedule.end (); + auto j = schedule.begin (), i = j; + + Clause *prev = 0; + int64_t subsumed = 0; + for (; i != end; i++) { + ticks++; + Clause *c = *j++ = *i; + if (!prev || c->size < prev->size) { + prev = c; + continue; + } + const auto eop = prev->end (); + auto k = prev->begin (); + for (auto l = c->begin (); k != eop; k++, l++) + if (*k != *l) + break; + if (k == eop) { + LOG (c, "found subsumed"); + LOG (prev, "subsuming"); + CADICAL_assert (!c->garbage); + CADICAL_assert (!prev->garbage); + CADICAL_assert (c->redundant || !prev->redundant); + mark_garbage (c); + subsumed++; + j--; + } else + prev = c; + } + + if (subsumed) + PHASE ("vivify", stats.vivifications, + "flushed %" PRId64 " subsumed scheduled clauses", subsumed); + + stats.vivifysubs += subsumed; + + if (subsumed) { + schedule.resize (j - schedule.begin ()); + shrink_vector (schedule); + } else + CADICAL_assert (j == end); +} + +/*------------------------------------------------------------------------*/ + +// Depending on whether we try to vivify redundant or irredundant clauses, +// we schedule a clause to be vivified. For redundant clauses we initially +// only try to vivify them if they are likely to survive the next 'reduce' +// operation, but this left the last schedule empty most of the time. + +bool Internal::consider_to_vivify_clause (Clause *c) { + if (c->garbage) + return false; + if (opts.vivifyonce >= 1 && c->redundant && c->vivified) + return false; + if (opts.vivifyonce >= 2 && !c->redundant && c->vivified) + return false; + if (!c->redundant) + return true; + CADICAL_assert (c->redundant); + + // likely_to_be_kept_clause is too aggressive at removing tier-3 clauses + return true; +} + +/*------------------------------------------------------------------------*/ + +// In a strengthened clause the idea is to move non-false literals to the +// front, followed by false literals. Literals are further sorted by +// reverse assignment order. The goal is to use watches which require to +// backtrack as few as possible decision levels. + +struct vivify_better_watch { + + Internal *internal; + + vivify_better_watch (Internal *i) : internal (i) {} + + bool operator() (int a, int b) { + + const signed char av = internal->val (a), bv = internal->val (b); + + if (av >= 0 && bv < 0) + return true; + if (av < 0 && bv >= 0) + return false; + + return internal->var (a).trail > internal->var (b).trail; + } +}; + +// Common code to actually strengthen a candidate clause. The resulting +// strengthened clause is communicated through the global 'clause'. + +void Internal::vivify_strengthen (Clause *c) { + + CADICAL_assert (!clause.empty ()); + + if (clause.size () == 1) { + + backtrack_without_updating_phases (); + const int unit = clause[0]; + LOG (c, "vivification shrunken to unit %d", unit); + CADICAL_assert (!val (unit)); + assign_unit (unit); + // lrat_chain.clear (); done in search_assign + stats.vivifyunits++; + + bool ok = propagate (); + if (!ok) + learn_empty_clause (); + + } else { + + // See explanation before 'vivify_better_watch' above. + // + sort (clause.begin (), clause.end (), vivify_better_watch (this)); + + int new_level = level; + + const int lit0 = clause[0]; + signed char val0 = val (lit0); + if (val0 < 0) { + const int level0 = var (lit0).level; + LOG ("1st watch %d negative at level %d", lit0, level0); + new_level = level0 - 1; + } + + const int lit1 = clause[1]; + const signed char val1 = val (lit1); + if (val1 < 0 && !(val0 > 0 && var (lit0).level <= var (lit1).level)) { + const int level1 = var (lit1).level; + LOG ("2nd watch %d negative at level %d", lit1, level1); + new_level = level1 - 1; + } + + CADICAL_assert (new_level >= 0); + if (new_level < level) + backtrack (new_level); + + CADICAL_assert (val (lit0) >= 0); + CADICAL_assert (val (lit1) >= 0 || (val (lit0) > 0 && val (lit1) < 0 && + var (lit0).level <= var (lit1).level)); + + Clause *d = new_clause_as (c); + LOG (c, "before vivification"); + LOG (d, "after vivification"); + (void) d; + } + clause.clear (); + mark_garbage (c); + lrat_chain.clear (); + ++stats.vivifystrs; +} + +void Internal::vivify_sort_watched (Clause *c) { + + sort (c->begin (), c->end (), vivify_better_watch (this)); + + int new_level = level; + + const int lit0 = c->literals[0]; + signed char val0 = val (lit0); + if (val0 < 0) { + const int level0 = var (lit0).level; + LOG ("1st watch %d negative at level %d", lit0, level0); + new_level = level0 - 1; + } + + const int lit1 = c->literals[1]; + const signed char val1 = val (lit1); + if (val1 < 0 && !(val0 > 0 && var (lit0).level <= var (lit1).level)) { + const int level1 = var (lit1).level; + LOG ("2nd watch %d negative at level %d", lit1, level1); + new_level = level1 - 1; + } + + CADICAL_assert (new_level >= 0); + if (new_level < level) + backtrack (new_level); + + CADICAL_assert (val (lit0) >= 0); + CADICAL_assert (val (lit1) >= 0 || (val (lit0) > 0 && val (lit1) < 0 && + var (lit0).level <= var (lit1).level)); +} +// Conflict analysis from 'start' which learns a decision only clause. +// +// We cannot use the stack-based implementation of Kissat, because we need +// to iterate over the conflict in topological ordering to produce a valid +// LRAT proof + +void Internal::vivify_analyze (Clause *start, bool &subsumes, + Clause **subsuming, + const Clause *const candidate, int implied, + bool &redundant) { + const auto &t = &trail; // normal trail, so next_trail is wrong + int i = t->size (); // Start at end-of-trail. + Clause *reason = start; + CADICAL_assert (reason); + CADICAL_assert (!trail.empty ()); + int uip = trail.back (); + bool mark_implied = (implied); + + while (i >= 0) { + if (reason) { + redundant = (redundant || reason->redundant); + subsumes = (start != reason && reason->size <= start->size); + LOG (reason, "resolving on %d with", uip); + for (auto other : *reason) { + const Var v = var (other); + Flags &f = flags (other); + if (!marked2 (other) && v.level) { + LOG ("not subsuming due to lit %d", other); + subsumes = false; + } + if (!val (other)) { + LOG ("skipping unset lit %d", other); + continue; + } + if (other == uip) { + continue; + } + if (!v.level) { + if (f.seen || !lrat || reason == start) + continue; + LOG ("unit reason for %d", other); + int64_t id = unit_id (-other); + LOG ("adding unit reason %zd for %d", id, other); + unit_chain.push_back (id); + f.seen = true; + analyzed.push_back (other); + continue; + } + if (mark_implied && other != implied) { + LOG ("skipping non-implied literal %d on current level", other); + continue; + } + + CADICAL_assert (val (other)); + if (f.seen) + continue; + LOG ("pushing lit %d", other); + analyzed.push_back (other); + f.seen = true; + } + if (start->redundant) { + const int new_glue = recompute_glue (start); + promote_clause (start, new_glue); + } + if (subsumes) { + CADICAL_assert (reason); + LOG (reason, "clause found subsuming"); + LOG (candidate, "clause found subsumed"); + *subsuming = reason; + return; + } + } else { + LOG ("vivify analyzed decision %d", uip); + clause.push_back (-uip); + } + mark_implied = false; + + uip = 0; + while (!uip && i > 0) { + CADICAL_assert (i > 0); + const int lit = (*t)[--i]; + if (!var (lit).level) + continue; + if (flags (lit).seen) + uip = lit; + } + if (!uip) + break; + LOG ("uip is %d", uip); + Var &w = var (uip); + reason = w.reason; + if (lrat && reason) + lrat_chain.push_back (reason->id); + } + (void) candidate; +} + +void Internal::vivify_deduce (Clause *candidate, Clause *conflict, + int implied, Clause **subsuming, + bool &redundant) { + CADICAL_assert (lrat_chain.empty ()); + bool subsumes; + Clause *reason; + + CADICAL_assert (clause.empty ()); + if (implied) { + reason = candidate; + mark2 (candidate); + const int not_implied = -implied; + CADICAL_assert (var (not_implied).level); + Flags &f = flags (not_implied); + f.seen = true; + LOG ("pushing implied lit %d", not_implied); + analyzed.push_back (not_implied); + clause.push_back (implied); + } else { + reason = (conflict ? conflict : candidate); + CADICAL_assert (reason); + CADICAL_assert (!reason->garbage); + mark2 (candidate); + subsumes = (candidate != reason); + redundant = reason->redundant; + LOG (reason, "resolving with"); + if (lrat) + lrat_chain.push_back (reason->id); + for (auto lit : *reason) { + const Var &v = var (lit); + Flags &f = flags (lit); + CADICAL_assert (val (lit) < 0); + if (!v.level) { + if (!lrat) + continue; + LOG ("adding unit %d", lit); + if (!f.seen) { + // nevertheless we can use var (l) as if l was still assigned + // because var is updated lazily + int64_t id = unit_id (-lit); + LOG ("adding unit reason %zd for %d", id, lit); + unit_chain.push_back (id); + } + f.seen = true; + analyzed.push_back (lit); + continue; + } + CADICAL_assert (v.level); + if (!marked2 (lit)) { + LOG ("lit %d is not marked", lit); + subsumes = false; + } + LOG ("analyzing lit %d", lit); + LOG ("pushing lit %d", lit); + analyzed.push_back (lit); + f.seen = true; + } + if (reason != candidate && reason->redundant) { + const int new_glue = recompute_glue (reason); + promote_clause (reason, new_glue); + } + if (subsumes) { + CADICAL_assert (candidate != reason); +#ifndef CADICAL_NDEBUG + int nonfalse_reason = 0; + for (auto lit : *reason) + if (!fixed (lit)) + ++nonfalse_reason; + + int nonfalse_candidate = 0; + for (auto lit : *candidate) + if (!fixed (lit)) + ++nonfalse_candidate; + + CADICAL_assert (nonfalse_reason <= nonfalse_candidate); +#endif + LOG (candidate, "vivify subsumed 0"); + LOG (reason, "vivify subsuming 0"); + *subsuming = reason; + unmark (candidate); + if (lrat) + lrat_chain.clear (); + return; + } + } + + vivify_analyze (reason, subsumes, subsuming, candidate, implied, + redundant); + unmark (candidate); + if (subsumes) { + CADICAL_assert (*subsuming); + LOG (candidate, "vivify subsumed"); + LOG (*subsuming, "vivify subsuming"); + if (lrat) + lrat_chain.clear (); + } +} +/*------------------------------------------------------------------------*/ + +bool Internal::vivify_shrinkable (const std::vector<int> &sorted, + Clause *conflict) { + + unsigned count_implied = 0; + for (auto lit : sorted) { + const signed char value = val (lit); + if (!value) { + LOG ("vivification unassigned %d", lit); + return true; + } + if (value > 0) { + LOG ("vivification implied satisfied %d", lit); + if (conflict) + return true; + if (count_implied++) { + LOG ("at least one implied literal with conflict thus shrinking"); + return true; + } + } else { + CADICAL_assert (value < 0); + const Var &v = var (lit); + const Flags &f = flags (lit); + if (!v.level) + continue; + if (!f.seen) { + LOG ("vivification non-analyzed %d", lit); + return true; + } + if (v.reason) { + LOG ("vivification implied falsified %d", lit); + return true; + } + } + } + return false; +} +/*------------------------------------------------------------------------*/ + +inline void Internal::vivify_increment_stats (const Vivifier &vivifier) { + switch (vivifier.tier) { + case Vivify_Mode::TIER1: + ++stats.vivifystred1; + break; + case Vivify_Mode::TIER2: + ++stats.vivifystred2; + break; + case Vivify_Mode::TIER3: + ++stats.vivifystred3; + break; + default: + CADICAL_assert (vivifier.tier == Vivify_Mode::IRREDUNDANT); + ++stats.vivifystrirr; + break; + } +} +/*------------------------------------------------------------------------*/ +// instantiate last literal (see the description of the hack track 2023), +// fix the watches and +// backtrack two level back +bool Internal::vivify_instantiate ( + const std::vector<int> &sorted, Clause *c, + std::vector<std::tuple<int, Clause *, bool>> &lrat_stack, + int64_t &ticks) { + LOG ("now trying instantiation"); + conflict = nullptr; + const int lit = sorted.back (); + LOG ("vivify instantiation"); + CADICAL_assert (!var (lit).reason); + CADICAL_assert (var (lit).level); + CADICAL_assert (val (lit)); + backtrack (level - 1); + CADICAL_assert (val (lit) == 0); + stats.vivifydecs++; + vivify_assume (lit); + bool ok = vivify_propagate (ticks); + if (!ok) { + LOG (c, "instantiate success with literal %d in", lit); + stats.vivifyinst++; + // strengthen clause + if (lrat) { + clear_analyzed_literals (); + CADICAL_assert (lrat_chain.empty ()); + vivify_build_lrat (0, c, lrat_stack); + vivify_build_lrat (0, conflict, lrat_stack); + clear_analyzed_literals (); + } + int remove = lit; + conflict = nullptr; + unwatch_clause (c); + backtrack_without_updating_phases (level - 2); + strengthen_clause (c, remove); + vivify_sort_watched (c); + watch_clause (c); + CADICAL_assert (!conflict); + return true; + } else { + LOG ("vivify instantiation failed"); + return false; + } +} + +/*------------------------------------------------------------------------*/ + +// Main function: try to vivify this candidate clause in the given mode. + +bool Internal::vivify_clause (Vivifier &vivifier, Clause *c) { + + CADICAL_assert (c->size > 2); // see (NO-BINARY) below + CADICAL_assert (analyzed.empty ()); + + c->vivify = false; // mark as checked / tried + c->vivified = true; // and globally remember + + CADICAL_assert (!c->garbage); + + auto &lrat_stack = vivifier.lrat_stack; + auto &ticks = vivifier.ticks; + ticks++; + + // First check whether the candidate clause is already satisfied and at + // the same time copy its non fixed literals to 'sorted'. The literals + // in the candidate clause might not be sorted anymore due to replacing + // watches during propagation, even though we sorted them initially + // while pushing the clause onto the schedule and sorting the schedule. + // + auto &sorted = vivifier.sorted; + sorted.clear (); + + for (const auto &lit : *c) { + const int tmp = fixed (lit); + if (tmp > 0) { + LOG (c, "satisfied by propagated unit %d", lit); + mark_garbage (c); + return false; + } else if (!tmp) + sorted.push_back (lit); + } + + CADICAL_assert (sorted.size () > 1); + if (sorted.size () == 2) { + LOG ("skipping actual binary"); + return false; + } + + sort (sorted.begin (), sorted.end (), vivify_more_noccs_kissat (this)); + + // The actual vivification checking is performed here, by assuming the + // negation of each of the remaining literals of the clause in turn and + // propagating it. If a conflict occurs or another literal in the + // clause becomes assigned during propagation, we can stop. + // + LOG (c, "vivification checking"); + stats.vivifychecks++; + + // If the decision 'level' is non-zero, then we can reuse decisions for + // the previous candidate, and avoid re-propagating them. In preliminary + // experiments this saved between 30%-50% decisions (and thus + // propagations), which in turn lets us also vivify more clauses within + // the same propagation bounds, or terminate earlier if vivify runs to + // completion. + // + if (level) { +#ifdef LOGGING + int orig_level = level; +#endif + // First check whether this clause is actually a reason for forcing + // one of its literals to true and then backtrack one level before + // that happened. Otherwise this clause might be incorrectly + // considered to be redundant or if this situation is checked then + // redundancy by other clauses using this forced literal becomes + // impossible. + // + int forced = 0; + + // This search could be avoided if we would eagerly set the 'reason' + // boolean flag of clauses, which however we do not want to do for + // binary clauses (during propagation) and thus would still require + // a version of 'protect_reason' for binary clauses during 'reduce' + // (well binary clauses are not collected during 'reduce', but again + // this exception from the exception is pretty complex and thus a + // simply search here is probably easier to understand). + + for (const auto &lit : *c) { + const signed char tmp = val (lit); + if (tmp < 0) + continue; + if (tmp > 0 && var (lit).reason == c) + forced = lit; + break; + } + if (forced) { + LOG ("clause is reason forcing %d", forced); + CADICAL_assert (var (forced).level); + backtrack_without_updating_phases (var (forced).level - 1); + } + + // As long the (remaining) literals of the sorted clause match + // decisions on the trail we just reuse them. + // + if (level) { + + int l = 1; // This is the decision level we want to reuse. + + for (const auto &lit : sorted) { + CADICAL_assert (!fixed (lit)); + const int decision = control[l].decision; + if (-lit == decision) { + LOG ("reusing decision %d at decision level %d", decision, l); + stats.vivifyreused++; + if (++l > level) + break; + } else { + LOG ("literal %d does not match decision %d at decision level %d", + lit, decision, l); + backtrack_without_updating_phases (l - 1); + break; + } + } + } + + LOG ("reused %d decision levels from %d", level, orig_level); + } + + LOG (sorted, "sorted size %zd probing schedule", sorted.size ()); + + // Make sure to ignore this clause during propagation. This is not that + // easy for binary clauses (NO-BINARY), e.g., ignoring binary clauses, + // without changing 'propagate'. Actually, we do not want to remove binary + // clauses which are subsumed. Those are hyper binary resolvents and + // should be kept as learned clauses instead, unless they are transitive + // in the binary implication graph, which in turn is detected during + // transitive reduction in 'transred'. + // + ignore = c; + + int subsume = 0; // determined to be redundant / subsumed + + // If the candidate is redundant, i.e., we are in redundant mode, the + // clause is subsumed (in one of the two cases below where 'subsume' is + // assigned) and further all reasons involved are only binary clauses, + // then this redundant clause is what we once called a hidden tautology, + // and even for redundant clauses it makes sense to remove the candidate. + // It does not add anything to propagation power of the formula. This is + // the same argument as removing transitive clauses in the binary + // implication graph during transitive reduction. + // + + // Go over the literals in the candidate clause in sorted order. + // + for (const auto &lit : sorted) { + + // Exit loop as soon a literal is positively implied (case '@5' below) + // or propagation of the negation of a literal fails ('@6'). + // + if (subsume) + break; + + // We keep on assigning literals, even though we know already that we + // can remove one (was negatively implied), since we either might run + // into the 'subsume' case above or more false literals become implied. + // In any case this might result in stronger vivified clauses. As a + // consequence continue with this loop even if 'remove' is non-zero. + + const signed char tmp = val (lit); + + if (tmp) { // literal already assigned + + const Var &v = var (lit); + CADICAL_assert (v.level); + if (!v.reason) { + LOG ("skipping decision %d", lit); + continue; + } + + if (tmp < 0) { + CADICAL_assert (v.level); + LOG ("literal %d is already false and can be removed", lit); + continue; + } + + CADICAL_assert (tmp > 0); + LOG ("subsumed since literal %d already true", lit); + subsume = lit; // will be able to subsume candidate '@5' + break; + } + + CADICAL_assert (!tmp); + + stats.vivifydecs++; + vivify_assume (-lit); + LOG ("negated decision %d score %" PRId64 "", lit, noccs (lit)); + + if (!vivify_propagate (ticks)) { + break; // hot-spot + } + } + + if (subsume) { + int better_subsume_trail = var (subsume).trail; + for (auto lit : sorted) { + if (val (lit) <= 0) + continue; + const Var v = var (lit); + if (v.trail < better_subsume_trail) { + LOG ("improving subsume from %d at %d to %d at %d", subsume, + better_subsume_trail, lit, v.trail); + better_subsume_trail = v.trail; + subsume = lit; + } + } + } + + Clause *subsuming = nullptr; + bool redundant = false; + const int level_after_assumptions = level; + CADICAL_assert (level_after_assumptions); + vivify_deduce (c, conflict, subsume, &subsuming, redundant); + + bool res; + + // reverse lrat_chain. We could probably work with reversed iterators + // (views) to be more efficient but we would have to distinguish in proof + // + if (lrat) { + for (auto id : unit_chain) + lrat_chain.push_back (id); + unit_chain.clear (); + reverse (lrat_chain.begin (), lrat_chain.end ()); + } + + if (subsuming) { + CADICAL_assert (c != subsuming); + LOG (c, "deleting subsumed clause"); + if (c->redundant && subsuming->redundant && c->glue < subsuming->glue) { + promote_clause (c, c->glue); + } + vivify_subsume_clause (subsuming, c); + res = false; + // stats.vivifysubs++; // already done in vivify_subsume_clause + } else if (vivify_shrinkable (sorted, conflict)) { + vivify_increment_stats (vivifier); + LOG ("vivify succeeded, learning new clause"); + clear_analyzed_literals (); + LOG (lrat_chain, "lrat"); + LOG (clause, "learning clause"); + conflict = nullptr; // TODO dup from below + vivify_strengthen (c); + res = true; + } else if (subsume && c->redundant) { + LOG (c, "vivification implied"); + mark_garbage (c); + ++stats.vivifyimplied; + res = true; + } else if ((conflict || subsume) && !c->redundant && !redundant) { + LOG ("demote clause from irredundant to redundant"); + if (opts.vivifydemote) { + demote_clause (c); + const int new_glue = recompute_glue (c); + promote_clause (c, new_glue); + res = false; + } else { + mark_garbage (c); + ++stats.vivifyimplied; + res = true; + } + } else if (subsume) { + LOG (c, "no vivification instantiation with implied literal %d", + subsume); + CADICAL_assert (!c->redundant); + CADICAL_assert (redundant); + res = false; + ++stats.vivifyimplied; + } else { + CADICAL_assert (level > 2); + CADICAL_assert ((size_t) level == sorted.size ()); + LOG (c, "vivification failed on"); + lrat_chain.clear (); + CADICAL_assert (!subsume); + if (!subsume && opts.vivifyinst) { + res = vivify_instantiate (sorted, c, lrat_stack, ticks); + CADICAL_assert (!conflict); + } else { + LOG ("cannot apply instantiation"); + res = false; + } + } + + if (conflict && level == level_after_assumptions) { + LOG ("forcing backtracking at least one level after conflict"); + backtrack_without_updating_phases (level - 1); + } + + clause.clear (); + clear_analyzed_literals (); // TODO why needed? + lrat_chain.clear (); + conflict = nullptr; + return res; +} + +// when we can strengthen clause c we have to build lrat. +// uses f.seen so do not forget to clear flags afterwards. +// this can happen in three cases. (1), (2) are only sound in redundant mode +// (1) literal l in c is positively implied. in this case we call the +// function with (l, l.reason). This justifies the reduction because the new +// clause c' will include l and all decisions so l.reason is a conflict +// assuming -c' (2) conflict during vivify propagation. function is called +// with (0, conflict) similar to (1) but more direct. (3) some literals in c +// are negatively implied and can therefore be removed. in this case we call +// the function with (0, c). originally we justified each literal in c on +// its own but this is not actually necessary. +// + +// Non-recursive version, as some bugs have been found. DFS over the +// reasons with preordering (aka we explore the entire reason before +// exploring deeper) +void Internal::vivify_build_lrat ( + int lit, Clause *reason, + std::vector<std::tuple<int, Clause *, bool>> &stack) { + CADICAL_assert (stack.empty ()); + stack.push_back ({lit, reason, false}); + while (!stack.empty ()) { + int lit; + Clause *reason; + bool finished; + std::tie (lit, reason, finished) = stack.back (); + LOG ("VIVIFY LRAT justifying %d", lit); + stack.pop_back (); + if (lit && flags (lit).seen) { + LOG ("skipping already justified"); + continue; + } + if (finished) { + lrat_chain.push_back (reason->id); + if (lit && reason) { + Flags &f = flags (lit); + f.seen = true; + analyzed.push_back (lit); // CADICAL_assert (val (other) < 0); + CADICAL_assert (flags (lit).seen); + } + continue; + } else + stack.push_back ({lit, reason, true}); + for (const auto &other : *reason) { + if (other == lit) + continue; + Var &v = var (other); + Flags &f = flags (other); + if (f.seen) + continue; + if (!v.level) { + const int64_t id = unit_id (-other); + lrat_chain.push_back (id); + f.seen = true; + analyzed.push_back (other); + continue; + } + if (v.reason) { // recursive justification + LOG ("VIVIFY LRAT pushing %d", other); + stack.push_back ({other, v.reason, false}); + } + } + } + stack.clear (); +} + +// calculate lrat_chain +// +inline void Internal::vivify_chain_for_units (int lit, Clause *reason) { + if (!lrat) + return; + // LOG ("building chain for units"); bad line for debugging + // equivalence if (opts.chrono && assignment_level (lit, reason)) return; + if (level) + return; // not decision level 0 + CADICAL_assert (lrat_chain.empty ()); + for (auto &reason_lit : *reason) { + if (lit == reason_lit) + continue; + CADICAL_assert (val (reason_lit)); + 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); +} + +vivify_ref create_ref (Internal *internal, Clause *c) { + LOG (c, "creating vivify_refs of clause"); + vivify_ref ref; + ref.clause = c; + ref.size = c->size; + for (int i = 0; i < COUNTREF_COUNTS; ++i) + ref.count[i] = 0; + ref.vivify = c->vivify; + int lits[COUNTREF_COUNTS] = {0}; + for (int i = 0; i != std::min (COUNTREF_COUNTS, c->size); ++i) { + int best = 0; + unsigned best_count = 0; + for (auto lit : *c) { + LOG ("to find best number of occurrences for literal %d, looking at " + "literal %d", + i, lit); + for (int j = 0; j != i; ++j) { + LOG ("comparing %d with literal %d", lit, lits[j]); + if (lits[j] == lit) + goto CONTINUE_WITH_NEXT_LITERAL; + } + { + const int64_t lit_count = internal->noccs (lit); + CADICAL_assert (lit_count); + LOG ("checking literal %d with %zd occurrences", lit, lit_count); + if (lit_count <= best_count) + continue; + best_count = lit_count; + best = lit; + } + CONTINUE_WITH_NEXT_LITERAL:; + } + CADICAL_assert (best); + CADICAL_assert (best_count); + CADICAL_assert (best_count < UINT32_MAX); + ref.count[i] = + ((uint64_t) best_count << 32) + (uint64_t) internal->vlit (best); + LOG ("final count at position %d is %d - %d: %lu", i, best, best_count, + ref.count[i]); + lits[i] = best; + } + return ref; +} +/*------------------------------------------------------------------------*/ +inline void +Internal::vivify_prioritize_leftovers ([[maybe_unused]] char tag, + size_t prioritized, + std::vector<Clause *> &schedule) { + if (prioritized) { + PHASE ("vivify", stats.vivifications, + "[phase %c] leftovers of %" PRId64 " clause", tag, prioritized); + } else { + PHASE ("vivify", stats.vivifications, + "[phase %c] prioritizing all clause", tag); + for (auto c : schedule) + c->vivify = true; + } + const size_t max = opts.vivifyschedmax; + if (schedule.size () > max) { + if (prioritized) { + std::partition (begin (schedule), end (schedule), + [] (Clause *c) { return c->vivify; }); + } + schedule.resize (max); + } + // let's try to save a bit of memory + shrink_vector (schedule); +} + +void Internal::vivify_initialize (Vivifier &vivifier, int64_t &ticks) { + + const int tier1 = vivifier.tier1_limit; + const int tier2 = vivifier.tier2_limit; + // Count the number of occurrences of literals in all clauses, + // particularly binary clauses, which are usually responsible + // for most of the propagations. + // + init_noccs (); + + // Disconnect all watches since we sort literals within clauses. + // + CADICAL_assert (watching ()); +#if 0 + clear_watches (); +#endif + + size_t prioritized_irred = 0, prioritized_tier1 = 0, + prioritized_tier2 = 0, prioritized_tier3 = 0; + for (const auto &c : clauses) { + ++ticks; + if (c->size == 2) + continue; // see also (NO-BINARY) above + if (!consider_to_vivify_clause (c)) + continue; + + // This computes an approximation of the Jeroslow Wang heuristic + // score + // + // nocc (L) = sum 2^(12-|C|) + // L in C in F + // + // but we cap the size at 12, that is all clauses of size 12 and + // larger contribute '1' to the score, which allows us to use 'long' + // numbers. See the example above (search for '@1'). + // + const int shift = 12 - c->size; + const int64_t score = shift < 1 ? 1 : (1l << shift); // @4 + for (const auto lit : *c) { + noccs (lit) += score; + } + LOG (c, "putting clause in candidates"); + if (!c->redundant) + vivifier.schedule_irred.push_back (c), + prioritized_irred += (c->vivify); + else if (c->glue <= tier1) + vivifier.schedule_tier1.push_back (c), + prioritized_tier1 += (c->vivify); + else if (c->glue <= tier2) + vivifier.schedule_tier2.push_back (c), + prioritized_tier2 += (c->vivify); + else + vivifier.schedule_tier3.push_back (c), + prioritized_tier3 += (c->vivify); + ++ticks; + } + + vivify_prioritize_leftovers ('x', prioritized_irred, + vivifier.schedule_irred); + vivify_prioritize_leftovers ('u', prioritized_tier1, + vivifier.schedule_tier1); + vivify_prioritize_leftovers ('v', prioritized_tier2, + vivifier.schedule_tier2); + vivify_prioritize_leftovers ('w', prioritized_tier3, + vivifier.schedule_tier3); + + if (opts.vivifyflush) { + clear_watches (); + for (auto &sched : vivifier.schedules) { + for (const auto &c : sched) { + // Literals in scheduled clauses are sorted with their highest score + // literals first (as explained above in the example at '@2'). This + // is also needed in the prefix subsumption checking below. We do an + // approximation below that is done only in the vivify_ref structure + // below. + // + sort (c->begin (), c->end (), vivify_more_noccs (this)); + } + // Flush clauses subsumed by another clause with the same prefix, + // which also includes flushing syntactically identical clauses. + // + flush_vivification_schedule (sched, ticks); + } + connect_watches (); // watch all relevant clauses + } +#if 0 + connect_watches (); // watch all relevant clauses + vivify_propagate (ticks); +#endif + vivify_propagate (ticks); +} + +inline std::vector<vivify_ref> ¤t_refs_schedule (Vivifier &vivifier) { + switch (vivifier.tier) { + case Vivify_Mode::TIER1: + return vivifier.refs_schedule_tier1; + break; + case Vivify_Mode::TIER2: + return vivifier.refs_schedule_tier2; + break; + case Vivify_Mode::TIER3: + return vivifier.refs_schedule_tier3; + break; + default: + return vivifier.refs_schedule_irred; + break; + } +#ifdef WIN32 + __assume(false); +#else + __builtin_unreachable (); +#endif +} + +inline std::vector<Clause *> ¤t_schedule (Vivifier &vivifier) { + switch (vivifier.tier) { + case Vivify_Mode::TIER1: + return vivifier.schedule_tier1; + break; + case Vivify_Mode::TIER2: + return vivifier.schedule_tier2; + break; + case Vivify_Mode::TIER3: + return vivifier.schedule_tier3; + break; + default: + return vivifier.schedule_irred; + break; + } +#ifdef WIN32 + __assume(false); +#else + __builtin_unreachable (); +#endif +} + +struct vivify_refcount_rank { + int offset; + vivify_refcount_rank (int j) : offset (j) { + CADICAL_assert (offset < COUNTREF_COUNTS); + } + typedef uint64_t Type; + Type operator() (const vivify_ref &a) const { return a.count[offset]; } +}; + +struct vivify_refcount_smaller { + int offset; + vivify_refcount_smaller (int j) : offset (j) { + CADICAL_assert (offset < COUNTREF_COUNTS); + } + bool operator() (const vivify_ref &a, const vivify_ref &b) const { + const auto s = vivify_refcount_rank (offset) (a); + const auto t = vivify_refcount_rank (offset) (b); + return s < t; + } +}; + +struct vivify_inversesize_rank { + vivify_inversesize_rank () {} + typedef uint64_t Type; + Type operator() (const vivify_ref &a) const { return ~a.size; } +}; + +struct vivify_inversesize_smaller { + vivify_inversesize_smaller () {} + bool operator() (const vivify_ref &a, const vivify_ref &b) const { + const auto s = vivify_inversesize_rank () (a); + const auto t = vivify_inversesize_rank () (b); + return s < t; + } +}; + +/*------------------------------------------------------------------------*/ +// There are two modes of vivification, one using all clauses and one +// focusing on irredundant clauses only. The latter variant working on +// irredundant clauses only can also remove irredundant asymmetric +// tautologies (clauses subsumed through unit propagation), which in +// redundant mode is incorrect (due to propagating over redundant clauses). + +void Internal::vivify_round (Vivifier &vivifier, int64_t ticks_limit) { + + if (unsat) + return; + if (terminated_asynchronously ()) + return; + + PHASE ("vivify", stats.vivifications, + "starting %c vivification round ticks limit %" PRId64 "", + vivifier.tag, ticks_limit); + + PHASE ("vivify", stats.vivifications, + "starting %c vivification round ticks limit %" PRId64 "", + vivifier.tag, ticks_limit); + + CADICAL_assert (watching ()); + + auto &refs_schedule = current_refs_schedule (vivifier); + auto &schedule = current_schedule (vivifier); + + int64_t ticks = 1 + schedule.size (); + + // Sort candidates, with first to be tried candidate clause last, i.e., + // many occurrences and high score literals) as in the example explained + // above (search for '@3'). + // + if (vivifier.tier != Vivify_Mode::IRREDUNDANT || + irredundant () / 10 < redundant ()) { + // Literals in scheduled clauses are sorted with their highest score + // literals first (as explained above in the example at '@2'). This is + // also needed in the prefix subsumption checking below. We do an + // approximation below that is done only in the vivify_ref structure + // below. + // + + // first build the schedule with vivifier_refs + auto end_schedule = end (schedule); + refs_schedule.resize (schedule.size ()); + std::transform (begin (schedule), end_schedule, begin (refs_schedule), + [&] (Clause *c) { return create_ref (this, c); }); + // now sort by size + MSORT (opts.radixsortlim, refs_schedule.begin (), refs_schedule.end (), + vivify_inversesize_rank (), vivify_inversesize_smaller ()); + // now (stable) sort by number of occurrences + for (int i = 0; i < COUNTREF_COUNTS; ++i) { + const int offset = COUNTREF_COUNTS - 1 - i; + MSORT (opts.radixsortlim, refs_schedule.begin (), + refs_schedule.end (), vivify_refcount_rank (offset), + vivify_refcount_smaller (offset)); + } + // force left-overs at the end + std::stable_partition (begin (refs_schedule), end (refs_schedule), + [] (vivify_ref c) { return !c.vivify; }); + std::transform (begin (refs_schedule), end (refs_schedule), + begin (schedule), + [] (vivify_ref c) { return c.clause; }); + erase_vector (refs_schedule); + LOG ("clause after sorting final:"); + } else { + // skip sorting but still put clauses with the vivify tag at the end to + // be done first Kissat does this implicitely by going twice over all + // clauses + std::stable_partition (begin (schedule), end (schedule), + [] (Clause *c) { return !c->vivify; }); + } + + // Remember old values of counters to summarize after each round with + // verbose messages what happened in that round. + // + int64_t checked = stats.vivifychecks; + int64_t subsumed = stats.vivifysubs; + int64_t strengthened = stats.vivifystrs; + int64_t units = stats.vivifyunits; + + int64_t scheduled = schedule.size (); + stats.vivifysched += scheduled; + + PHASE ("vivify", stats.vivifications, + "scheduled %" PRId64 " clauses to be vivified %.0f%%", scheduled, + percent (scheduled, stats.current.irredundant)); + + // Limit the number of propagations during vivification as in 'probe'. + // + const int64_t limit = ticks_limit - stats.ticks.vivify; + CADICAL_assert (limit >= 0); + + // the clauses might still contain set literals, so propagation since the + // beginning + propagated2 = propagated = 0; + + if (!unsat && !propagate ()) { + LOG ("propagation after connecting watches in inconsistency"); + learn_empty_clause (); + } + + vivifier.ticks = ticks; + int retry = 0; + while (!unsat && !terminated_asynchronously () && !schedule.empty () && + vivifier.ticks < limit) { + Clause *c = schedule.back (); // Next candidate. + schedule.pop_back (); + if (vivify_clause (vivifier, c) && !c->garbage && c->size > 2 && + retry < opts.vivifyretry) { + ++retry; + schedule.push_back (c); + } else + retry = 0; + } + + if (level) + backtrack_without_updating_phases (); + + if (!unsat) { + int64_t still_need_to_be_vivified = schedule.size (); +#if 0 + // in the current round we have new_clauses_to_vivify @ leftovers from previous round There are + // now two possibilities: (i) we consider all clauses as leftovers, or (ii) only the leftovers + // from previous round are considered leftovers. + // + // CaDiCaL had the first version before. If + // commented out we go to the second version. + for (auto c : schedule) + c->vivify = true; +#elif 1 + // if we have gone through all the leftovers, the current clauses are + // leftovers for the next round + if (!schedule.empty () && !schedule.front ()->vivify && + schedule.back ()->vivify) + for (auto c : schedule) + c->vivify = true; +#else + // do nothing like in kissat and use the candidates for next time. +#endif + // Preference clauses scheduled but not vivified yet next time. + // + if (still_need_to_be_vivified) + PHASE ("vivify", stats.vivifications, + "still need to vivify %" PRId64 " clauses %.02f%% of %" PRId64 + " scheduled", + still_need_to_be_vivified, + percent (still_need_to_be_vivified, scheduled), scheduled); + else { + PHASE ("vivify", stats.vivifications, + "no previously not yet vivified clause left"); + } + + erase_vector (schedule); // Reclaim memory early. + } + + if (!unsat) { + + // Since redundant clause were disconnected during propagating vivified + // units in redundant mode, and further irredundant clauses are + // arbitrarily sorted, we have to propagate all literals again after + // connecting the first two literals in the clauses, in order to + // reestablish the watching invariant. + // + propagated2 = propagated = 0; + + if (!propagate ()) { + LOG ("propagating vivified units leads to conflict"); + learn_empty_clause (); + } + } + + checked = stats.vivifychecks - checked; + subsumed = stats.vivifysubs - subsumed; + strengthened = stats.vivifystrs - strengthened; + units = stats.vivifyunits - units; + + PHASE ("vivify", stats.vivifications, + "checked %" PRId64 " clauses %.02f%% of %" PRId64 + " scheduled using %" PRIu64 " ticks", + checked, percent (checked, scheduled), scheduled, vivifier.ticks); + if (units) + PHASE ("vivify", stats.vivifications, + "found %" PRId64 " units %.02f%% of %" PRId64 " checked", units, + percent (units, checked), checked); + if (subsumed) + PHASE ("vivify", stats.vivifications, + "subsumed %" PRId64 " clauses %.02f%% of %" PRId64 " checked", + subsumed, percent (subsumed, checked), checked); + if (strengthened) + PHASE ("vivify", stats.vivifications, + "strengthened %" PRId64 " clauses %.02f%% of %" PRId64 + " checked", + strengthened, percent (strengthened, checked), checked); + + stats.subsumed += subsumed; + stats.strengthened += strengthened; + stats.ticks.vivify += vivifier.ticks; + + bool unsuccessful = !(subsumed + strengthened + units); + report (vivifier.tag, unsuccessful); +} + +void set_vivifier_mode (Vivifier &vivifier, Vivify_Mode tier) { + vivifier.tier = tier; + switch (tier) { + case Vivify_Mode::TIER1: + vivifier.tag = 'u'; + break; + case Vivify_Mode::TIER2: + vivifier.tag = 'v'; + break; + case Vivify_Mode::TIER3: + vivifier.tag = 'w'; + break; + default: + CADICAL_assert (tier == Vivify_Mode::IRREDUNDANT); + vivifier.tag = 'x'; + break; + } +} +/*------------------------------------------------------------------------*/ + +void Internal::compute_tier_limits (Vivifier &vivifier) { + if (!opts.vivifycalctier) { + vivifier.tier1_limit = 2; + vivifier.tier2_limit = 6; + return; + } + vivifier.tier1_limit = tier1[false]; + vivifier.tier2_limit = tier2[false]; +} + +/*------------------------------------------------------------------------*/ + +bool Internal::vivify () { + + if (unsat) + return false; + if (terminated_asynchronously ()) + return false; + if (!opts.vivify) + return false; + if (!stats.current.irredundant) + return false; + if (level) + backtrack (); + CADICAL_assert (opts.vivify); + CADICAL_assert (!level); + + SET_EFFORT_LIMIT (totallimit, vivify, true); + + private_steps = true; + + START_SIMPLIFIER (vivify, VIVIFY); + stats.vivifications++; + + // the effort is normalized by dividing by sumeffort below, hence no need + // to multiply by 1e-3 (also making the precision better) + double tier1effort = !opts.vivifytier1 ? 0 : (double) opts.vivifytier1eff; + double tier2effort = !opts.vivifytier2 ? 0 : (double) opts.vivifytier2eff; + double tier3effort = !opts.vivifytier3 ? 0 : (double) opts.vivifytier3eff; + double irreffort = + delaying_vivify_irredundant.bumpreasons.delay () || !opts.vivifyirred + ? 0 + : (double) opts.vivifyirredeff; + double sumeffort = tier1effort + tier2effort + tier3effort + irreffort; + if (!stats.current.redundant) + tier1effort = tier2effort = tier3effort = 0; + if (!sumeffort) + sumeffort = irreffort = 1; + int64_t total = totallimit - stats.ticks.vivify; + + PHASE ("vivify", stats.vivifications, + "vivification limit of %" PRId64 " ticks", total); + Vivifier vivifier (Vivify_Mode::TIER1); + compute_tier_limits (vivifier); + + if (vivifier.tier1_limit == vivifier.tier2_limit) { + tier1effort += tier2effort; + tier2effort = 0; + LOG ("vivification tier1 matches tier2 " + "thus using tier2 budget for tier1"); + } + int64_t init_ticks = 0; + + // Refill the schedule every time. Unchecked clauses are 'saved' by + // setting their 'vivify' bit, such that they can be tried next time. + // + // TODO: count against ticks.vivify directly instead of this unholy + // shifting. + vivify_initialize (vivifier, init_ticks); + stats.ticks.vivify += init_ticks; + int64_t limit = stats.ticks.vivify; + const double shared_effort = (double) init_ticks / 4.0; + if (opts.vivifytier1) { + set_vivifier_mode (vivifier, Vivify_Mode::TIER1); + if (limit < stats.ticks.vivify) + limit = stats.ticks.vivify; + const double effort = (total * tier1effort) / sumeffort; + CADICAL_assert (std::numeric_limits<int64_t>::max () - (int64_t) effort >= + limit); + limit += effort; + if (limit - shared_effort > stats.ticks.vivify) { + limit -= shared_effort; + CADICAL_assert (limit >= 0); + vivify_round (vivifier, limit); + } else { + LOG ("building the schedule already used our entire ticks budget for " + "tier1"); + } + } + + if (!unsat && tier2effort) { + erase_vector ( + vivifier.schedule_tier1); // save memory (well, not really as we + // already reached the peak memory) + if (limit < stats.ticks.vivify) + limit = stats.ticks.vivify; + const double effort = (total * tier2effort) / sumeffort; + CADICAL_assert (std::numeric_limits<int64_t>::max () - (int64_t) effort >= + limit); + limit += effort; + if (limit - shared_effort > stats.ticks.vivify) { + limit -= shared_effort; + CADICAL_assert (limit >= 0); + set_vivifier_mode (vivifier, Vivify_Mode::TIER2); + vivify_round (vivifier, limit); + } else { + LOG ("building the schedule already used our entire ticks budget for " + "tier2"); + } + } + + if (!unsat && tier3effort) { + erase_vector (vivifier.schedule_tier2); + if (limit < stats.ticks.vivify) + limit = stats.ticks.vivify; + const double effort = (total * tier3effort) / sumeffort; + CADICAL_assert (std::numeric_limits<int64_t>::max () - (int64_t) effort >= + limit); + limit += effort; + if (limit - shared_effort > stats.ticks.vivify) { + limit -= shared_effort; + CADICAL_assert (limit >= 0); + set_vivifier_mode (vivifier, Vivify_Mode::TIER3); + vivify_round (vivifier, limit); + } else { + LOG ("building the schedule already used our entire ticks budget for " + "tier3"); + } + } + + if (!unsat && irreffort) { + erase_vector (vivifier.schedule_tier3); + if (limit < stats.ticks.vivify) + limit = stats.ticks.vivify; + const double effort = (total * irreffort) / sumeffort; + CADICAL_assert (std::numeric_limits<int64_t>::max () - (int64_t) effort >= + limit); + limit += effort; + if (limit - shared_effort > stats.ticks.vivify) { + limit -= shared_effort; + CADICAL_assert (limit >= 0); + set_vivifier_mode (vivifier, Vivify_Mode::IRREDUNDANT); + const int old = stats.vivifystrirr; + const int old_tried = stats.vivifychecks; + vivify_round (vivifier, limit); + if (stats.vivifychecks - old_tried == 0 || + (float) (stats.vivifystrirr - old) / + (float) (stats.vivifychecks - old_tried) < + 0.01) { + delaying_vivify_irredundant.bumpreasons.bump_delay (); + } else { + delaying_vivify_irredundant.bumpreasons.reduce_delay (); + } + } else { + delaying_vivify_irredundant.bumpreasons.bump_delay (); + LOG ("building the schedule already used our entire ticks budget for " + "irredundant"); + } + } + + reset_noccs (); + STOP_SIMPLIFIER (vivify, VIVIFY); + + private_steps = false; + + return true; +} + +} // namespace CaDiCaL + +ABC_NAMESPACE_IMPL_END diff --git a/src/sat/cadical/cadical_walk.cpp b/src/sat/cadical/cadical_walk.cpp new file mode 100644 index 0000000000..13b1bf9f46 --- /dev/null +++ b/src/sat/cadical/cadical_walk.cpp @@ -0,0 +1,710 @@ +#include "global.h" + +#include "internal.hpp" + +ABC_NAMESPACE_IMPL_START + +namespace CaDiCaL { + +/*------------------------------------------------------------------------*/ + +// Random walk local search based on 'ProbSAT' ideas. + +struct Walker { + + Internal *internal; + + Random random; // local random number generator + int64_t propagations; // number of propagations + int64_t limit; // limit on number of propagations + vector<Clause *> broken; // currently unsatisfied clauses + double epsilon; // smallest considered score + vector<double> table; // break value to score table + vector<double> scores; // scores of candidate literals + + double score (unsigned); // compute score from break count + + Walker (Internal *, double size, int64_t limit); +}; + +// These are in essence the CB values from Adrian Balint's thesis. They +// denote the inverse 'cb' of the base 'b' of the (probability) weight +// 'b^-i' for picking a literal with the break value 'i' (first column is +// the 'size', second the 'CB' value). + +static double cbvals[][2] = { + {0.0, 2.00}, {3.0, 2.50}, {4.0, 2.85}, {5.0, 3.70}, + {6.0, 5.10}, {7.0, 7.40}, // Adrian has '5.4', but '7.4' looks better. +}; + +static const int ncbvals = sizeof cbvals / sizeof cbvals[0]; + +// We interpolate the CB values for uniform random SAT formula to the non +// integer situation of average clause size by piecewise linear functions. +// +// y2 - y1 +// ------- * (x - x1) + y1 +// x2 - x1 +// +// where 'x' is the average size of clauses and 'y' the CB value. + +inline static double fitcbval (double size) { + int i = 0; + while (i + 2 < ncbvals && + (cbvals[i][0] > size || cbvals[i + 1][0] < size)) + i++; + const double x2 = cbvals[i + 1][0], x1 = cbvals[i][0]; + const double y2 = cbvals[i + 1][1], y1 = cbvals[i][1]; + const double dx = x2 - x1, dy = y2 - y1; + CADICAL_assert (dx); + const double res = dy * (size - x1) / dx + y1; + CADICAL_assert (res > 0); + return res; +} + +// Initialize the data structures for one local search round. + +Walker::Walker (Internal *i, double size, int64_t l) + : internal (i), random (internal->opts.seed), // global random seed + propagations (0), limit (l) { + random += internal->stats.walk.count; // different seed every time + + // This is the magic constant in ProbSAT (also called 'CB'), which we pick + // according to the average size every second invocation and otherwise + // just the default '2.0', which turns into the base '0.5'. + // + const bool use_size_based_cb = (internal->stats.walk.count & 1); + const double cb = use_size_based_cb ? fitcbval (size) : 2.0; + CADICAL_assert (cb); + const double base = 1 / cb; // scores are 'base^0,base^1,base^2,... + + double next = 1; + for (epsilon = next; next; next = epsilon * base) + table.push_back (epsilon = next); + + PHASE ("walk", internal->stats.walk.count, + "CB %.2f with inverse %.2f as base and table size %zd", cb, base, + table.size ()); +} + +// The scores are tabulated for faster computation (to avoid 'pow'). + +inline double Walker::score (unsigned i) { + const double res = (i < table.size () ? table[i] : epsilon); + LOG ("break %u mapped to score %g", i, res); + return res; +} + +/*------------------------------------------------------------------------*/ + +Clause *Internal::walk_pick_clause (Walker &walker) { + require_mode (WALK); + CADICAL_assert (!walker.broken.empty ()); + int64_t size = walker.broken.size (); + if (size > INT_MAX) + size = INT_MAX; + int pos = walker.random.pick_int (0, size - 1); + Clause *res = walker.broken[pos]; + LOG (res, "picking random position %d", pos); + return res; +} + +/*------------------------------------------------------------------------*/ + +// Compute the number of clauses which would be become unsatisfied if 'lit' +// is flipped and set to false. This is called the 'break-count' of 'lit'. + +unsigned Internal::walk_break_value (int lit) { + + require_mode (WALK); + CADICAL_assert (val (lit) > 0); + + unsigned res = 0; // The computed break-count of 'lit'. + + for (auto &w : watches (lit)) { + CADICAL_assert (w.blit != lit); + if (val (w.blit) > 0) + continue; + if (w.binary ()) { + res++; + continue; + } + + Clause *c = w.clause; + CADICAL_assert (lit == c->literals[0]); + + // Now try to find a second satisfied literal starting at 'literals[1]' + // shifting all the traversed literals to right by one position in order + // to move such a second satisfying literal to 'literals[1]'. This move + // to front strategy improves the chances to find the second satisfying + // literal earlier in subsequent break-count computations. + // + auto begin = c->begin () + 1; + const auto end = c->end (); + auto i = begin; + int prev = 0; + while (i != end) { + const int other = *i; + *i++ = prev; + prev = other; + if (val (other) < 0) + continue; + + // Found 'other' as second satisfying literal. + + w.blit = other; // Update 'blit' + *begin = other; // and move to front. + + break; + } + + if (i != end) + continue; // Double satisfied! + + // Otherwise restore literals (undo shift to the right). + // + while (i != begin) { + const int other = *--i; + *i = prev; + prev = other; + } + + res++; // Literal 'lit' single satisfies clause 'c'. + } + + return res; +} + +/*------------------------------------------------------------------------*/ + +// Given an unsatisfied clause 'c', in which we want to flip a literal, we +// first determine the exponential score based on the break-count of its +// literals and then sample the literals based on these scores. The CB +// value is smaller than one and thus the score is exponentially decreasing +// with the break-count increasing. The sampling works as in 'ProbSAT' and +// 'YalSAT' by summing up the scores and then picking a random limit in the +// range of zero to the sum, then summing up the scores again and picking +// the first literal which reaches the limit. Note, that during incremental +// SAT solving we can not flip assumed variables. Those are assigned at +// decision level one, while the other variables are assigned at two. + +int Internal::walk_pick_lit (Walker &walker, Clause *c) { + LOG ("picking literal by break-count"); + CADICAL_assert (walker.scores.empty ()); + double sum = 0; + int64_t propagations = 0; + for (const auto lit : *c) { + CADICAL_assert (active (lit)); + if (var (lit).level == 1) { + LOG ("skipping assumption %d for scoring", -lit); + continue; + } + CADICAL_assert (active (lit)); + propagations++; + unsigned tmp = walk_break_value (-lit); + double score = walker.score (tmp); + LOG ("literal %d break-count %u score %g", lit, tmp, score); + walker.scores.push_back (score); + sum += score; + } + LOG ("scored %zd literals", walker.scores.size ()); + CADICAL_assert (!walker.scores.empty ()); + walker.propagations += propagations; + stats.propagations.walk += propagations; + CADICAL_assert (walker.scores.size () <= (size_t) c->size); + const double lim = sum * walker.random.generate_double (); + LOG ("score sum %g limit %g", sum, lim); + const auto end = c->end (); + auto i = c->begin (); + auto j = walker.scores.begin (); + int res; + for (;;) { + CADICAL_assert (i != end); + res = *i++; + if (var (res).level > 1) + break; + LOG ("skipping assumption %d without score", -res); + } + sum = *j++; + while (sum <= lim && i != end) { + res = *i++; + if (var (res).level == 1) { + LOG ("skipping assumption %d without score", -res); + continue; + } + sum += *j++; + } + walker.scores.clear (); + LOG ("picking literal %d by break-count", res); + return res; +} + +/*------------------------------------------------------------------------*/ + +void Internal::walk_flip_lit (Walker &walker, int lit) { + + require_mode (WALK); + LOG ("flipping assign %d", lit); + CADICAL_assert (val (lit) < 0); + + // First flip the literal value. + // + const int tmp = sign (lit); + const int idx = abs (lit); + set_val (idx, tmp); + CADICAL_assert (val (lit) > 0); + + // Then remove 'c' and all other now satisfied (made) clauses. + { + // Simply go over all unsatisfied (broken) clauses. + + LOG ("trying to make %zd broken clauses", walker.broken.size ()); + + // We need to measure (and bound) the memory accesses during traversing + // broken clauses in terms of 'propagations'. This is tricky since we + // are not actually propagating literals. Instead we use the clause + // variable 'ratio' as an approximation to the number of clauses used + // during propagating a literal. Note that we use a one-watch scheme. + // Accordingly the number of broken clauses traversed divided by that + // ratio is an approximation of the number of propagation this would + // correspond to (in terms of memory access). To eagerly update these + // statistics we simply increment the propagation counter after every + // 'ratio' traversed clause. These propagations are particularly + // expensive if the number of broken clauses is large which usually + // happens initially. + // + const double ratio = clause_variable_ratio (); + const auto eou = walker.broken.end (); + auto j = walker.broken.begin (), i = j; +#ifdef LOGGING + int64_t made = 0; +#endif + int64_t count = 0; + + while (i != eou) { + + Clause *d = *j++ = *i++; + + int *literals = d->literals, prev = 0; + + // Find 'lit' in 'd'. + // + const int size = d->size; + for (int i = 0; i < size; i++) { + const int other = literals[i]; + CADICAL_assert (active (other)); + literals[i] = prev; + prev = other; + if (other == lit) + break; + CADICAL_assert (val (other) < 0); + } + + // If 'lit' is in 'd' then move it to the front to watch it. + // + if (prev == lit) { + literals[0] = lit; + LOG (d, "made"); + watch_literal (literals[0], literals[1], d); +#ifdef LOGGING + made++; +#endif + j--; + + } else { // Otherwise the clause is not satisfied, undo shift. + + for (int i = size - 1; i >= 0; i--) { + int other = literals[i]; + literals[i] = prev; + prev = other; + } + } + + if (count--) + continue; + + // Update these counters eagerly. Otherwise if we delay the update + // until all clauses are traversed, interrupting the solver has a high + // chance of giving bogus statistics on the number of 'propagations' + // in 'walk', if it is interrupted in this loop. + + count = ratio; // Starting counting down again. + walker.propagations++; + stats.propagations.walk++; + } + LOG ("made %" PRId64 " clauses by flipping %d", made, lit); + walker.broken.resize (j - walker.broken.begin ()); + } + + // Finally add all new unsatisfied (broken) clauses. + { + walker.propagations++; // This really corresponds now to one + stats.propagations.walk++; // propagation (in a one-watch scheme). + +#ifdef LOGGING + int64_t broken = 0; +#endif + Watches &ws = watches (-lit); + + LOG ("trying to break %zd watched clauses", ws.size ()); + + for (const auto &w : ws) { + Clause *d = w.clause; + LOG (d, "unwatch %d in", -lit); + int *literals = d->literals, replacement = 0, prev = -lit; + CADICAL_assert (literals[0] == -lit); + const int size = d->size; + for (int i = 1; i < size; i++) { + const int other = literals[i]; + CADICAL_assert (active (other)); + literals[i] = prev; // shift all to right + prev = other; + const signed char tmp = val (other); + if (tmp < 0) + continue; + replacement = other; // satisfying literal + break; + } + if (replacement) { + literals[1] = -lit; + literals[0] = replacement; + CADICAL_assert (-lit != replacement); + watch_literal (replacement, -lit, d); + } else { + for (int i = size - 1; i > 0; i--) { // undo shift + const int other = literals[i]; + literals[i] = prev; + prev = other; + } + CADICAL_assert (literals[0] == -lit); + LOG (d, "broken"); + walker.broken.push_back (d); +#ifdef LOGGING + broken++; +#endif + } + } + LOG ("broken %" PRId64 " clauses by flipping %d", broken, lit); + ws.clear (); + } +} + +/*------------------------------------------------------------------------*/ + +// Check whether to save the current phases as new global minimum. + +inline void Internal::walk_save_minimum (Walker &walker) { + int64_t broken = walker.broken.size (); + if (broken >= stats.walk.minimum) + return; + VERBOSE (3, "new global minimum %" PRId64 "", broken); + stats.walk.minimum = broken; + for (auto i : vars) { + const signed char tmp = vals[i]; + if (tmp) + phases.min[i] = phases.saved[i] = tmp; + } +} + +/*------------------------------------------------------------------------*/ + +int Internal::walk_round (int64_t limit, bool prev) { + + backtrack (); + if (propagated < trail.size () && !propagate ()) { + LOG ("empty clause after root level propagation"); + learn_empty_clause (); + return 20; + } + + stats.walk.count++; + + clear_watches (); + + // Remove all fixed variables first (assigned at decision level zero). + // + if (last.collect.fixed < stats.all.fixed) + garbage_collection (); + +#ifndef CADICAL_QUIET + // We want to see more messages during initial local search. + // + if (localsearching) { + CADICAL_assert (!force_phase_messages); + force_phase_messages = true; + } +#endif + + PHASE ("walk", stats.walk.count, + "random walk limit of %" PRId64 " propagations", limit); + + // First compute the average clause size for picking the CB constant. + // + double size = 0; + int64_t n = 0; + for (const auto c : clauses) { + if (c->garbage) + continue; + if (c->redundant) { + if (!opts.walkredundant) + continue; + if (!likely_to_be_kept_clause (c)) + continue; + } + size += c->size; + n++; + } + double average_size = relative (size, n); + + PHASE ("walk", stats.walk.count, + "%" PRId64 " clauses average size %.2f over %d variables", n, + average_size, active ()); + + // Instantiate data structures for this local search round. + // + Walker walker (internal, average_size, limit); + + bool failed = false; // Inconsistent assumptions? + + level = 1; // Assumed variables assigned at level 1. + + if (assumptions.empty ()) { + LOG ("no assumptions so assigning all variables to decision phase"); + } else { + LOG ("assigning assumptions to their forced phase first"); + for (const auto lit : assumptions) { + signed char tmp = val (lit); + if (tmp > 0) + continue; + if (tmp < 0) { + LOG ("inconsistent assumption %d", lit); + failed = true; + break; + } + if (!active (lit)) + continue; + tmp = sign (lit); + const int idx = abs (lit); + LOG ("initial assign %d to assumption phase", tmp < 0 ? -idx : idx); + set_val (idx, tmp); + CADICAL_assert (level == 1); + var (idx).level = 1; + } + if (!failed) + LOG ("now assigning remaining variables to their decision phase"); + } + + level = 2; // All other non assumed variables assigned at level 2. + + if (!failed) { + + for (auto idx : vars) { + if (!active (idx)) { + LOG ("skipping inactive variable %d", idx); + continue; + } + if (vals[idx]) { + CADICAL_assert (var (idx).level == 1); + LOG ("skipping assumed variable %d", idx); + continue; + } + int tmp = 0; + if (prev) + tmp = phases.prev[idx]; + if (!tmp) + tmp = sign (decide_phase (idx, true)); + CADICAL_assert (tmp == 1 || tmp == -1); + set_val (idx, tmp); + CADICAL_assert (level == 2); + var (idx).level = 2; + LOG ("initial assign %d to decision phase", tmp < 0 ? -idx : idx); + } + + LOG ("watching satisfied and registering broken clauses"); +#ifdef LOGGING + int64_t watched = 0; +#endif + for (const auto c : clauses) { + + if (c->garbage) + continue; + if (c->redundant) { + if (!opts.walkredundant) + continue; + if (!likely_to_be_kept_clause (c)) + continue; + } + + bool satisfiable = false; // contains not only assumptions + int satisfied = 0; // clause satisfied? + + int *lits = c->literals; + const int size = c->size; + + // Move to front satisfied literals and determine whether there + // is at least one (non-assumed) literal that can be flipped. + // + for (int i = 0; satisfied < 2 && i < size; i++) { + const int lit = lits[i]; + CADICAL_assert (active (lit)); // Due to garbage collection. + if (val (lit) > 0) { + swap (lits[satisfied], lits[i]); + if (!satisfied++) + LOG ("first satisfying literal %d", lit); + } else if (!satisfiable && var (lit).level > 1) { + LOG ("non-assumption potentially satisfying literal %d", lit); + satisfiable = true; + } + } + + if (!satisfied && !satisfiable) { + LOG (c, "due to assumptions unsatisfiable"); + LOG ("stopping local search since assumptions falsify a clause"); + failed = true; + break; + } + + if (satisfied) { + watch_literal (lits[0], lits[1], c); +#ifdef LOGGING + watched++; +#endif + } else { + CADICAL_assert (satisfiable); // at least one non-assumed variable ... + LOG (c, "broken"); + walker.broken.push_back (c); + } + } +#ifdef LOGGING + if (!failed) { + int64_t broken = walker.broken.size (); + int64_t total = watched + broken; + LOG ("watching %" PRId64 " clauses %.0f%% " + "out of %" PRId64 " (watched and broken)", + watched, percent (watched, total), total); + } +#endif + } + + int64_t old_global_minimum = stats.walk.minimum; + + int res; // Tells caller to continue with local search. + + if (!failed) { + + int64_t broken = walker.broken.size (); + + PHASE ("walk", stats.walk.count, + "starting with %" PRId64 " unsatisfied clauses " + "(%.0f%% out of %" PRId64 ")", + broken, percent (broken, stats.current.irredundant), + stats.current.irredundant); + + walk_save_minimum (walker); + + int64_t minimum = broken; +#ifndef CADICAL_QUIET + int64_t flips = 0; +#endif + while (!terminated_asynchronously () && !walker.broken.empty () && + walker.propagations < walker.limit) { +#ifndef CADICAL_QUIET + flips++; +#endif + stats.walk.flips++; + stats.walk.broken += broken; + Clause *c = walk_pick_clause (walker); + const int lit = walk_pick_lit (walker, c); + walk_flip_lit (walker, lit); + broken = walker.broken.size (); + LOG ("now have %" PRId64 " broken clauses in total", broken); + if (broken >= minimum) + continue; + minimum = broken; + VERBOSE (3, "new phase minimum %" PRId64 " after %" PRId64 " flips", + minimum, flips); + walk_save_minimum (walker); + } + + if (minimum < old_global_minimum) + PHASE ("walk", stats.walk.count, + "%snew global minimum %" PRId64 "%s in %" PRId64 " flips and " + "%" PRId64 " propagations", + tout.bright_yellow_code (), minimum, tout.normal_code (), + flips, walker.propagations); + else + PHASE ("walk", stats.walk.count, + "best phase minimum %" PRId64 " in %" PRId64 " flips and " + "%" PRId64 " propagations", + minimum, flips, walker.propagations); + + if (opts.profile >= 2) { + PHASE ("walk", stats.walk.count, + "%.2f million propagations per second", + relative (1e-6 * walker.propagations, + time () - profiles.walk.started)); + + PHASE ("walk", stats.walk.count, "%.2f thousand flips per second", + relative (1e-3 * flips, time () - profiles.walk.started)); + + } else { + PHASE ("walk", stats.walk.count, "%.2f million propagations", + 1e-6 * walker.propagations); + + PHASE ("walk", stats.walk.count, "%.2f thousand flips", 1e-3 * flips); + } + + if (minimum > 0) { + LOG ("minimum %" PRId64 " non-zero thus potentially continue", + minimum); + res = 0; + } else { + LOG ("minimum is zero thus stop local search"); + res = 10; + } + + } else { + + res = 20; + + PHASE ("walk", stats.walk.count, + "aborted due to inconsistent assumptions"); + } + + copy_phases (phases.prev); + + for (auto idx : vars) + if (active (idx)) + set_val (idx, 0); + + CADICAL_assert (level == 2); + level = 0; + + clear_watches (); + connect_watches (); + +#ifndef CADICAL_QUIET + if (localsearching) { + CADICAL_assert (force_phase_messages); + force_phase_messages = false; + } +#endif + + return res; +} + +void Internal::walk () { + START_INNER_WALK (); + int64_t limit = stats.propagations.search; + limit *= 1e-3 * opts.walkeffort; + if (limit < opts.walkmineff) + limit = opts.walkmineff; + if (limit > opts.walkmaxeff) + limit = opts.walkmaxeff; + (void) walk_round (limit, false); + STOP_INNER_WALK (); +} + +} // namespace CaDiCaL + +ABC_NAMESPACE_IMPL_END diff --git a/src/sat/cadical/cadical_watch.cpp b/src/sat/cadical/cadical_watch.cpp new file mode 100644 index 0000000000..84c51ef2c5 --- /dev/null +++ b/src/sat/cadical/cadical_watch.cpp @@ -0,0 +1,132 @@ +#include "global.h" + +#include "internal.hpp" + +ABC_NAMESPACE_IMPL_START + +namespace CaDiCaL { + +void Internal::init_watches () { + CADICAL_assert (wtab.empty ()); + if (wtab.size () < 2 * vsize) + wtab.resize (2 * vsize, Watches ()); + LOG ("initialized watcher tables"); +} + +void Internal::clear_watches () { + for (auto lit : lits) + watches (lit).clear (); +} + +void Internal::reset_watches () { + CADICAL_assert (!wtab.empty ()); + erase_vector (wtab); + LOG ("reset watcher tables"); +} + +// This can be quite costly since lots of memory is accessed in a rather +// random fashion, and thus we optionally profile it. + +void Internal::connect_watches (bool irredundant_only) { + START (connect); + CADICAL_assert (watching ()); + + LOG ("watching all %sclauses", irredundant_only ? "irredundant " : ""); + + // First connect binary clauses. + // + for (const auto &c : clauses) { + if (irredundant_only && c->redundant) + continue; + if (c->garbage || c->size > 2) + continue; + watch_clause (c); + } + + // Then connect non-binary clauses. + // + for (const auto &c : clauses) { + if (irredundant_only && c->redundant) + continue; + if (c->garbage || c->size == 2) + continue; + watch_clause (c); + if (!level) { + const int lit0 = c->literals[0]; + const int lit1 = c->literals[1]; + const signed char tmp0 = val (lit0); + const signed char tmp1 = val (lit1); + if (tmp0 > 0) + continue; + if (tmp1 > 0) + continue; + if (tmp0 < 0) { + const size_t pos0 = var (lit0).trail; + if (pos0 < propagated) { + propagated = pos0; + LOG ("literal %d resets propagated to %zd", lit0, pos0); + } + } + if (tmp1 < 0) { + const size_t pos1 = var (lit1).trail; + if (pos1 < propagated) { + propagated = pos1; + LOG ("literal %d resets propagated to %zd", lit1, pos1); + } + } + } + } + + STOP (connect); +} + +// This can be quite costly since lots of memory is accessed in a rather +// random fashion, and thus we optionally profile it. + +void Internal::connect_binary_watches () { + START (connect); + CADICAL_assert (watching ()); + + LOG ("watching binary clauses"); + + // First connect binary clauses. + // + for (const auto &c : clauses) { + if (c->garbage || c->size > 2) + continue; + watch_clause (c); + } + + STOP (connect); +} + +void Internal::sort_watches () { + CADICAL_assert (watching ()); + LOG ("sorting watches"); + Watches saved; + for (auto lit : lits) { + Watches &ws = watches (lit); + + const const_watch_iterator end = ws.end (); + watch_iterator j = ws.begin (); + const_watch_iterator i; + + CADICAL_assert (saved.empty ()); + + for (i = j; i != end; i++) { + const Watch w = *i; + if (w.binary ()) + *j++ = w; + else + saved.push_back (w); + } + + std::copy (saved.cbegin (), saved.cend (), j); + + saved.clear (); + } +} + +} // namespace CaDiCaL + +ABC_NAMESPACE_IMPL_END diff --git a/src/sat/cadical/ccadical.h b/src/sat/cadical/ccadical.h new file mode 100644 index 0000000000..ec5292a79d --- /dev/null +++ b/src/sat/cadical/ccadical.h @@ -0,0 +1,74 @@ +#ifndef _ccadical_h_INCLUDED +#define _ccadical_h_INCLUDED + +#include "global.h" + +/*------------------------------------------------------------------------*/ +ABC_NAMESPACE_HEADER_START +/*------------------------------------------------------------------------*/ + +#include <stdint.h> +#include <stdio.h> + +// C wrapper for CaDiCaL's C++ API following IPASIR. + +typedef struct CCaDiCaL CCaDiCaL; + +const char *ccadical_signature (void); +CCaDiCaL *ccadical_init (void); +void ccadical_release (CCaDiCaL *); + +void ccadical_add (CCaDiCaL *, int lit); +void ccadical_assume (CCaDiCaL *, int lit); +int ccadical_solve (CCaDiCaL *); +int ccadical_val (CCaDiCaL *, int lit); +int ccadical_failed (CCaDiCaL *, int lit); + +void ccadical_set_terminate (CCaDiCaL *, void *state, + int (*terminate) (void *state)); + +void ccadical_set_learn (CCaDiCaL *, void *state, int max_length, + void (*learn) (void *state, int *clause)); + +/*------------------------------------------------------------------------*/ + +// Non-IPASIR conformant 'C' functions. + +void ccadical_constrain (CCaDiCaL *, int lit); +int ccadical_constraint_failed (CCaDiCaL *); +void ccadical_set_option (CCaDiCaL *, const char *name, int val); +void ccadical_limit (CCaDiCaL *, const char *name, int limit); +int ccadical_get_option (CCaDiCaL *, const char *name); +void ccadical_print_statistics (CCaDiCaL *); +int64_t ccadical_active (CCaDiCaL *); +int64_t ccadical_irredundant (CCaDiCaL *); +int ccadical_fixed (CCaDiCaL *, int lit); +int ccadical_trace_proof (CCaDiCaL *, FILE *, const char *); +void ccadical_close_proof (CCaDiCaL *); +void ccadical_conclude (CCaDiCaL *); +void ccadical_terminate (CCaDiCaL *); +void ccadical_freeze (CCaDiCaL *, int lit); +int ccadical_frozen (CCaDiCaL *, int lit); +void ccadical_melt (CCaDiCaL *, int lit); +int ccadical_simplify (CCaDiCaL *); +int ccadical_vars (CCaDiCaL *); +int ccadical_reserve_difference (CCaDiCaL *, int number_of_vars); + +// Extra + +void ccadical_reserve(CCaDiCaL *, int min_max_var); +int ccadical_is_inconsistent(CCaDiCaL *); + +/*------------------------------------------------------------------------*/ + +// Support legacy names used before moving to more IPASIR conforming names. + +#define ccadical_reset ccadical_release +#define ccadical_sat ccadical_solve +#define ccadical_deref ccadical_val + +/*------------------------------------------------------------------------*/ +ABC_NAMESPACE_HEADER_END +/*------------------------------------------------------------------------*/ + +#endif diff --git a/src/sat/cadical/checker.hpp b/src/sat/cadical/checker.hpp new file mode 100644 index 0000000000..8c351e3269 --- /dev/null +++ b/src/sat/cadical/checker.hpp @@ -0,0 +1,178 @@ +#ifndef _checker_hpp_INCLUDED +#define _checker_hpp_INCLUDED + +#include "global.h" + +#include "tracer.hpp" // Alphabetically after 'checker'. + +#include <cstdint> + +ABC_NAMESPACE_CXX_HEADER_START + +namespace CaDiCaL { + +/*------------------------------------------------------------------------*/ + +// This checker implements an online forward DRUP proof checker enabled by +// 'opts.checkproof' (requires 'opts.check' also to be enabled). This is +// useful for model basted testing (and delta-debugging), where we can not +// rely on an external proof checker such as 'drat-trim'. We also do not +// have yet a flow for offline incremental proof checking, while this +// checker here can also be used in an incremental setting. +// +// In essence the checker implements is a simple propagation online SAT +// solver with an additional hash table to find clauses fast for +// 'delete_clause'. It requires its own data structure for clauses +// ('CheckerClause') and watches ('CheckerWatch'). +// +// In our experiments the checker slows down overall SAT solving time by a +// factor of 3, which we contribute to its slightly less efficient +// implementation. + +/*------------------------------------------------------------------------*/ + +struct CheckerClause { + CheckerClause *next; // collision chain link for hash table + uint64_t hash; // previously computed full 64-bit hash + unsigned size; // zero if this is a garbage clause + int literals[2]; // otherwise 'literals' of length 'size' +}; + +struct CheckerWatch { + int blit; + unsigned size; + CheckerClause *clause; + CheckerWatch () {} + CheckerWatch (int b, CheckerClause *c) + : blit (b), size (c->size), clause (c) {} +}; + +typedef vector<CheckerWatch> CheckerWatcher; + +/*------------------------------------------------------------------------*/ + +class Checker : public StatTracer { + + Internal *internal; + + // Capacity of variable values. + // + int64_t size_vars; + + // For the assignment we want to have an as fast access as possible and + // thus we use an array which can also be indexed by negative literals and + // is actually valid in the range [-size_vars+1, ..., size_vars-1]. + // + signed char *vals; + + // The 'watchers' and 'marks' data structures are not that time critical + // and thus we access them by first mapping a literal to 'unsigned'. + // + static unsigned l2u (int lit); + vector<CheckerWatcher> watchers; // watchers of literals + vector<signed char> marks; // mark bits of literals + + signed char &mark (int lit); + CheckerWatcher &watcher (int lit); + + bool inconsistent; // found or added empty clause + + uint64_t num_clauses; // number of clauses in hash table + uint64_t num_garbage; // number of garbage clauses + uint64_t size_clauses; // size of clause hash table + CheckerClause **clauses; // hash table of clauses + CheckerClause *garbage; // linked list of garbage clauses + + vector<int> unsimplified; // original clause for reporting + vector<int> simplified; // clause for sorting + + vector<int> trail; // for propagation + + unsigned next_to_propagate; // next to propagate on trail + + void enlarge_vars (int64_t idx); + void import_literal (int lit); + void import_clause (const vector<int> &); + bool tautological (); + + static const unsigned num_nonces = 4; + + uint64_t nonces[num_nonces]; // random numbers for hashing + uint64_t last_hash; // last computed hash value of clause + int64_t last_id; + uint64_t compute_hash (); // compute and save hash value of clause + + // Reduce hash value to the actual size. + // + static uint64_t reduce_hash (uint64_t hash, uint64_t size); + + void enlarge_clauses (); // enlarge hash table for clauses + void insert (); // insert clause in hash table + CheckerClause **find (); // find clause position in hash table + + void add_clause (const char *type); + + void collect_garbage_clauses (); + + CheckerClause *new_clause (); + void delete_clause (CheckerClause *); + + signed char val (int lit); // returns '-1', '0' or '1' + + bool clause_satisfied (CheckerClause *); + + void assign (int lit); // assign a literal to true + void assume (int lit); // assume a literal + bool propagate (); // propagate and check for conflicts + void backtrack (unsigned); // prepare for next clause + bool check (); // check simplified clause is implied + bool check_blocked (); // check if clause is blocked + + struct { + + int64_t added; // number of added clauses + int64_t original; // number of added original clauses + int64_t derived; // number of added derived clauses + + int64_t deleted; // number of deleted clauses + + int64_t assumptions; // number of assumed literals + int64_t propagations; // number of propagated literals + + int64_t insertions; // number of clauses added to hash table + int64_t collisions; // number of hash collisions in 'find' + int64_t searches; // number of searched clauses in 'find' + + int64_t checks; // number of implication checks + + int64_t collections; // garbage collections + int64_t units; + + } stats; + +public: + Checker (Internal *); + virtual ~Checker (); + + void connect_internal (Internal *i) override; + + void add_original_clause (int64_t, bool, const vector<int> &, + bool = false) override; + void add_derived_clause (int64_t, bool, const vector<int> &, + const vector<int64_t> &) override; + void delete_clause (int64_t, bool, const vector<int> &) override; + + void finalize_clause (int64_t, const vector<int> &) override {} // skip + void report_status (int, int64_t) override {} // skip + void begin_proof (int64_t) override {} // skip + void add_assumption_clause (int64_t, const vector<int> &, + const vector<int64_t> &) override; + void print_stats () override; + void dump (); // for debugging purposes only +}; + +} // namespace CaDiCaL + +ABC_NAMESPACE_CXX_HEADER_END + +#endif diff --git a/src/sat/cadical/clause.hpp b/src/sat/cadical/clause.hpp new file mode 100644 index 0000000000..e5d0290f63 --- /dev/null +++ b/src/sat/cadical/clause.hpp @@ -0,0 +1,194 @@ +#ifndef _clause_hpp_INCLUDED +#define _clause_hpp_INCLUDED + +#include "global.h" + +#include "util.hpp" +#include <climits> +#include <cstdint> +#include <cstdlib> + +ABC_NAMESPACE_CXX_HEADER_START + +namespace CaDiCaL { + +/*------------------------------------------------------------------------*/ + +typedef int *literal_iterator; +typedef const int *const_literal_iterator; + +/*------------------------------------------------------------------------*/ + +// The 'Clause' data structure is very important. There are usually many +// clauses and accessing them is a hot-spot. Thus we use common +// optimizations to reduce memory and improve cache usage, even though this +// induces some complexity in understanding the code. +// +// The most important optimization is to 'embed' the actual literals in the +// clause. This requires a variadic size structure and thus strictly is not +// 'C' conform, but supported by all compilers we used. The alternative is +// to store the actual literals somewhere else, which not only needs more +// memory but more importantly also requires another memory access and thus +// is very costly. + +struct Clause { + union { + int64_t id; // Used to create LRAT-style proofs + Clause *copy; // Only valid if 'moved', then that's where to. + // + // The 'copy' field is only valid for 'moved' clauses in the moving + // garbage collector 'copy_non_garbage_clauses' for keeping clauses + // compactly in a contiguous memory arena. Otherwise, so almost all of + // the time, 'id' is valid. See 'collect.cpp' for details. + }; + bool conditioned : 1; // Tried for globally blocked clause elimination. + bool covered : 1; // Already considered for covered clause elimination. + bool enqueued : 1; // Enqueued on backward queue. + bool frozen : 1; // Temporarily frozen (in covered clause elimination). + bool garbage : 1; // can be garbage collected unless it is a 'reason' + bool gate : 1; // Clause part of a gate (function definition). + bool hyper : 1; // redundant hyper binary or ternary resolved + bool instantiated : 1; // tried to instantiate + bool moved : 1; // moved during garbage collector ('copy' valid) + bool reason : 1; // reason / antecedent clause can not be collected + bool redundant : 1; // aka 'learned' so not 'irredundant' (original) + bool transred : 1; // already checked for transitive reduction + bool subsume : 1; // not checked in last subsumption round + bool swept : 1; // clause used to sweep equivalences + bool flushed : 1; // garbage in proof deleted binaries + unsigned used : 8; // resolved in conflict analysis since last 'reduce' + bool vivified : 1; // clause already vivified + bool vivify : 1; // clause scheduled to be vivified + + // The glucose level ('LBD' or short 'glue') is a heuristic value for the + // expected usefulness of a learned clause, where smaller glue is consider + // more useful. During learning the 'glue' is determined as the number of + // decisions in the learned clause. Thus the glue of a clause is a strict + // upper limit on the smallest number of decisions needed to make it + // propagate. For instance a binary clause will propagate if one of its + // literals is set to false. Similarly a learned clause with glue 1 can + // propagate after one decision, one with glue 2 after 2 decisions etc. + // In some sense the glue is an abstraction of the size of the clause. + // + // See the IJCAI'09 paper by Audemard & Simon for more details. We + // switched back and forth between keeping the glue stored in a clause and + // using it only initially to determine whether it is kept, that is + // survives clause reduction. The latter strategy is not bad but also + // does not allow to use glue values for instance in 'reduce'. + // + // More recently we also update the glue and promote clauses to lower + // level tiers during conflict analysis. The idea of using three tiers is + // also due to Chanseok Oh and thus used in all recent 'Maple...' solvers. + // Tier one are the always kept clauses with low glue at most + // 'opts.reducetier1glue' (default '2'). The second tier contains all + // clauses with glue larger than 'opts.reducetier1glue' but smaller or + // equal than 'opts.reducetier2glue' (default '6'). The third tier + // consists of clauses with glue larger than 'opts.reducetier2glue'. + // + // Clauses in tier one are not deleted in 'reduce'. Clauses in tier + // two require to be unused in two consecutive 'reduce' intervals before + // being collected while for clauses in tier three not being used since + // the last 'reduce' call makes them deletion candidates. Clauses derived + // by hyper binary or ternary resolution (even though small and thus with + // low glue) are always removed if they remain unused during one interval. + // See 'mark_useless_redundant_clauses_as_garbage' in 'reduce.cpp' and + // 'bump_clause' in 'analyze.cpp'. + // + int glue; + + int size; // Actual size of 'literals' (at least 2). + int pos; // Position of last watch replacement [Gent'13]. + + // This 'flexible array member' is of variadic 'size' (and actually + // shrunken if strengthened) and keeps the literals close to the header of + // the clause to avoid another pointer dereference, which would be costly. + + // In earlier versions we used 'literals[2]' to fake it (in order to + // support older Microsoft compilers even though this feature is in C99) + // and at the same time being able to overlay the first two literals with + // the 'copy' field above, as having a flexible array member inside a + // union is not allowed. Now compilers start to figure out that those + // literals can be accessed with indices larger than 1 and produce + // warnings. After having the 'id' field mandatory we now overlay that + // one with the copy field. + + // However, it turns out that even though flexible array members are in + // C99 they are not in C11++, and therefore pedantic compilation with + // '--pedantic' fails completely. Therefore we still support as + // alternative faked flexible array members, which unfortunately need + // then again more care when accessing the literals outside the faked + // virtual sizes and the compiler can somehow figure that out, because + // that would in turn produce a warning. + +#ifndef NFLEXIBLE + int literals[]; +#else + int literals[2]; +#endif + + // Supports simple range based for loops over clauses. + + literal_iterator begin () { return literals; } + literal_iterator end () { return literals + size; } + + const_literal_iterator begin () const { return literals; } + const_literal_iterator end () const { return literals + size; } + + static size_t bytes (int size) { + + // Memory sanitizer insists that clauses put into consecutive memory in + // the arena are still 8 byte aligned. We could also allocate 8 byte + // aligned memory there. However, assuming the real memory foot print + // of a clause is 8 bytes anyhow, we just allocate 8 byte aligned memory + // all the time (even if allocated outside of the arena). + // + CADICAL_assert (size > 1); + const size_t header_bytes = sizeof (Clause); + const size_t actual_literal_bytes = size * sizeof (int); + size_t combined_bytes = header_bytes + actual_literal_bytes; +#ifdef NFLEXIBLE + const size_t faked_literals_bytes = sizeof ((Clause *) 0)->literals; + combined_bytes -= faked_literals_bytes; +#endif + size_t aligned_bytes = align (combined_bytes, 8); + return aligned_bytes; + } + + size_t bytes () const { return bytes (size); } + + // Check whether this clause is ready to be collected and deleted. The + // 'reason' flag is only there to protect reason clauses in 'reduce', + // which does not backtrack to the root level. If garbage collection is + // triggered from a preprocessor, which backtracks to the root level, then + // 'reason' is false for sure. We want to use the same garbage collection + // code though for both situations and thus hide here this variance. + // + bool collect () const { return !reason && garbage; } +}; + +struct clause_smaller_size { + bool operator() (const Clause *a, const Clause *b) { + return a->size < b->size; + } +}; + +/*------------------------------------------------------------------------*/ + +// Place literals over the same variable close to each other. This would +// allow eager removal of identical literals and detection of tautological +// clauses but is only currently used for better logging (see also +// 'opts.logsort' in 'logging.cpp'). + +struct clause_lit_less_than { + bool operator() (int a, int b) const { + using namespace std; + int s = abs (a), t = abs (b); + return s < t || (s == t && a < b); + } +}; + +} // namespace CaDiCaL + +ABC_NAMESPACE_CXX_HEADER_END + +#endif diff --git a/src/sat/cadical/config.hpp b/src/sat/cadical/config.hpp new file mode 100644 index 0000000000..d49b05c840 --- /dev/null +++ b/src/sat/cadical/config.hpp @@ -0,0 +1,26 @@ +#ifndef _config_hpp_INCLUDED +#define _config_hpp_INCLUDED + +#include "global.h" + +ABC_NAMESPACE_CXX_HEADER_START + +namespace CaDiCaL { + +class Options; + +struct Config { + + static bool has (const char *); + static bool set (Options &, const char *); + static void usage (); + + static const char **begin (); + static const char **end (); +}; + +} // namespace CaDiCaL + +ABC_NAMESPACE_CXX_HEADER_END + +#endif diff --git a/src/sat/cadical/congruence.hpp b/src/sat/cadical/congruence.hpp new file mode 100644 index 0000000000..e0acf9b00a --- /dev/null +++ b/src/sat/cadical/congruence.hpp @@ -0,0 +1,720 @@ +#ifndef _congruenc_hpp_INCLUDED +#define _congruenc_hpp_INCLUDED + +#include "global.h" + +#include <algorithm> +#include <array> +#include <cassert> +#include <cstddef> +#include <cstdint> +#include <queue> +#include <string> +#include <sys/types.h> +#include <unordered_set> +#include <vector> + +#include "clause.hpp" +#include "inttypes.hpp" +#include "util.hpp" +#include "watch.hpp" + +ABC_NAMESPACE_CXX_HEADER_START + +namespace CaDiCaL { + +typedef int64_t LRAT_ID; + +// This implements the algorithm algorithm from SAT 2024. +// +// The idea is to: +// 0. handle binary clauses +// 1. detect gates and merge gates with same inputs ('lazy') +// 2. eagerly replace the equivalent literals and merge gates with same +// inputs +// 3. forward subsume +// +// In step 0 the normalization is fully lazy but we do not care about a +// normal form. Therefore we actually eagerly merge literals. +// +// In step 2 there is a subtility: we only replace with the equivalence +// chain as far as we propagated so far. This is the eager part. For LRAT we +// produce the equivalence up to the point we have propagated, no the full +// chain. This is important for merging literals. To merge literals we use +// union-find but we only compress paths when rewriting the literal, not +// before. The compression was not considered important in Kissat, but we do +// it aggressively as a mirror of the equivalences we have generated. +// +// We have two structures for merging: +// - the lazy ones contains alls merges, with functions like +// find_representative +// +// - the eager version that gets the merges one by one, with functions +// like find_eager_representatives +// +// The two structures are nicely separated and we only working on one of +// them except for: +// +// 1. When propagating one equivalence, we first important the +// equivalence from the lazy to the eager version, producing the full +// chain. +// +// 2. When merging the literals, we merge the literals given by the lazy +// structure, then we merge their representative in the eager version, +// updating only the lazy structure. We do not update the eager version. +// +// An important point: We cannot use internal->lrat_chain and +// internal->clause because in most places we can interrupt the +// transformation to learn a new clause representing an equivalence. +// However, we can only have 2 layers so we use this->lrat_chain and +// internal->lrat_chain when we really produce the proof. +struct Internal; + +#define LD_MAX_ARITY 26 +#define MAX_ARITY ((1 << LD_MAX_ARITY) - 1) + +enum class Gate_Type { And_Gate, XOr_Gate, ITE_Gate }; + +// Wrapper when we are looking for implication in if-then-else gates +struct lit_implication { + int first; + int second; + Clause *clause; + lit_implication (int f, int s, Clause *_id) + : first (f), second (s), clause (_id) {} + lit_implication (int f, int s) : first (f), second (s), clause (0) {} + lit_implication () : first (0), second (0), clause (nullptr) {} + void swap () { std::swap (first, second); } +}; + +// Wrapper when we are looking for equivalence for if-then-else-gate. They +// are produced by merging implication +struct lit_equivalence { + int first; + int second; + Clause *first_clause; + Clause *second_clause; + void check_invariant () { + CADICAL_assert (second_clause); + CADICAL_assert (first_clause); + CADICAL_assert (std::find (begin (*first_clause), end (*first_clause), first) != + end (*first_clause)); + CADICAL_assert (std::find (begin (*second_clause), end (*second_clause), + second) != end (*second_clause)); + CADICAL_assert (std::find (begin (*first_clause), end (*first_clause), + -second) != end (*first_clause)); + CADICAL_assert (std::find (begin (*second_clause), end (*second_clause), + -first) != end (*second_clause)); + } + lit_equivalence (int f, Clause *f_id, int s, Clause *s_id) + : first (f), second (s), first_clause (f_id), second_clause (s_id) {} + lit_equivalence (int f, int s) + : first (f), second (s), first_clause (nullptr), + second_clause (nullptr) {} + lit_equivalence () + : first (0), second (0), first_clause (nullptr), + second_clause (nullptr) {} + lit_equivalence swap () { + std::swap (first, second); + std::swap (first_clause, second_clause); + return *this; + } + lit_equivalence negate_both () { + first = -first; + second = -second; + std::swap (first_clause, second_clause); + return *this; + } +}; + +typedef std::vector<lit_implication> lit_implications; +typedef std::vector<lit_equivalence> lit_equivalences; + +std::string string_of_gate (Gate_Type t); + +struct LitClausePair { + int current_lit; // current literal from the gate + Clause *clause; + LitClausePair (int lit, Clause *cl) : current_lit (lit), clause (cl) {} + LitClausePair () : current_lit (0), clause (nullptr) {} +}; +struct LitIdPair { + int lit; // current literal from the gate + LRAT_ID id; + LitIdPair (int l, LRAT_ID i) : lit (l), id (i) {} + LitIdPair () : lit (0), id (0) {} +}; + +/*------------------------------------------------------------------------*/ + +// Sorting the scheduled clauses is way faster if we compute and save the +// clause size in the schedule to avoid pointer access to clauses during +// sorting. This slightly increases the schedule size though. + +struct ClauseSize { + size_t size; + Clause *clause; + ClauseSize (int s, Clause *c) : size (s), clause (c) {} + ClauseSize (Clause *c): size (c->size), clause (c) {} + ClauseSize () {} +}; + +struct smaller_clause_size_rank { + typedef size_t Type; + Type operator() (const ClauseSize &a) { return a.size; } +}; + +/*------------------------------------------------------------------------*/ +// There are many special cases for ITE gates and we have to keep track of +// them as it is a gate property (rewriting might not make it obvious +// anymore). +// a = (a ? t : e) results in no -t and no +e gate (a --> a = t == (-a v -a v t) & (-a v a v -t)) +// a = (-a ? t : e) results in no +t and no -e gate +// a = (c ? a : e) results in no t gate (none of them) +// a = (c ? t : a) results in no e gate (none of them) + +enum Special_ITE_GATE { + NORMAL = 0, + NO_PLUS_THEN = (1 << 0), + NO_NEG_THEN = (1 << 1), + NO_THEN = NO_PLUS_THEN + NO_NEG_THEN, + NO_PLUS_ELSE = (1 << 2), + NO_NEG_ELSE = (1 << 3), + NO_ELSE = NO_PLUS_ELSE + NO_NEG_ELSE, + COND_LHS = NO_NEG_THEN + NO_PLUS_ELSE, + UCOND_LHS = NO_PLUS_THEN + NO_NEG_ELSE, +}; + +inline bool ite_flags_no_then_clauses (int8_t flag) { + return (flag & NO_THEN) == NO_THEN; +} + +inline bool ite_flags_no_else_clauses (int8_t flag) { + return (flag & NO_ELSE) == NO_ELSE; +} + +inline bool ite_flags_neg_cond_lhs (int8_t flag) { + return (flag & UCOND_LHS) == UCOND_LHS; +} + +inline bool ite_flags_cond_lhs (int8_t flag) { + return (flag & COND_LHS) == COND_LHS; +} + +/*------------------------------------------------------------------------*/ + +// The core structure of this algorithm: the gate. It is composed of a +// left-hand side and an array of right-hand side. +// +// There are a few tags to help remembering the status of the gate (like +// deleted) +// +// To keep track of the proof we use two extra arrays: +// - `neg_lhs_ids' contains the long clause for AND gates. Otherwise, it is +// empty. TODO: change to std::option as it contains at most one element +// - `pos_lhs_ids' contains all the remaining gates. +// +// We keep the reasons with an index. This index depends on the gates: + +// - AND-Gates and ITE-Gates: the index is the literal from the RHS +// +// - XOR-Gates: if you order the clauses by the order of the literals, +// each literal is either positive (bit '1') or negative (bit '0'). This +// gives a number that we can use. +// +// TODO Florian: I do not think that you have to changed anything, look at +// the 'Look at this first' in the CPP file. +// +// Important for the proofs: the LHS is not updated. +// +// TODO: we currently use a vector for the rhs, but we could also use FMA +// and inline the structure to avoid any indirection. +// +// One warning for degenerated gate: it is a monotone property on the +// defining clauses, but not on the LHS/RHS as the LHS is not rewritten: +// take 4 = AND 3 4 (degenerated with only the clause -4 3) with a rewriting +// 4 -> 1 (unchanged clause) and later 1 -> 3 (unchanged clause) but you do +// not know anymore from the gate that it is degenerated +struct Gate { +#ifdef LOGGING + uint64_t id; +#endif + int lhs; + Gate_Type tag; + bool garbage : 1; + bool indexed : 1; + bool marked : 1; + bool shrunken : 1; + size_t hash; // TODO remove this field (the C++ implementation is caching + // it anyway) + vector<LitClausePair> pos_lhs_ids; + vector<LitClausePair> neg_lhs_ids; + bool degenerated_and_neg = false; // LRAT only relevant for AND Gates, neg lhs in RHS + bool degenerated_and_pos = false; // LRAT only relevant for AND Gates, pos lhs in RHS + int8_t degenerated_ite = Special_ITE_GATE::NORMAL; + vector<int> rhs; + + size_t arity () const { return rhs.size (); } + + bool operator== (Gate const &lhs) { + return tag == lhs.tag && hash == lhs.hash && rhs == lhs.rhs; + } +}; + +typedef vector<Gate *> GOccs; + +struct GateEqualTo { + bool operator() (const Gate *const lhs, const Gate *const rhs) const { + return lhs->rhs == rhs->rhs && lhs->tag == rhs->tag; + } +}; + +struct CompactBinary { + Clause *clause; + LRAT_ID id; + int lit1, lit2; + CompactBinary (Clause *c, LRAT_ID i, int l1, int l2) + : clause (c), id (i), lit1 (l1), lit2 (l2) {} + CompactBinary () : clause (nullptr), id (0), lit1 (0), lit2 (0) {} +}; + +struct Hash { + Hash (std::array<int, 16> &ncs) : nonces (ncs) {} + std::array<int, 16> &nonces; + size_t operator() (const Gate *const g) const; +}; + +struct Rewrite { + int src, dst; + LRAT_ID id1; + LRAT_ID id2; + + Rewrite (int _src, int _dst, LRAT_ID _id1, LRAT_ID _id2) + : src (_src), dst (_dst), id1 (_id1), id2 (_id2) {} + Rewrite () : src (0), dst (0), id1 (0), id2 (0) {} +}; + +struct Closure { + + Closure (Internal *i); + + Internal *const internal; + vector<Clause*> extra_clauses; + vector<CompactBinary> binaries; + std::vector<std::pair<size_t, size_t>> offsetsize; + bool full_watching = false; + std::array<int, 16> nonces; + typedef unordered_set<Gate *, Hash, GateEqualTo> GatesTable; + + vector<bool> scheduled; + vector<signed char> marks; + vector<LitClausePair> mu1_ids, mu2_ids, + mu4_ids; // remember the ids and the literal. 2 and 4 are + // only used for lrat proofs, but we need 1 to + // promote binary clauses to irredundant + + vector<int> lits; // result of definitions + vector<int> rhs; // stack for storing RHS + vector<int> unsimplified; // stack for storing unsimplified version (XOR, + // ITEs) for DRAT proof + vector<int> chain; // store clauses to be able to delete them properly + vector<int> clause; // storing partial clauses + vector<uint64_t> + glargecounts; // count for large clauses to complement internal->noccs + vector<uint64_t> gnew_largecounts; // count for large clauses to + // complement internal->noccs + GatesTable table; + std::array<lit_implications, 2> condbin; + std::array<lit_equivalences, 2> condeq; + + std::vector<Clause *> new_unwatched_binary_clauses; + // LRAT proofs + vector<int> resolvent_analyzed; + mutable vector<LRAT_ID> lrat_chain; // storing LRAT chain + +#ifdef LOGGING + uint64_t fresh_id; +#endif + + uint64_t &new_largecounts (int lit); + uint64_t &largecounts (int lit); + + void unmark_all (); + vector<int> representant; // union-find + vector<int> eager_representant; // union-find + vector<LRAT_ID> representant_id; // lrat version of union-find + vector<LRAT_ID> eager_representant_id; // lrat version of union-find + int &representative (int lit); + int representative (int lit) const; + LRAT_ID &representative_id (int lit); + LRAT_ID representative_id (int lit) const; + int &eager_representative (int lit); + int eager_representative (int lit) const; + LRAT_ID &eager_representative_id (int lit); + LRAT_ID eager_representative_id (int lit) const; + std::vector<char> lazy_propagated_idx; + char &lazy_propagated (int lit); + + int find_lrat_representative_with_marks (int lit); + // representative in the union-find structure in the lazy equivalences + int find_representative (int lit); + // find the representative and produce the binary clause representing the + // normalization from the literal to the result. + int find_representative_and_compress (int, bool update_eager = true); + // find the lazy representative for the `lit' and `-lit' + void find_representative_and_compress_both (int); + // find the eager representative + int find_eager_representative (int); + + // compreses the path from lit to the representative with a new clause if + // needed. Save internal->lrat_chain to avoid any issue. + int find_eager_representative_and_compress (int); + // Import the path from the literal and its negation to the representative + // in the lazy graph to the eager part, producing the binary clauses. + void import_lazy_and_find_eager_representative_and_compress_both ( + int); // generates clauses for -lit and lit + + // returns the ID of the LRAT clause for the normalization from the + // literal lit to its argument, assuming that the representative was + // already compressed. + LRAT_ID find_representative_lrat (int lit); + // returns the ID of the LRAT clause for the eager normalization from the + // literal lit to its argument assuming that the representative was + // already compressed. + LRAT_ID find_eager_representative_lrat (int lit); + + // Writes the LRAT chain required for the eager normalization to + // `lrat_chain`. + void produce_eager_representative_lrat (int lit); + // Writes the LRAT chain required for the lazy normalization to + // `lrat_chain`. + void produce_representative_lrat (int lit); + + // learns a binary clause if not unit + Clause *maybe_add_binary_clause (int a, int b); + // add binary clause + Clause *add_binary_clause (int a, int b); + // add tmp clause + Clause *add_tmp_binary_clause (int a, int b); + // add clause taking core of tmp or full + Clause *learn_binary_tmp_or_full_clause (int a, int b); + + // promotes a clause from redundant to irredundant. We do this for all + // clauses involved in gates to make sure that we produce correct result. + void promote_clause (Clause *); + + // Merge functions. We actually need different several versions for LRAT + // in order to simplify the proof production. + // + // When merging binary clauses, we can simply produce the LRAT chain by + // (1) using the two binary clauses and (2) the reason clause from the + // literals to the representatives. + // + // The same approach does not work for merging gates because the + // representative might be also a representative of another literal + // (because of eager rewriting), requiring to resolve more than once on + // the same literal. An example of this are the two gates 4=-2&7 and + // 6=-2&1, the rewriting 7=1 and the equivalence 4=1. The simple road of + // merging 6 and 4 (requires resolving away 1) + adding the rewrite 4 to 1 + // (requires adding 1) does not work. + // + // Therefore, we actually go for the more regular road and produce two + // equivalence: the merge from the LHS, followed by the actual equivalence + // (by combining it with the rewrite). In DRAT this is less important + // because the checker finds a chain and is less restricted than our LRAT + // chain. + bool merge_literals_equivalence (int lit, int other, Clause *c1, + Clause *c2); + bool merge_literals_lrat (Gate *g, Gate *h, int lit, int other, + const std::vector<LRAT_ID> & = {}, + const std::vector<LRAT_ID> & = {}); + bool merge_literals_lrat (int lit, int other, + const std::vector<LRAT_ID> & = {}, + const std::vector<LRAT_ID> & = {}); + + // proof production + vector<LitClausePair> lrat_chain_and_gate; + void push_lrat_id (const Clause *const c, int lit); + void push_lrat_unit (int lit); + + // pushes the clause with the reasons to rewrite clause + // unless: + // - the rewriting is not necessary (resolvent_marked == 1) + // - it is overwritten by one of the arguments + void push_id_and_rewriting_lrat_unit (Clause *c, Rewrite rewrite1, + std::vector<LRAT_ID> &chain, + bool = true, + Rewrite rewrite2 = Rewrite (), + int execept_lhs = 0, + int except_lhs2 = 0); + void push_id_and_rewriting_lrat_full (Clause *c, Rewrite rewrite1, + std::vector<LRAT_ID> &chain, + bool = true, + Rewrite rewrite2 = Rewrite (), + int execept_lhs = 0, + int except_lhs2 = 0); + // TODO: does nothing except pushing on the stack, remove! + void push_id_on_chain (std::vector<LRAT_ID> &chain, Clause *c); + // TODO: does nothing except pushing on the stack, remove! + void push_id_on_chain (std::vector<LRAT_ID> &chain, + const std::vector<LitClausePair> &c); + // TODO: does nothing except pushing on the stack, remove! + void push_id_on_chain (std::vector<LRAT_ID> &chain, Rewrite rewrite, int); + void update_and_gate_build_lrat_chain ( + Gate *g, Gate *h, std::vector<LRAT_ID> &extra_reasons_lit, + std::vector<LRAT_ID> &extra_reasons_ulit, bool remove_units = true); + void update_and_gate_unit_build_lrat_chain ( + Gate *g, int src, LRAT_ID id1, LRAT_ID id2, int dst, + std::vector<LRAT_ID> &extra_reasons_lit, + std::vector<LRAT_ID> &extra_reasons_ulit); + // occs + vector<GOccs> gtab; + GOccs &goccs (int lit); + void connect_goccs (Gate *g, int lit); + vector<Gate *> garbage; + void mark_garbage (Gate *); + // remove the gate from the table + bool remove_gate (Gate *); + bool remove_gate (GatesTable::iterator git); + void index_gate (Gate *); + + // second counter for size, complements noccs + uint64_t &largecount (int lit); + + // simplification + bool skip_and_gate (Gate *g); + bool skip_xor_gate (Gate *g); + void update_and_gate (Gate *g, GatesTable::iterator, int src, int dst, + LRAT_ID id1, LRAT_ID id2, int falsified = 0, + int clashing = 0); + void update_xor_gate (Gate *g, GatesTable::iterator); + void shrink_and_gate (Gate *g, int falsified = 0, int clashing = 0); + bool simplify_gate (Gate *g); + void simplify_and_gate (Gate *g); + void simplify_ite_gate (Gate *g); + Clause *simplify_xor_clause (int lhs, Clause *); + void simplify_xor_gate (Gate *g); + bool simplify_gates (int lit); + void simplify_and_sort_xor_lrat_clauses (const vector<LitClausePair> &, + vector<LitClausePair> &, int, + int except2 = 0, bool flip = 0); + void simplify_unit_xor_lrat_clauses (const vector<LitClausePair> &, int); + + // rewriting + bool rewriting_lhs (Gate *g, int dst); + bool rewrite_gates (int dst, int src, LRAT_ID id1, LRAT_ID id2); + bool rewrite_gate (Gate *g, int dst, int src, LRAT_ID id1, LRAT_ID id2); + void rewrite_xor_gate (Gate *g, int dst, int src); + void rewrite_and_gate (Gate *g, int dst, int src, LRAT_ID id1, + LRAT_ID id2); + void rewrite_ite_gate (Gate *g, int dst, int src); + + size_t units; // next trail position to propagate + bool propagate_unit (int lit); + bool propagate_units (); + size_t propagate_units_and_equivalences (); + bool propagate_equivalence (int lit); + + // gates + void init_closure (); + void reset_closure (); + void reset_extraction (); + void reset_and_gate_extraction (); + void extract_and_gates (Closure &); + void extract_gates (); + void extract_and_gates_with_base_clause (Clause *c); + void init_and_gate_extraction (); + Gate *find_first_and_gate (Clause *base_clause, int lhs); + Gate *find_remaining_and_gate (Clause *base_clause, int lhs); + void extract_and_gates (); + + Gate *find_and_lits (const vector<int> &rhs, Gate *except = nullptr); + // rhs is sorted, so passing by copy + Gate *find_gate_lits (const vector<int> &rhs, Gate_Type typ, + Gate *except = nullptr); + Gate *find_xor_lits (const vector<int> &rhs); + // not const to normalize negations, also fixes the order of the LRAT + Gate *find_ite_gate (Gate *, bool &); + Gate *find_xor_gate (Gate *); + + void reset_xor_gate_extraction (); + void init_xor_gate_extraction (std::vector<Clause*> &candidates); + LRAT_ID check_and_add_to_proof_chain (vector<int> &clause); + void add_xor_matching_proof_chain (Gate *g, int lhs1, + const vector<LitClausePair> &, + int lhs2, vector<LRAT_ID> &, + vector<LRAT_ID> &); + void add_xor_shrinking_proof_chain (Gate *g, int src); + void extract_xor_gates (); + void extract_xor_gates_with_base_clause (Clause *c); + Clause *find_large_xor_side_clause (std::vector<int> &lits); + + void merge_condeq (int cond, lit_equivalences &condeq, + lit_equivalences ¬_condeq); + void find_conditional_equivalences (int lit, lit_implications &condbin, + lit_equivalences &condeq); + void copy_conditional_equivalences (int lit, lit_implications &condbin); + void check_ite_implied (int lhs, int cond, int then_lit, int else_lit); + void check_ite_gate_implied (Gate *g); + void check_and_gate_implied (Gate *g); + void check_ite_lrat_reasons (Gate *g, bool = false); + void check_contained_module_rewriting (Clause *c, int lit, bool, + int except); + void delete_proof_chain (); + + // ite gate extraction + void extract_ite_gates_of_literal (int); + void extract_ite_gates_of_variable (int idx); + void extract_condeq_pairs (int lit, lit_implications &condbin, + lit_equivalences &condeq); + void init_ite_gate_extraction (std::vector<ClauseSize> &candidates); + lit_implications::const_iterator find_lit_implication_second_literal ( + int lit, lit_implications::const_iterator begin, + lit_implications::const_iterator end); + void search_condeq (int lit, int pos_lit, + lit_implications::const_iterator pos_begin, + lit_implications::const_iterator pos_end, int neg_lit, + lit_implications::const_iterator neg_begin, + lit_implications::const_iterator neg_end, + lit_equivalences &condeq); + void reset_ite_gate_extraction (); + void extract_ite_gates (); + + void forward_subsume_matching_clauses (); + + void extract_congruence (); + + void add_ite_matching_proof_chain (Gate *g, Gate *h, int lhs1, int lhs2, + std::vector<LRAT_ID> &reasons1, + std::vector<LRAT_ID> &reasons2); + void add_ite_turned_and_binary_clauses (Gate *g); + Gate *new_and_gate (Clause *, int); + Gate *new_ite_gate (int lhs, int cond, int then_lit, int else_lit, + std::vector<LitClausePair> &&clauses); + Gate *new_xor_gate (const vector<LitClausePair> &, int); + // check + void check_xor_gate_implied (Gate const *const); + void check_ternary (int a, int b, int c); + void check_binary_implied (int a, int b); + void check_implied (); + + // learn units. You can delay units if you want to learn several at once before + // propagation. Otherwise, propagate! If you need propagation even if nothing is set, use the + // second parameter. + // + // The function can also learn the empty clause if the unit is already set. Do not add the unit in + // the chain! + bool learn_congruence_unit (int unit, bool = false, bool = false); + bool fully_propagate (); + void learn_congruence_unit_falsifies_lrat_chain (Gate *g, int src, + int dst, + int clashing, + int falsified, int unit); + void learn_congruence_unit_when_lhs_set (Gate *g, int src, LRAT_ID id1, + LRAT_ID id2, int dst); + + void find_units (); + void find_equivalences (); + void subsume_clause (Clause *subsuming, Clause *subsumed); + bool find_subsuming_clause (Clause *c); + void produce_rewritten_clause_lrat_and_clean (vector<LitClausePair> &, + int execept_lhs = 0, + bool = true); + // rewrite the clause using eager rewriting and rew1 and rew2, except for + // 2 literals Usage: + // - the except are used to ignore LHS of gates that have not and should + // not be rewritten. + // - TODO: except_lhs2 should never be used actually + // - the Rewrite are for additional rewrite to allow for lazy rewrites + // to be taken into account without being added to the eager rewriting + // (yet) + Clause *produce_rewritten_clause_lrat (Clause *c, int execept_lhs = 0, + bool remove_units = true, bool = true); + void produce_rewritten_clause_lrat (vector<LitClausePair> &, + int execept_lhs = 0, + bool = true); + void compute_rewritten_clause_lrat_simple (Clause *c, int except); + // variant where we update the indices after removing the tautologies and + // remove the tautological clauses + void produce_rewritten_clause_lrat_and_clean ( + std::vector<LitClausePair> &litIds, int except_lhs, + size_t &old_position1, size_t &old_position2, + bool remove_units = true); + // binary extraction and ternary strengthening + void extract_binaries (); + bool find_binary (int, int) const; + + Clause *new_tmp_clause (std::vector<int> &clause); + Clause *maybe_promote_tmp_binary_clause (Clause *); + void check_not_tmp_binary_clause (Clause *c); + Clause *new_clause (); + // + void sort_literals_by_var (vector<int> &rhs); + void sort_literals_by_var_except (vector<int> &rhs, int, int except2 = 0); + + // schedule + queue<int> schedule; + void schedule_literal (int lit); + void add_clause_to_chain (std::vector<int>, LRAT_ID); + // proof. If delete_id is non-zero, then delete the clause instead of + // learning it + LRAT_ID simplify_and_add_to_proof_chain (vector<int> &unsimplified, + LRAT_ID delete_id = 0); + + // we define our own wrapper as cadical has otherwise a non-compatible + // marking system + signed char &marked (int lit); + void set_mu1_reason (int lit, Clause *c); + void set_mu2_reason (int lit, Clause *c); + void set_mu4_reason (int lit, Clause *c); + LitClausePair marked_mu1 (int lit); + LitClausePair marked_mu2 (int lit); + LitClausePair marked_mu4 (int lit); + + // XOR + uint32_t number_from_xor_reason_reversed (const std::vector<int> &rhs); + uint32_t number_from_xor_reason (const std::vector<int> &rhs, int, + int except2 = 0, bool flip = 0); + void gate_sort_lrat_reasons (std::vector<LitClausePair> &, int, + int except2 = 0, bool flip = 0); + void gate_sort_lrat_reasons (LitClausePair &, int, int except2 = 0, + bool flip = 0); + + bool rewrite_ite_gate_to_and (Gate *g, int dst, int src, size_t c, + size_t d, int cond_lit_to_learn_if_degenerated); + void produce_ite_merge_then_else_reasons ( + Gate *g, int dst, int src, std::vector<LRAT_ID> &reasons_implication, + std::vector<LRAT_ID> &reasons_back); + void produce_ite_merge_lhs_then_else_reasons ( + Gate *g, std::vector<LRAT_ID> &reasons_implication, + std::vector<LRAT_ID> &reasons_back, + std::vector<LRAT_ID> &reasons_unit, bool, bool &); + void rewrite_ite_gate_update_lrat_reasons (Gate *g, int src, int dst); + void simplify_ite_gate_produce_unit_lrat (Gate *g, int lit, size_t idx1, + size_t idx2); + void merge_and_gate_lrat_produce_lrat ( + Gate *g, Gate *h, std::vector<LRAT_ID> &reasons_lrat, + std::vector<LRAT_ID> &reasons_lrat_back, bool remove_units = true); + // first index is a binary clause after unit propagation and the second + // has length 3 + bool simplify_ite_gate_to_and (Gate *g, size_t idx1, size_t idx2, + int removed); + void + merge_ite_gate_same_then_else_lrat (std::vector<LitClausePair> &clauses, + std::vector<LRAT_ID> &reasons_implication, + std::vector<LRAT_ID> &reasons_back); + void simplify_ite_gate_then_else_set ( + Gate *g, std::vector<LRAT_ID> &reasons_implication, + std::vector<LRAT_ID> &reasons_back, size_t idx1, size_t idx2); + + void simplify_ite_gate_condition_set ( + Gate *g, std::vector<LRAT_ID> &reasons_lrat, + std::vector<LRAT_ID> &reasons_back_lrat, size_t idx1, size_t idx2); + bool normalize_ite_lits_gate (Gate *rhs); +}; + +} // namespace CaDiCaL + +ABC_NAMESPACE_CXX_HEADER_END + +#endif diff --git a/src/sat/cadical/contract.hpp b/src/sat/cadical/contract.hpp new file mode 100644 index 0000000000..4a3ba066ab --- /dev/null +++ b/src/sat/cadical/contract.hpp @@ -0,0 +1,142 @@ +#ifndef _contract_hpp_INCLUDED +#define _contract_hpp_INCLUDED + +#include "global.h" + +ABC_NAMESPACE_CXX_HEADER_START + +/*------------------------------------------------------------------------*/ +#ifndef CADICAL_NCONTRACTS +/*------------------------------------------------------------------------*/ + +// If the user violates API contracts while calling functions declared in +// 'cadical.hpp' and implemented in 'solver.cpp' then an error is reported. +// Currently we also force aborting the program. In the future it might be +// better to allow the user to provide a call back function, which then can +// for instance throw a C++ exception or execute a 'longjmp' in 'C' etc. + +#define CONTRACT_VIOLATED(...) \ + do { \ + fatal_message_start (); \ + fprintf (stderr, \ + "invalid API usage of '%s' in '%s': ", __PRETTY_FUNCTION__, \ + __FILE__); \ + fprintf (stderr, __VA_ARGS__); \ + fputc ('\n', stderr); \ + fflush (stderr); \ + abort (); \ + } while (0) + +/*------------------------------------------------------------------------*/ + +namespace CaDiCaL { + +// It would be much easier to just write 'REQUIRE (this, "not initialized")' +// which however produces warnings due to the '-Wnonnull' check. Note, that +// 'this' is always assumed to be non zero in modern C++. Much worse, if we +// use instead 'this != 0' or something similar like 'this != nullptr' then +// optimization silently removes this check ('gcc-7.4.0' at least) even +// though of course a zero pointer might be used as 'this' if the user did +// not initialize it. The only solution I found is to disable optimization +// for this check. It does not seem to be necessary for 'clang++' though +// ('clang++-6.0.0' at least). The alternative is to not check that the +// user forgot to initialize the solver pointer, but as long this works we +// keep this ugly hack. It also forces the function not to be inlined. +// The actual code I is in 'contract.cpp'. +// +void require_solver_pointer_to_be_non_zero (const void *ptr, + const char *function_name, + const char *file_name); +#define REQUIRE_NON_ZERO_THIS() \ + do { \ + require_solver_pointer_to_be_non_zero (this, __PRETTY_FUNCTION__, \ + __FILE__); \ + } while (0) + +} // namespace CaDiCaL + +/*------------------------------------------------------------------------*/ + +// These are common shortcuts for 'Solver' API contracts (requirements). + +#define REQUIRE(COND, ...) \ + do { \ + if ((COND)) \ + break; \ + CONTRACT_VIOLATED (__VA_ARGS__); \ + } while (0) + +#define REQUIRE_INITIALIZED() \ + do { \ + REQUIRE_NON_ZERO_THIS (); \ + REQUIRE (external, "external solver not initialized"); \ + REQUIRE (internal, "internal solver not initialized"); \ + } while (0) + +#define REQUIRE_VALID_STATE() \ + do { \ + REQUIRE_INITIALIZED (); \ + REQUIRE (this->state () & VALID, "solver in invalid state"); \ + } while (0) + +#define REQUIRE_READY_STATE() \ + do { \ + REQUIRE_VALID_STATE (); \ + REQUIRE (state () != ADDING, \ + "clause incomplete (terminating zero not added)"); \ + } while (0) + +#define REQUIRE_VALID_OR_SOLVING_STATE() \ + do { \ + REQUIRE_INITIALIZED (); \ + REQUIRE (this->state () & (VALID | SOLVING), \ + "solver neither in valid nor solving state"); \ + } while (0) + +#define REQUIRE_VALID_LIT(LIT) \ + do { \ + REQUIRE ((int) (LIT) && ((int) (LIT)) != INT_MIN, \ + "invalid literal '%d'", (int) (LIT)); \ + REQUIRE (external->is_valid_input ((int) (LIT)), \ + "extension variable %d defined by the solver", (int) (LIT)); \ + } while (0) + +#define REQUIRE_STEADY_STATE() \ + do { \ + REQUIRE_INITIALIZED (); \ + REQUIRE (this->state () & STEADY, "solver is not in steady state"); \ + } while (0) + +/*------------------------------------------------------------------------*/ +#else // CADICAL_NCONTRACTS +/*------------------------------------------------------------------------*/ + +#define REQUIRE(...) \ + do { \ + } while (0) +#define REQUIRE_INITIALIZED() \ + do { \ + } while (0) +#define REQUIRE_VALID_STATE() \ + do { \ + } while (0) +#define REQUIRE_READY_STATE() \ + do { \ + } while (0) +#define REQUIRE_VALID_OR_SOLVING_STATE() \ + do { \ + } while (0) +#define REQUIRE_VALID_LIT(...) \ + do { \ + } while (0) +#define REQUIRE_STEADY_STATE() \ + do { \ + } while (0) + +/*------------------------------------------------------------------------*/ +#endif +/*------------------------------------------------------------------------*/ + +ABC_NAMESPACE_CXX_HEADER_END + +#endif diff --git a/src/sat/cadical/cover.hpp b/src/sat/cadical/cover.hpp new file mode 100644 index 0000000000..47e6f1b822 --- /dev/null +++ b/src/sat/cadical/cover.hpp @@ -0,0 +1,36 @@ +#ifndef _cover_hpp_INCLUDED +#define _cover_hpp_INCLUDED + +#include "global.h" + +/*------------------------------------------------------------------------*/ + +// This header only provides the 'COVER' macro for testing. It is unrelated +// to 'cover.cpp' which implements covered clause elimination (CCE), but we +// wanted to use the name base name in both cases. More explanation on CCE +// is provided in 'cover.cpp'. + +/*------------------------------------------------------------------------*/ + +// Coverage goal, used similar to 'CADICAL_assert' (but with flipped condition) and +// also included even if 'CADICAL_NDEBUG' is defined (in optimizing compilation). +// +// This should in essence not be used in production code. +// +// There seems to be no problem overloading the name 'COVER' of this macro +// with the constant 'COVER' of 'Internal::Mode' (surprisingly). + +#define COVER(COND) \ + do { \ + if (!(COND)) \ + break; \ + fprintf (stderr, \ + "%scadical%s: %s:%d: %s: Coverage goal %s`%s'%s reached.\n", \ + terr.bold_code (), terr.normal_code (), __FUNCTION__, \ + __LINE__, __FILE__, terr.green_code (), #COND, \ + terr.normal_code ()); \ + fflush (stderr); \ + abort (); \ + } while (0) + +#endif diff --git a/src/sat/cadical/decompose.hpp b/src/sat/cadical/decompose.hpp new file mode 100644 index 0000000000..9c93e84486 --- /dev/null +++ b/src/sat/cadical/decompose.hpp @@ -0,0 +1,29 @@ +#ifndef _decompose_hpp_INCLUDED +#define _decompose_hpp_INCLUDED + +#include "global.h" + +ABC_NAMESPACE_CXX_HEADER_START + +namespace CaDiCaL { + +// This implements Tarjan's algorithm for decomposing the binary implication +// graph intro strongly connected components (SCCs). Literals in one SCC +// are equivalent and we replace them all by the literal with the smallest +// index in the SCC. These variables are marked 'substituted' and will be +// removed from all clauses. Their value will be fixed during 'extend'. + +#define TRAVERSED UINT_MAX // mark completely traversed + +struct DFS { + unsigned idx; // depth first search index + unsigned min; // minimum reachable index + Clause *parent; // for lrat + DFS () : idx (0), min (0), parent (0) {} +}; + +} // namespace CaDiCaL + +ABC_NAMESPACE_CXX_HEADER_END + +#endif diff --git a/src/sat/cadical/delay.hpp b/src/sat/cadical/delay.hpp new file mode 100644 index 0000000000..9ee9a92348 --- /dev/null +++ b/src/sat/cadical/delay.hpp @@ -0,0 +1,44 @@ +#ifndef _delay_hpp_INCLUDED +#define _delay_hpp_INCLUDED + +#include "global.h" + +#include <cstdint> +#include <limits> + +ABC_NAMESPACE_CXX_HEADER_START + +namespace CaDiCaL { +struct Delay { + unsigned count; + unsigned current; + + Delay () : count (0), current (0) {} + + bool delay () { + if (count) { + --count; + return true; + } else { + return false; + } + } + + void bump_delay () { + current += current < std::numeric_limits<unsigned>::max (); + count = current; + } + + void reduce_delay () { + if (!current) + return; + current /= 2; + count = current; + } +}; + +} // namespace CaDiCaL + +ABC_NAMESPACE_CXX_HEADER_END + +#endif diff --git a/src/sat/cadical/drattracer.hpp b/src/sat/cadical/drattracer.hpp new file mode 100644 index 0000000000..8c2c023d51 --- /dev/null +++ b/src/sat/cadical/drattracer.hpp @@ -0,0 +1,59 @@ +#ifndef _drattracer_h_INCLUDED +#define _drattracer_h_INCLUDED + +#include "global.h" + +#include "tracer.hpp" + +ABC_NAMESPACE_CXX_HEADER_START + +namespace CaDiCaL { + +class DratTracer : public FileTracer { + + Internal *internal; + File *file; + bool binary; +#ifndef CADICAL_QUIET + int64_t added, deleted; +#endif + void put_binary_zero (); + void put_binary_lit (int external_lit); + + // support DRAT + void drat_add_clause (const vector<int> &); + void drat_delete_clause (const vector<int> &); + +public: + // own and delete 'file' + DratTracer (Internal *, File *file, bool binary); + ~DratTracer (); + + void connect_internal (Internal *i) override; + void begin_proof (int64_t) override {} // skip + + void add_original_clause (int64_t, bool, const vector<int> &, + bool = false) override {} // skip + + void add_derived_clause (int64_t, bool, const vector<int> &, + const vector<int64_t> &) override; + + void delete_clause (int64_t, bool, const vector<int> &) override; + + void finalize_clause (int64_t, const vector<int> &) override {} // skip + + void report_status (int, int64_t) override {} // skip + +#ifndef CADICAL_QUIET + void print_statistics (); +#endif + bool closed () override; + void close (bool) override; + void flush (bool) override; +}; + +} // namespace CaDiCaL + +ABC_NAMESPACE_CXX_HEADER_END + +#endif diff --git a/src/sat/cadical/elim.hpp b/src/sat/cadical/elim.hpp new file mode 100644 index 0000000000..73e1bf5479 --- /dev/null +++ b/src/sat/cadical/elim.hpp @@ -0,0 +1,59 @@ +#ifndef _elim_hpp_INCLUDED +#define _elim_hpp_INCLUDED + +#include "global.h" + +#include "heap.hpp" // Alphabetically after 'elim.hpp'. + +ABC_NAMESPACE_CXX_HEADER_START + +namespace CaDiCaL { + +struct Internal; + +struct elim_more { + Internal *internal; + elim_more (Internal *i) : internal (i) {} + bool operator() (unsigned a, unsigned b); +}; + +typedef heap<elim_more> ElimSchedule; + +struct proof_clause { + int64_t id; + vector<int> literals; + // for lrat + unsigned cid; // cadical_kitten id + bool learned; + vector<int64_t> chain; +}; + +enum GateType { NO = 0, EQUI = 1, AND = 2, ITE = 3, XOR = 4, DEF = 5 }; + +struct Eliminator { + + Internal *internal; + ElimSchedule schedule; + + Eliminator (Internal *i) + : internal (i), schedule (elim_more (i)), definition_unit (0), + gatetype (NO) {} + ~Eliminator (); + + queue<Clause *> backward; + + Clause *dequeue (); + void enqueue (Clause *); + + vector<Clause *> gates; + unsigned definition_unit; + vector<proof_clause> proof_clauses; + vector<int> marked; + GateType gatetype; +}; + +} // namespace CaDiCaL + +ABC_NAMESPACE_CXX_HEADER_END + +#endif diff --git a/src/sat/cadical/ema.hpp b/src/sat/cadical/ema.hpp new file mode 100644 index 0000000000..51eb5f8c16 --- /dev/null +++ b/src/sat/cadical/ema.hpp @@ -0,0 +1,74 @@ +#ifndef _ema_hpp_INCLUDED +#define _ema_hpp_INCLUDED + +#include "global.h" + +#include <cstdint> + +ABC_NAMESPACE_CXX_HEADER_START + +namespace CaDiCaL { + +struct Internal; + +// This is a more complex generic exponential moving average class to +// support more robust initialization (see comments in the 'update' +// implementation). + +struct EMA { + +#ifdef LOGGING + uint64_t updated; +#endif + double value; // unbiased (corrected) moving average + double biased; // biased initialized moving average + double alpha; // input scaling with 'alpha = 1 - beta' + double beta; // decay of 'biased' with 'beta = 1 - alpha' + double exp; // 'exp = pow (beta, updated)' + + EMA () + : +#ifdef LOGGING + updated (0), +#endif + value (0), biased (0), alpha (0), beta (0), exp (0) { + } + + EMA (double a) + : +#ifdef LOGGING + updated (0), +#endif + value (0), biased (0), alpha (a), beta (1 - a), exp (!!beta) { + CADICAL_assert (beta >= 0); + } + + operator double () const { return value; } + void update (Internal *, double y, const char *name); +}; + +} // namespace CaDiCaL + +/*------------------------------------------------------------------------*/ + +// Compact average update and initialization macros for better logging. + +#define UPDATE_AVERAGE(A, Y) \ + do { \ + A.update (internal, (Y), #A); \ + } while (0) + +#define INIT_EMA(E, WINDOW) \ + do { \ + CADICAL_assert ((WINDOW) >= 1); \ + double ALPHA = 1.0 / (double) (WINDOW); \ + E = EMA (ALPHA); \ + LOG ("init " #E " EMA target alpha %g window %d", ALPHA, \ + (int) WINDOW); \ + } while (0) + +/*------------------------------------------------------------------------*/ + +ABC_NAMESPACE_CXX_HEADER_END + +#endif diff --git a/src/sat/cadical/external.hpp b/src/sat/cadical/external.hpp new file mode 100644 index 0000000000..7618fb936a --- /dev/null +++ b/src/sat/cadical/external.hpp @@ -0,0 +1,467 @@ +#ifndef _external_hpp_INCLUDED +#define _external_hpp_INCLUDED + +#include "global.h" + +/*------------------------------------------------------------------------*/ + +#include "range.hpp" +#include <unordered_map> +#include <vector> + +/*------------------------------------------------------------------------*/ + +ABC_NAMESPACE_CXX_HEADER_START + +namespace CaDiCaL { + +using namespace std; + +/*------------------------------------------------------------------------*/ + +// The CaDiCaL code is split into three layers: +// +// Solver: facade object providing the actual API of the solver +// External: communication layer between 'Solver' and 'Internal' +// Internal: the actual solver code +// +// Note, that 'Solver' is defined in 'cadical.hpp' and 'solver.cpp', while +// 'External' and 'Internal' in '{external,internal}.{hpp,cpp}'. +// +// Also note, that any user should access the library only through the +// 'Solver' API. For the library internal 'Parser' code we make an +// exception and allow access to both 'External' and 'Internal'. The former +// to enforce the same external to internal mapping of variables and the +// latter for profiling and messages. The same applies to 'App'. +// +// The 'External' class provided here stores the information needed to map +// external variable indices to internal variables (actually literals). +// This is helpful for shrinking the working size of the internal solver +// after many variables become inactive. It will also help to provide +// support for extended resolution in the future, since it allows to +// introduce only internally visible variables (even though we do not know +// how to support generating incremental proofs in this situation yet). +// +// External literals are usually called 'elit' and internal 'ilit'. + +/*------------------------------------------------------------------------*/ + +struct Clause; +struct Internal; +struct CubesWithStatus; + +/*------------------------------------------------------------------------*/ + +/*------------------------------------------------------------------------*/ + +struct External { + + /*==== start of state ==================================================*/ + + Internal *internal; // The actual internal solver. + + int max_var; // External maximum variable index. + size_t vsize; // Allocated external size. + + vector<bool> vals; // Current external (extended) assignment. + vector<int> e2i; // External 'idx' to internal 'lit'. + + vector<int> assumptions; // External assumptions. + vector<int> constraint; // External constraint. Terminated by zero. + + vector<int64_t> + ext_units; // External units. Needed to compute LRAT for eclause + vector<bool> ext_flags; // to avoid duplicate units + vector<int> eclause; // External version of original input clause. + // The extension stack for reconstructing complete satisfying assignments + // (models) of the original external formula is kept in this external + // solver object. It keeps track of blocked clauses and clauses containing + // eliminated variable. These irredundant clauses are stored in terms of + // external literals on the 'extension' stack after mapping the + // internal literals given as arguments with 'externalize'. + + bool extended; // Have been extended. + bool concluded; + vector<int> extension; // Solution reconstruction extension stack. + + vector<bool> witness; // Literal witness on extension stack. + vector<bool> tainted; // Literal tainted in adding literals. + + vector<bool> ervars; // Variables added through Extended Resolution. + + vector<unsigned> frozentab; // Reference counts for frozen variables. + + // Regularly checked terminator if non-zero. The terminator is set from + // 'Solver::set (Terminator *)' and checked by 'Internal::terminating ()'. + + Terminator *terminator; + + // If there is a learner export learned clauses. + + Learner *learner; + + void export_learned_empty_clause (); + void export_learned_unit_clause (int ilit); + void export_learned_large_clause (const vector<int> &); + + // If there is a listener for fixed assignments. + + FixedAssignmentListener *fixed_listener; + + // If there is an external propagator. + + ExternalPropagator *propagator; + + vector<bool> is_observed; // Quick flag for each external variable + + // Saved 'forgettable' original clauses coming from the external + // propagator. The value of the map starts with a Boolean flag indicating + // if the clause is still present or got already deleted, and then + // followed by the literals of the clause. + unordered_map<uint64_t, vector<int>> forgettable_original; + + void add_observed_var (int elit); + void remove_observed_var (int elit); + void reset_observed_vars (); + + bool observed (int elit); + bool is_witness (int elit); + bool is_decision (int elit); + + void force_backtrack (size_t new_level); + + //----------------------------------------------------------------------// + + signed char *solution; // Given solution checking for debugging. + int solution_size; // Given solution checking for debugging. + vector<int> original; // Saved original formula for checking. + + // If 'opts.checkfrozen' is set make sure that only literals are added + // which were never completely molten before. These molten literals are + // marked at the beginning of the 'solve' call. Note that variables + // larger than 'max_var' are not molten and can thus always be used in the + // future. Only needed to check and debug old style freeze semantics. + // + vector<bool> moltentab; + + //----------------------------------------------------------------------// + + const Range vars; // Provides safe variable iterations. + + /*==== end of state ====================================================*/ + + // These two just factor out common sanity (CADICAL_assertion) checking code. + + inline int vidx (int elit) const { + CADICAL_assert (elit); + CADICAL_assert (elit != INT_MIN); + int res = abs (elit); + CADICAL_assert (res <= max_var); + return res; + } + + inline int vlit (int elit) const { + CADICAL_assert (elit); + CADICAL_assert (elit != INT_MIN); + CADICAL_assert (abs (elit) <= max_var); + return elit; + } + + inline bool is_valid_input (int elit) { + CADICAL_assert (elit); + CADICAL_assert (elit != INT_MIN); + int eidx = abs (elit); + return eidx > max_var || !ervars[eidx]; + } + + /*----------------------------------------------------------------------*/ + + // The following five functions push individual literals or clauses on the + // extension stack. They all take internal literals as argument, and map + // them back to external literals first, before pushing them on the stack. + + void push_zero_on_extension_stack (); + + // Our general version of extension stacks always pushes a set of witness + // literals (for variable elimination the literal of the eliminated + // literal and for blocked clauses the blocking literal) followed by all + // the clause literals starting with and separated by zero. + // + void push_clause_literal_on_extension_stack (int ilit); + void push_witness_literal_on_extension_stack (int ilit); + + void push_clause_on_extension_stack (Clause *); + void push_clause_on_extension_stack (Clause *, int witness); + void push_binary_clause_on_extension_stack (int64_t id, int witness, + int other); + + // The main 'extend' function which extends an internal assignment to an + // external assignment using the extension stack (and sets 'extended'). + // + void extend (); + void conclude_sat (); + + /*----------------------------------------------------------------------*/ + + // Marking external literals. + + unsigned elit2ulit (int elit) const { + CADICAL_assert (elit); + CADICAL_assert (elit != INT_MIN); + const int idx = abs (elit) - 1; + CADICAL_assert (idx <= max_var); + return 2u * idx + (elit < 0); + } + + bool marked (const vector<bool> &map, int elit) const { + const unsigned ulit = elit2ulit (elit); + return ulit < map.size () ? map[ulit] : false; + } + + void mark (vector<bool> &map, int elit) { + const unsigned ulit = elit2ulit (elit); + if (ulit >= map.size ()) + map.resize (ulit + 1, false); + map[ulit] = true; + } + + void unmark (vector<bool> &map, int elit) { + const unsigned ulit = elit2ulit (elit); + if (ulit < map.size ()) + map[ulit] = false; + } + + /*----------------------------------------------------------------------*/ + + void push_external_clause_and_witness_on_extension_stack ( + const vector<int> &clause, const vector<int> &witness, int64_t id); + + void push_id_on_extension_stack (int64_t id); + + // Restore a clause, which was pushed on the extension stack. + void restore_clause (const vector<int>::const_iterator &begin, + const vector<int>::const_iterator &end, + const int64_t id); + + void restore_clauses (); + + /*----------------------------------------------------------------------*/ + + // Explicitly freeze and melt literals (instead of just freezing + // internally and implicitly assumed literals). Passes on freezing and + // melting to the internal solver, which has separate frozen counters. + + void freeze (int elit); + void melt (int elit); + + bool frozen (int elit) { + CADICAL_assert (elit); + CADICAL_assert (elit != INT_MIN); + int eidx = abs (elit); + if (eidx > max_var) + return false; + if (eidx >= (int) frozentab.size ()) + return false; + return frozentab[eidx] > 0; + } + + /*----------------------------------------------------------------------*/ + + External (Internal *); + ~External (); + + void enlarge (int new_max_var); // Enlarge allocated 'vsize'. + void init (int new_max_var, + bool extension = false); // Initialize up-to 'new_max_var'. + + int internalize ( + int, + bool extension = false); // Translate external to internal literal. + + /*----------------------------------------------------------------------*/ + + // According to the CaDiCaL API contract (as well as IPASIR) we have to + // forget about the previous assumptions after a 'solve' call. This + // should however be delayed until we transition out of an 'UNSATISFIED' + // state, i.e., after no more 'failed' calls are expected. Note that + // 'failed' requires to know the failing assumptions, and the 'failed' + // status of those should cleared before at start of the next 'solve'. + // As a consequence 'reset_assumptions' is only called from + // 'transition_to_unknown_state' in API calls in 'solver.cpp'. + + void reset_assumptions (); + + // Similarly to 'failed', 'conclude' needs to know about failing + // assumptions and therefore needs to be reset when leaving the + // 'UNSATISFIED' state. + // + void reset_concluded (); + + // Similarly a valid external assignment obtained through 'extend' has to + // be reset at each point it risks to become invalid. This is done + // in the external layer in 'external.cpp' functions.. + + void reset_extended (); + + // Finally, the semantics of incremental solving also require that limits + // are only valid for the next 'solve' call. Since the limits can not + // really be queried, handling them is less complex and they are just + // reset immediately at the end of 'External::solve'. + + void reset_limits (); + + /*----------------------------------------------------------------------*/ + + // Proxies to IPASIR functions. + + void add (int elit); + void assume (int elit); + int solve (bool preprocess_only); + + // We call it 'ival' as abbreviation for 'val' with 'int' return type to + // avoid bugs due to using 'signed char tmp = val (lit)', which might turn + // a negative value into a positive one (happened in 'extend'). + // + inline int ival (int elit) const { + CADICAL_assert (elit != INT_MIN); + int eidx = abs (elit); + bool val = false; + if (eidx <= max_var && (size_t) eidx < vals.size ()) + val = vals[eidx]; + if (elit < 0) + val = !val; + return val ? elit : -elit; + } + + bool flip (int elit); + bool flippable (int elit); + + bool failed (int elit); + + void terminate (); + + // Other important non IPASIR functions. + + /*----------------------------------------------------------------------*/ + + // Add literal to external constraint. + // + void constrain (int elit); + + // Returns true if 'solve' returned 20 because of the constraint. + // + bool failed_constraint (); + + // Deletes the current constraint clause. Called on + // 'transition_to_unknown_state' and if a new constraint is added. Can be + // called directly using the API. + // + void reset_constraint (); + + /*----------------------------------------------------------------------*/ + + int propagate_assumptions (); + void implied (std::vector<int> &entrailed); + void conclude_unknown (); + + /*----------------------------------------------------------------------*/ + int lookahead (); + CaDiCaL::CubesWithStatus generate_cubes (int, int); + + int fixed (int elit) const; // Implemented in 'internal.hpp'. + + /*----------------------------------------------------------------------*/ + + void phase (int elit); + void unphase (int elit); + + /*----------------------------------------------------------------------*/ + + // Traversal functions for the witness stack and units. The explanation + // in 'external.cpp' for why we have to distinguish these cases. + + bool traverse_all_frozen_units_as_clauses (ClauseIterator &); + bool traverse_all_non_frozen_units_as_witnesses (WitnessIterator &); + bool traverse_witnesses_backward (WitnessIterator &); + bool traverse_witnesses_forward (WitnessIterator &); + + /*----------------------------------------------------------------------*/ + + // Copy flags for determining preprocessing state. + + void copy_flags (External &other) const; + + /*----------------------------------------------------------------------*/ + + // Check solver behaves as expected during testing and debugging. + + void check_assumptions_satisfied (); + void check_constraint_satisfied (); + void check_failing (); + + void check_solution_on_learned_clause (); + void check_solution_on_shrunken_clause (Clause *); + void check_solution_on_learned_unit_clause (int unit); + void check_no_solution_after_learning_empty_clause (); + + void check_learned_empty_clause () { + if (solution) + check_no_solution_after_learning_empty_clause (); + } + + void check_learned_unit_clause (int unit) { + if (solution) + check_solution_on_learned_unit_clause (unit); + } + + void check_learned_clause () { + if (solution) + check_solution_on_learned_clause (); + } + + void check_shrunken_clause (Clause *c) { + if (solution) + check_solution_on_shrunken_clause (c); + } + + void check_assignment (int (External::*assignment) (int) const); + + void check_satisfiable (); + void check_unsatisfiable (); + + void check_solve_result (int res); + + void update_molten_literals (); + + /*----------------------------------------------------------------------*/ + + // For debugging and testing only. See 'solution.hpp' for more details. + // TODO: if elit > solution_size, elit is an extension variable. For now + // the clause will count as satisfied regardless. For the future one + // should check that actually there is one consistent extension for the + // solution that satisfies the clauses with this extension variable (by + // setting it to a value once a clause is learned which is not satisfied + // already). + // + inline int sol (int elit) const { + CADICAL_assert (solution); + CADICAL_assert (elit != INT_MIN); + int eidx = abs (elit); + if (eidx > max_var) + return 0; + else if (eidx > solution_size) + return elit; + signed char value = solution[eidx]; + if (!value) + return 0; + if (elit < 0) + value = -value; + return value > 0 ? elit : -elit; + } +}; + +} // namespace CaDiCaL + +ABC_NAMESPACE_CXX_HEADER_END + +#endif diff --git a/src/sat/cadical/factor.hpp b/src/sat/cadical/factor.hpp new file mode 100644 index 0000000000..3aa5ea6c66 --- /dev/null +++ b/src/sat/cadical/factor.hpp @@ -0,0 +1,60 @@ +#ifndef _factor_hpp_INCLUDED +#define _factor_hpp_INCLUDED + +#include "global.h" + +#include "clause.hpp" +#include "heap.hpp" + +ABC_NAMESPACE_CXX_HEADER_START + +namespace CaDiCaL { + +struct Internal; + +struct factor_occs_size { + Internal *internal; + factor_occs_size (Internal *i) : internal (i) {} + bool operator() (unsigned a, unsigned b); +}; + +struct Quotient { + Quotient (int f) : factor (f) {} + ~Quotient () {} + int factor; + size_t id; + int64_t bid; // for LRAT + Quotient *prev, *next; + vector<Clause *> qlauses; + vector<size_t> matches; + size_t matched; +}; + +typedef heap<factor_occs_size> FactorSchedule; + +struct Factoring { + Factoring (Internal *, int64_t); + ~Factoring (); + + // These are initialized by the constructor + Internal *internal; + int64_t limit; + FactorSchedule schedule; + + int initial; + int bound; + vector<unsigned> count; + vector<int> fresh; + vector<int> counted; + vector<int> nounted; + vector<Clause *> flauses; + struct { + Quotient *first, *last; + } quotients; +}; + +} // namespace CaDiCaL + +ABC_NAMESPACE_CXX_HEADER_END + +#endif diff --git a/src/sat/cadical/file.hpp b/src/sat/cadical/file.hpp new file mode 100644 index 0000000000..860e9613a7 --- /dev/null +++ b/src/sat/cadical/file.hpp @@ -0,0 +1,221 @@ +#ifndef _file_hpp_INCLUDED +#define _file_hpp_INCLUDED + +#include "global.h" + +#include <cassert> +#include <cstdint> +#include <cstdio> +#include <cstdlib> +#include <vector> + +#ifndef CADICAL_NDEBUG +#include <climits> +#endif + +/*------------------------------------------------------------------------*/ +#ifdef WIN32 +#define cadical_putc_unlocked putc +#define cadical_getc_unlocked getc +#else +#ifndef NUNLOCKED +#define cadical_putc_unlocked putc_unlocked +#define cadical_getc_unlocked getc_unlocked +#else +#define cadical_putc_unlocked putc +#define cadical_getc_unlocked getc +#endif +#endif +/*------------------------------------------------------------------------*/ + +ABC_NAMESPACE_CXX_HEADER_START + +namespace CaDiCaL { + +// Wraps a 'C' file 'FILE' with name and supports zipped reading and writing +// through 'popen' using external helper tools. Reading has line numbers. +// Compression and decompression relies on external utilities, e.g., 'gzip', +// 'bzip2', 'xz', and '7z', which should be in the 'PATH'. + +struct Internal; + +class File { + + Internal *internal; +#if !defined(CADICAL_QUIET) || !defined(CADICAL_NDEBUG) + bool writing; +#endif + + int close_file; // need to close file (1=fclose, 2=pclose, 3=pipe) + int child_pid; + FILE *file; + char *_name; + uint64_t _lineno; + uint64_t _bytes; + + File (Internal *, bool, int, int, FILE *, const char *); + + static FILE *open_file (Internal *, const char *path, const char *mode); + static FILE *read_file (Internal *, const char *path); + static FILE *write_file (Internal *, const char *path); + + static void split_str (const char *, std::vector<char *> &); + static void delete_str_vector (std::vector<char *> &); + + static FILE *open_pipe (Internal *, const char *fmt, const char *path, + const char *mode); + static FILE *read_pipe (Internal *, const char *fmt, const int *sig, + const char *path); +#ifndef WIN32 + static FILE *write_pipe (Internal *, const char *fmt, const char *path, + int &child_pid); +#endif + +public: + static char *find_program (const char *prg); // search in 'PATH' + static bool exists (const char *path); // file exists? + static bool writable (const char *path); // can write to that file? + static size_t size (const char *path); // file size in bytes + + bool piping (); // Is opened file a pipe? + + // Does the file match the file type signature. + // + static bool match (Internal *, const char *path, const int *sig); + + // Read from existing file. Assume given name. + // + static File *read (Internal *, FILE *f, const char *name); + + // Open file from path name for reading (possibly through opening a pipe + // to a decompression utility, based on the suffix). + // + static File *read (Internal *, const char *path); + + // Same for writing as for reading above. + // + static File *write (Internal *, FILE *, const char *name); + static File *write (Internal *, const char *path); + + ~File (); + + // Using the 'unlocked' versions here is way faster but + // not thread safe if the same file is used by different + // threads, which on the other hand currently is impossible. + + int get () { + CADICAL_assert (!writing); + int res = cadical_getc_unlocked (file); + if (res == '\n') + _lineno++; + if (res != EOF) + _bytes++; + return res; + } + + bool put (char ch) { + CADICAL_assert (writing); + if (cadical_putc_unlocked (ch, file) == EOF) + return false; + _bytes++; + return true; + } + + bool endl () { return put ('\n'); } + + bool put (unsigned char ch) { + CADICAL_assert (writing); + if (cadical_putc_unlocked (ch, file) == EOF) + return false; + _bytes++; + return true; + } + + bool put (const char *s) { + for (const char *p = s; *p; p++) + if (!put (*p)) + return false; + return true; + } + + bool put (int lit) { + CADICAL_assert (writing); + if (!lit) + return put ('0'); + else if (lit == -2147483648) { + CADICAL_assert (lit == INT_MIN); + return put ("-2147483648"); + } else { + char buffer[11]; + int i = sizeof buffer; + buffer[--i] = 0; + CADICAL_assert (lit != INT_MIN); + unsigned idx = abs (lit); + while (idx) { + CADICAL_assert (i > 0); + buffer[--i] = '0' + idx % 10; + idx /= 10; + } + if (lit < 0 && !put ('-')) + return false; + return put (buffer + i); + } + } + + bool put (int64_t l) { + CADICAL_assert (writing); + if (!l) + return put ('0'); + else if (l == INT64_MIN) { + CADICAL_assert (sizeof l == 8); + return put ("-9223372036854775808"); + } else { + char buffer[21]; + int i = sizeof buffer; + buffer[--i] = 0; + CADICAL_assert (l != INT64_MIN); + uint64_t k = l < 0 ? -l : l; + while (k) { + CADICAL_assert (i > 0); + buffer[--i] = '0' + k % 10; + k /= 10; + } + if (l < 0 && !put ('-')) + return false; + return put (buffer + i); + } + } + + bool put (uint64_t l) { + CADICAL_assert (writing); + if (!l) + return put ('0'); + else { + char buffer[22]; + int i = sizeof buffer; + buffer[--i] = 0; + while (l) { + CADICAL_assert (i > 0); + buffer[--i] = '0' + l % 10; + l /= 10; + } + return put (buffer + i); + } + } + + const char *name () const { return _name; } + uint64_t lineno () const { return _lineno; } + uint64_t bytes () const { return _bytes; } + + void connect_internal (Internal *i) { internal = i; } + bool closed () { return !file; } + + void close (bool print = false); + void flush (); +}; + +} // namespace CaDiCaL + +ABC_NAMESPACE_CXX_HEADER_END + +#endif diff --git a/src/sat/cadical/flags.hpp b/src/sat/cadical/flags.hpp new file mode 100644 index 0000000000..995438c7a8 --- /dev/null +++ b/src/sat/cadical/flags.hpp @@ -0,0 +1,91 @@ +#ifndef _flags_hpp_INCLUDED +#define _flags_hpp_INCLUDED + +#include "global.h" + +ABC_NAMESPACE_CXX_HEADER_START + +namespace CaDiCaL { + +struct Flags { // Variable flags. + + // The first set of flags is related to 'analyze' and 'minimize'. + // + bool seen : 1; // seen in generating first UIP clause in 'analyze' + bool keep : 1; // keep in learned clause in 'minimize' + bool poison : 1; // can not be removed in 'minimize' + bool removable : 1; // can be removed in 'minimize' + bool shrinkable : 1; // can be removed in 'shrink' + bool added : 1; // has already been added to lrat_chain (in 'minimize') + + // These three variable flags are used to schedule clauses in subsumption + // ('subsume'), variables in bounded variable elimination ('elim') and in + // hyper ternary resolution ('ternary'). + // + bool elim : 1; // removed since last 'elim' round (*) + bool subsume : 1; // added since last 'subsume' round (*) + bool ternary : 1; // added in ternary clause since last 'ternary' (*) + bool sweep : 1; + bool blockable : 1; + + unsigned char + marked_signed : 2; // generate correct LRAT chains in decompose + unsigned char factor : 2; + + // These literal flags are used by blocked clause elimination ('block'). + // + unsigned char block : 2; // removed since last 'block' round (*) + unsigned char skip : 2; // skip this literal as blocking literal + + // Bits for handling assumptions. + // + unsigned char assumed : 2; + unsigned char failed : 2; // 0 if not part of failure + // 1 if positive lit is in failure + // 2 if negated lit is in failure + + enum { + UNUSED = 0, + ACTIVE = 1, + FIXED = 2, + ELIMINATED = 3, + SUBSTITUTED = 4, + PURE = 5 + }; + + unsigned char status : 3; + + // Initialized explicitly in 'Internal::init' through this function. + // + Flags () { + seen = keep = poison = removable = shrinkable = added = sweep = false; + subsume = elim = ternary = true; + block = 3u; + skip = assumed = failed = marked_signed = factor = 0; + status = UNUSED; + } + + bool unused () const { return status == UNUSED; } + bool active () const { return status == ACTIVE; } + bool fixed () const { return status == FIXED; } + bool eliminated () const { return status == ELIMINATED; } + bool substituted () const { return status == SUBSTITUTED; } + bool pure () const { return status == PURE; } + + // The flags marked with '(*)' are copied during 'External::copy_flags', + // which in essence means they are reset in the copy if they were clear. + // This avoids the effort of fruitless preprocessing the copy. + + void copy (Flags &dst) const { + dst.elim = elim; + dst.subsume = subsume; + dst.ternary = ternary; + dst.block = block; + } +}; + +} // namespace CaDiCaL + +ABC_NAMESPACE_CXX_HEADER_END + +#endif diff --git a/src/sat/cadical/format.hpp b/src/sat/cadical/format.hpp new file mode 100644 index 0000000000..cf229e7607 --- /dev/null +++ b/src/sat/cadical/format.hpp @@ -0,0 +1,42 @@ +#ifndef _format_hpp_INCLUDED +#define _format_hpp_INCLUDED + +#include "global.h" + +#include <cstdarg> +#include <cstdint> + +ABC_NAMESPACE_CXX_HEADER_START + +namespace CaDiCaL { + +// This class provides a 'printf' style formatting utility. +// Only '%c', '%d', '%s' are supported at this point. +// It is used to capture and save an error message. + +class Format { + char *buffer; + int64_t count, size; + void enlarge (); + void push_char (char); + void push_string (const char *); + void push_int (int); + void push_uint64 (uint64_t); + const char *add (const char *fmt, va_list &); + +public: + Format () : buffer (0), count (0), size (0) {} + ~Format () { + if (buffer) + delete[] buffer; + } + const char *init (const char *fmt, ...) CADICAL_ATTRIBUTE_FORMAT (2, 3); + const char *append (const char *fmt, ...) CADICAL_ATTRIBUTE_FORMAT (2, 3); + operator const char * () const { return count ? buffer : 0; } +}; + +} // namespace CaDiCaL + +ABC_NAMESPACE_CXX_HEADER_END + +#endif diff --git a/src/sat/cadical/frattracer.hpp b/src/sat/cadical/frattracer.hpp new file mode 100644 index 0000000000..cadfa37217 --- /dev/null +++ b/src/sat/cadical/frattracer.hpp @@ -0,0 +1,68 @@ +#ifndef _frattracer_h_INCLUDED +#define _frattracer_h_INCLUDED + +#include "global.h" + +ABC_NAMESPACE_CXX_HEADER_START + +namespace CaDiCaL { + +class FratTracer : public FileTracer { + + Internal *internal; + File *file; + bool binary; + bool with_antecedents; + +#ifndef CADICAL_QUIET + int64_t added, deleted; + int64_t finalized, original; +#endif + + vector<int64_t> delete_ids; + + void put_binary_zero (); + void put_binary_lit (int external_lit); + void put_binary_id (int64_t id, bool = false); + + // support FRAT + void frat_add_original_clause (int64_t, const vector<int> &); + void frat_add_derived_clause (int64_t, const vector<int> &); + void frat_add_derived_clause (int64_t, const vector<int> &, + const vector<int64_t> &); + void frat_delete_clause (int64_t, const vector<int> &); + void frat_finalize_clause (int64_t, const vector<int> &); + +public: + // own and delete 'file' + FratTracer (Internal *, File *file, bool binary, bool antecedents); + ~FratTracer (); + + void connect_internal (Internal *i) override; + void begin_proof (int64_t) override {} // skip + + void add_original_clause (int64_t, bool, const vector<int> &, + bool = false) override; + + void add_derived_clause (int64_t, bool, const vector<int> &, + const vector<int64_t> &) override; + + void delete_clause (int64_t, bool, const vector<int> &) override; + + void finalize_clause (int64_t, const vector<int> &) override; + + void report_status (int, int64_t) override {} // skip + +#ifndef CADICAL_QUIET + void print_statistics (); +#endif + bool closed () override; + void close (bool) override; + void flush (bool) override; +}; + +} // namespace CaDiCaL + +ABC_NAMESPACE_CXX_HEADER_END + +#endif diff --git a/src/sat/cadical/global.h b/src/sat/cadical/global.h new file mode 100644 index 0000000000..916246cab7 --- /dev/null +++ b/src/sat/cadical/global.h @@ -0,0 +1,21 @@ +#ifndef ABC_SAT_CADICAL_GLOBAL_HPP_ +#define ABC_SAT_CADICAL_GLOBAL_HPP_ + +// comment out next line to enable cadical debug mode +#define CADICAL_NDEBUG + +#define CADICAL_NBUILD +#define CADICAL_QUIET +#define CADICAL_NCONTRACTS +#define CADICAL_NTRACING +#define CADICAL_NCLOSEFROM + +#ifdef CADICAL_NDEBUG +#define CADICAL_assert(ignore) ((void)0) +#else +#define CADICAL_assert(cond) assert(cond) +#endif + +#include "misc/util/abc_global.h" + +#endif diff --git a/src/sat/cadical/heap.hpp b/src/sat/cadical/heap.hpp new file mode 100644 index 0000000000..c429414132 --- /dev/null +++ b/src/sat/cadical/heap.hpp @@ -0,0 +1,218 @@ +#ifndef _heap_hpp_INCLUDED +#define _heap_hpp_INCLUDED + +#include "global.h" + +#include "util.hpp" // Alphabetically after 'heap.hpp'. + +#include <cassert> + +ABC_NAMESPACE_CXX_HEADER_START + +namespace CaDiCaL { + +using namespace std; + +// This is a priority queue with updates for unsigned integers implemented +// as binary heap. We need to map integer elements added (through +// 'push_back') to positions on the binary heap in 'array'. This map is +// stored in the 'pos' array. This approach is really wasteful (at least in +// terms of memory) if only few and a sparse set of integers is added. So +// it should not be used in this situation. A generic priority queue would +// implement the mapping externally provided by another template parameter. +// Since we use 'UINT_MAX' as 'not contained' flag, we can only have +// 'UINT_MAX - 1' elements in the heap. + +const unsigned invalid_heap_position = UINT_MAX; + +template <class C> class heap { + + vector<unsigned> array; // actual binary heap + vector<unsigned> pos; // positions of elements in array + C less; // less-than for elements + + // Map an element to its position entry in the 'pos' map. + // + unsigned &index (unsigned e) { + if (e >= pos.size ()) + pos.resize (1 + (size_t) e, invalid_heap_position); + unsigned &res = pos[e]; + CADICAL_assert (res == invalid_heap_position || (size_t) res < array.size ()); + return res; + } + + bool has_parent (unsigned e) { return index (e) > 0; } + bool has_left (unsigned e) { + return (size_t) 2 * index (e) + 1 < size (); + } + bool has_right (unsigned e) { + return (size_t) 2 * index (e) + 2 < size (); + } + + unsigned parent (unsigned e) { + CADICAL_assert (has_parent (e)); + return array[(index (e) - 1) / 2]; + } + + unsigned left (unsigned e) { + CADICAL_assert (has_left (e)); + return array[2 * index (e) + 1]; + } + + unsigned right (unsigned e) { + CADICAL_assert (has_right (e)); + return array[2 * index (e) + 2]; + } + + // Exchange elements 'a' and 'b' in 'array' and fix their positions. + // + void exchange (unsigned a, unsigned b) { + unsigned &i = index (a), &j = index (b); + swap (array[i], array[j]); + swap (i, j); + } + + // Bubble up an element as far as necessary. + // + void up (unsigned e) { + unsigned p; + while (has_parent (e) && less ((p = parent (e)), e)) + exchange (p, e); + } + + // Bubble down an element as far as necessary. + // + void down (unsigned e) { + while (has_left (e)) { + unsigned c = left (e); + if (has_right (e)) { + unsigned r = right (e); + if (less (c, r)) + c = r; + } + if (!less (e, c)) + break; + exchange (e, c); + } + } + + // Very expensive checker for the main 'heap' invariant. Can be enabled + // to find violations of antisymmetry in the client implementation of + // 'less' and as well of course bugs in this heap implementation. It + // should be enabled during testing applications of the heap. + // + void check () { +#if 0 // EXPENSIVE HEAP CHECKING IF ENABLED +#warning "expensive checking in heap enabled" + CADICAL_assert (array.size () <= invalid_heap_position); + for (size_t i = 0; i < array.size (); i++) { + size_t l = 2*i + 1, r = 2*i + 2; + if (l < array.size ()) CADICAL_assert (!less (array[i], array[l])); + if (r < array.size ()) CADICAL_assert (!less (array[i], array[r])); + CADICAL_assert (array[i] >= 0); + { + CADICAL_assert ((size_t) array[i] < pos.size ()); + CADICAL_assert (i == (size_t) pos[array[i]]); + } + } + for (size_t i = 0; i < pos.size (); i++) { + if (pos[i] == invalid_heap_position) continue; + CADICAL_assert (pos[i] < array.size ()); + CADICAL_assert (array[pos[i]] == (unsigned) i); + } +#endif + } + +public: + heap (const C &c) : less (c) {} + + // Number of elements in the heap. + // + size_t size () const { return array.size (); } + + // Check if no more elements are in the heap. + // + bool empty () const { return array.empty (); } + + // Check whether 'e' is already in the heap. + // + bool contains (unsigned e) const { + if ((size_t) e >= pos.size ()) + return false; + return pos[e] != invalid_heap_position; + } + + // Add a new (not contained) element 'e' to the heap. + // + void push_back (unsigned e) { + CADICAL_assert (!contains (e)); + size_t i = array.size (); + CADICAL_assert (i < (size_t) invalid_heap_position); + array.push_back (e); + index (e) = (unsigned) i; + up (e); + down (e); + check (); + } + + // Returns the maximum element in the heap. + // + unsigned front () const { + CADICAL_assert (!empty ()); + return array[0]; + } + + // Removes the maximum element in the heap. + // + unsigned pop_front () { + CADICAL_assert (!empty ()); + unsigned res = array[0], last = array.back (); + if (size () > 1) + exchange (res, last); + index (res) = invalid_heap_position; + array.pop_back (); + if (size () > 1) + down (last); + check (); + return res; + } + + // Notify the heap, that evaluation of 'less' has changed for 'e'. + // + void update (unsigned e) { + CADICAL_assert (contains (e)); + up (e); + down (e); + check (); + } + + void clear () { + array.clear (); + pos.clear (); + } + + void erase () { + erase_vector (array); + erase_vector (pos); + } + + void shrink () { + shrink_vector (array); + shrink_vector (pos); + } + + // Standard iterators 'inherited' from 'vector'. + // + typedef typename vector<unsigned>::iterator iterator; + typedef typename vector<unsigned>::const_iterator const_iterator; + iterator begin () { return array.begin (); } + iterator end () { return array.end (); } + const_iterator begin () const { return array.begin (); } + const_iterator end () const { return array.end (); } +}; + +} // namespace CaDiCaL + +ABC_NAMESPACE_CXX_HEADER_END + +#endif diff --git a/src/sat/cadical/idruptracer.hpp b/src/sat/cadical/idruptracer.hpp new file mode 100644 index 0000000000..4ea478705b --- /dev/null +++ b/src/sat/cadical/idruptracer.hpp @@ -0,0 +1,116 @@ +#ifndef _idruptracer_h_INCLUDED +#define _idruptracer_h_INCLUDED + +#include "global.h" + +ABC_NAMESPACE_CXX_HEADER_START + +class FileTracer; + +namespace CaDiCaL { + +struct IdrupClause { + IdrupClause *next; // collision chain link for hash table + uint64_t hash; // previously computed full 64-bit hash + int64_t id; // id of clause + unsigned size; + int literals[1]; +}; + +class IdrupTracer : public FileTracer { + + Internal *internal; + File *file; + bool binary; + bool piping; // The 'file' is a pipe and needs eagerly flushing. + + // hash table for conclusion + // + uint64_t num_clauses; // number of clauses in hash table + uint64_t size_clauses; // size of clause hash table + IdrupClause **clauses; // hash table of clauses + vector<int> imported_clause; + vector<int> assumptions; + + static const unsigned num_nonces = 4; + + uint64_t nonces[num_nonces]; // random numbers for hashing + uint64_t last_hash; // last computed hash value of clause + int64_t last_id; // id of the last added clause + IdrupClause *last_clause; + uint64_t compute_hash (int64_t); // compute and save hash value of clause + + IdrupClause *new_clause (); + void delete_clause (IdrupClause *); + + static uint64_t reduce_hash (uint64_t hash, uint64_t size); + + void enlarge_clauses (); // enlarge hash table for clauses + void insert (); // insert clause in hash table + bool + find_and_delete (const int64_t); // find clause position in hash table + +#ifndef CADICAL_QUIET + int64_t added, deleted, weakened, restore, original, solved; +#endif + + void flush_if_piping (); + + void put_binary_zero (); + void put_binary_lit (int external_lit); + void put_binary_id (int64_t id, bool = false); + + void idrup_add_derived_clause (const vector<int> &clause); + void idrup_delete_clause (int64_t id, const vector<int> &clause); + void idrup_add_restored_clause (const vector<int> &clause); + void idrup_add_original_clause (const vector<int> &clause); + void idrup_conclude_and_delete (const vector<int64_t> &conclusion); + void idrup_report_status (int status); + void idrup_conclude_sat (const vector<int> &model); + void idrup_conclude_unknown (const vector<int> &trail); + void idrup_solve_query (); + +public: + IdrupTracer (Internal *, File *file, bool); + ~IdrupTracer (); + + // proof section: + void add_derived_clause (int64_t, bool, const vector<int> &, + const vector<int64_t> &) override; + void add_assumption_clause (int64_t, const vector<int> &, + const vector<int64_t> &) override; + void weaken_minus (int64_t, const vector<int> &) override; + void delete_clause (int64_t, bool, const vector<int> &) override; + void add_original_clause (int64_t, bool, const vector<int> &, + bool = false) override; + void report_status (int, int64_t) override; + void conclude_sat (const vector<int> &) override; + void conclude_unsat (ConclusionType, const vector<int64_t> &) override; + void conclude_unknown (const vector<int> &) override; + + void solve_query () override; + void add_assumption (int) override; + void reset_assumptions () override; + + // skip + void begin_proof (int64_t) override {} + void finalize_clause (int64_t, const vector<int> &) override {} + void strengthen (int64_t) override {} + void add_constraint (const vector<int> &) override {} + + // logging and file io + void connect_internal (Internal *i) override; + +#ifndef CADICAL_QUIET + void print_statistics (); +#endif + bool closed () override; + void close (bool) override; + void flush (bool) override; +}; + +} // namespace CaDiCaL + +ABC_NAMESPACE_CXX_HEADER_END + +#endif diff --git a/src/sat/cadical/instantiate.hpp b/src/sat/cadical/instantiate.hpp new file mode 100644 index 0000000000..f6c8767dd9 --- /dev/null +++ b/src/sat/cadical/instantiate.hpp @@ -0,0 +1,51 @@ +#ifndef _instantiate_hpp_INCLUDED +#define _instantiate_hpp_INCLUDED + +#include "global.h" + +ABC_NAMESPACE_CXX_HEADER_START + +namespace CaDiCaL { + +// We are trying to remove literals in clauses, which occur in few clauses +// and further restrict this removal to variables for which variable +// elimination failed. Thus if for instance we succeed in removing the +// single occurrence of a literal, pure literal elimination can +// eliminate the corresponding variable in the next variable elimination +// round. The set of such literal clause candidate pairs is collected at +// the end of a variable elimination round and tried before returning. The +// name of this technique is inspired by 'variable instantiation' as +// described in [AnderssonBjesseCookHanna-DAC'02] and apparently +// successfully used in the 'Oepir' SAT solver. + +struct Clause; +struct Internal; + +class Instantiator { + + friend struct Internal; + + struct Candidate { + int lit; + int size; + size_t negoccs; + Clause *clause; + Candidate (int l, Clause *c, int s, size_t n) + : lit (l), size (s), negoccs (n), clause (c) {} + }; + + vector<Candidate> candidates; + +public: + void candidate (int l, Clause *c, int s, size_t n) { + candidates.push_back (Candidate (l, c, s, n)); + } + + operator bool () const { return !candidates.empty (); } +}; + +} // namespace CaDiCaL + +ABC_NAMESPACE_CXX_HEADER_END + +#endif diff --git a/src/sat/cadical/internal.hpp b/src/sat/cadical/internal.hpp new file mode 100644 index 0000000000..d86479a522 --- /dev/null +++ b/src/sat/cadical/internal.hpp @@ -0,0 +1,1866 @@ +#ifndef _internal_hpp_INCLUDED +#define _internal_hpp_INCLUDED + +#include "global.h" + +/*------------------------------------------------------------------------*/ + +// Wrapped build specific headers which should go first. + +#include "inttypes.hpp" + +/*------------------------------------------------------------------------*/ + +// Common 'C' headers. + +#include <cassert> +#include <cctype> +#include <climits> +#include <cmath> +#include <csignal> +#include <cstdio> +#include <cstdlib> +#include <cstring> + +// Less common 'C' header. + +#ifndef WIN32 +extern "C" { +#include <unistd.h> +} +#endif + +/*------------------------------------------------------------------------*/ + +// Common 'C++' headers. + +#include <algorithm> +#include <queue> +#include <string> +#include <unordered_set> +#include <vector> + +/*------------------------------------------------------------------------*/ + +// All internal headers are included here. This gives a nice overview on +// what is needed altogether. The 'Internal' class needs almost all the +// headers anyhow (since the idea is to avoid pointer references as much as +// possible). Most implementation files need to see the definition of the +// 'Internal' too. Thus there is no real advantage in trying to reduce the +// number of header files included here. The other benefit of having all +// header files here is that '.cpp' files then only need to include this. + +#include "arena.hpp" +#include "averages.hpp" +#include "bins.hpp" +#include "block.hpp" +#include "cadical.hpp" +#include "checker.hpp" +#include "clause.hpp" +#include "config.hpp" +#include "congruence.hpp" +#include "contract.hpp" +#include "cover.hpp" +#include "decompose.hpp" +#include "drattracer.hpp" +#include "elim.hpp" +#include "ema.hpp" +#include "external.hpp" +#include "factor.hpp" +#include "file.hpp" +#include "flags.hpp" +#include "format.hpp" +#include "frattracer.hpp" +#include "heap.hpp" +#include "idruptracer.hpp" +#include "instantiate.hpp" +#include "internal.hpp" +#include "level.hpp" +#include "lidruptracer.hpp" +#include "limit.hpp" +#include "logging.hpp" +#include "lratchecker.hpp" +#include "lrattracer.hpp" +#include "message.hpp" +#include "occs.hpp" +#include "options.hpp" +#include "parse.hpp" +#include "phases.hpp" +#include "profile.hpp" +#include "proof.hpp" +#include "queue.hpp" +#include "radix.hpp" +#include "random.hpp" +#include "range.hpp" +#include "reap.hpp" +#include "reluctant.hpp" +#include "resources.hpp" +#include "score.hpp" +#include "stats.hpp" +#include "sweep.hpp" +#include "terminal.hpp" +#include "tracer.hpp" +#include "util.hpp" +#include "var.hpp" +#include "veripbtracer.hpp" +#include "version.hpp" +#include "vivify.hpp" +#include "watch.hpp" + +// c headers +//extern "C" { +#include "kitten.h" +//} +/*------------------------------------------------------------------------*/ + +ABC_NAMESPACE_CXX_HEADER_START + +namespace CaDiCaL { + +using namespace std; + +struct Coveror; +struct External; +struct Walker; +class Tracer; +class FileTracer; +class StatTracer; + +struct CubesWithStatus { + int status = 0; + std::vector<std::vector<int>> cubes; +}; + +/*------------------------------------------------------------------------*/ + +struct Internal { + + /*----------------------------------------------------------------------*/ + + // The actual internal state of the solver is set and maintained in this + // section. This is currently only used for debugging and testing. + + enum Mode { + BLOCK = (1 << 0), + CONDITION = (1 << 1), + CONGRUENCE = (1 << 2), + COVER = (1 << 3), + DECOMP = (1 << 4), + DEDUP = (1 << 5), + ELIM = (1 << 6), + FACTOR = (1 << 7), + LUCKY = (1 << 8), + PROBE = (1 << 9), + SEARCH = (1 << 10), + SIMPLIFY = (1 << 11), + SUBSUME = (1 << 12), + SWEEP = (1 << 13), + TERNARY = (1 << 14), + TRANSRED = (1 << 15), + VIVIFY = (1 << 16), + WALK = (1 << 17), + }; + + bool in_mode (Mode m) const { return (mode & m) != 0; } + void set_mode (Mode m) { + CADICAL_assert (!(mode & m)); + mode |= m; + } + void reset_mode (Mode m) { + CADICAL_assert (mode & m); + mode &= ~m; + } + void require_mode (Mode m) const { CADICAL_assert (mode & m), (void) m; } + + /*----------------------------------------------------------------------*/ + + int mode; // current internal state + int tier1[2] = { + 2, 2}; // tier1 limit for 0=focused, 1=stable; aka tier1[stable] + int tier2[2] = { + 6, 6}; // tier2 limit for 0=focused, 1=stable; aka tier1[stable] + bool unsat; // empty clause found or learned + bool iterating; // report learned unit ('i' line) + bool localsearching; // true during local search + bool lookingahead; // true during look ahead + bool preprocessing; // true during preprocessing + bool protected_reasons; // referenced reasons are protected + bool force_saved_phase; // force saved phase in decision + bool searching_lucky_phases; // during 'lucky_phases' + bool stable; // true during stabilization phase + bool reported; // reported in this solving call + bool external_prop; // true if an external propagator is connected + bool did_external_prop; // true if ext. propagation happened + bool external_prop_is_lazy; // true if the external propagator is lazy + bool forced_backt_allowed; // external propagator can force backtracking + bool private_steps; // no notification of ext. prop during these steps + char rephased; // last type of resetting phases + Reluctant reluctant; // restart counter in stable mode + size_t vsize; // actually allocated variable data size + int max_var; // internal maximum variable index + int64_t clause_id; // last used id for clauses + int64_t original_id; // ids for original clauses to produce LRAT + int64_t reserved_ids; // number of reserved ids for original clauses + int64_t conflict_id; // store conflict id for finalize (frat) + int64_t saved_decisions; // to compute decision rate average + bool concluded; // keeps track of conclude + vector<int64_t> conclusion; // store ids of conclusion clauses + vector<int64_t> + unit_clauses_idx; // keep track of unit_clauses (LRAT/FRAT) + vector<int64_t> lrat_chain; // create LRAT in solver: option lratdirect + vector<int64_t> mini_chain; // used to create LRAT in minimize + vector<int64_t> minimize_chain; // used to create LRAT in minimize + vector<int64_t> unit_chain; // used to avoid duplicate units + vector<Clause *> inst_chain; // for LRAT in instantiate + vector<vector<vector<int64_t>>> + probehbr_chains; // only used if opts.probehbr=false + bool lrat; // generate LRAT internally + bool frat; // finalize non-deleted clauses in proof + int level; // decision level ('control.size () - 1') + Phases phases; // saved, target and best phases + signed char *vals; // assignment [-max_var,max_var] + vector<signed char> marks; // signed marks [1,max_var] + vector<unsigned> frozentab; // frozen counters [1,max_var] + vector<int> i2e; // maps internal 'idx' to external 'lit' + vector<unsigned> relevanttab; // Reference counts for observed variables. + Queue queue; // variable move to front decision queue + Links links; // table of links for decision queue + double score_inc; // current score increment + ScoreSchedule scores; // score based decision priority queue + vector<double> stab; // table of variable scores [1,max_var] + vector<Var> vtab; // variable table [1,max_var] + vector<int> parents; // parent literals during probing + vector<Flags> ftab; // variable and literal flags + vector<int64_t> btab; // enqueue time stamps for queue + vector<int64_t> gtab; // time stamp table to recompute glue + vector<Occs> otab; // table of occurrences for all literals + vector<Occs> rtab; // table of redundant occurrences + vector<int> ptab; // table for caching probing attempts + vector<int64_t> ntab; // number of one-sided occurrences table + vector<Bins> big; // binary implication graph + vector<Watches> wtab; // table of watches for all literals + Clause *conflict; // set in 'propagation', reset in 'analyze' + Clause *ignore; // ignored during 'vivify_propagate' + Clause *dummy_binary; // Dummy binary clause for subsumption + Clause *external_reason; // used as reason at external propagations + Clause *newest_clause; // used in external_propagate + bool force_no_backtrack; // for new clauses with external propagator + bool from_propagator; // differentiate new clauses... + bool ext_clause_forgettable; // Is new clause from propagator forgettable + int tainted_literal; // used for ILB + size_t notified; // next trail position to notify external prop + Clause *probe_reason; // set during probing + size_t propagated; // next trail position to propagate + size_t propagated2; // next binary trail position to propagate + size_t propergated; // propagated without blocking literals + size_t best_assigned; // best maximum assigned ever + size_t target_assigned; // maximum assigned without conflict + size_t no_conflict_until; // largest trail prefix without conflict + vector<int> trail; // currently assigned literals + vector<int> clause; // simplified in parsing & learning + vector<int> assumptions; // assumed literals + vector<int> constraint; // literals of the constraint + bool unsat_constraint; // constraint used for unsatisfiability? + bool marked_failed; // are the failed assumptions marked? + vector<int> original; // original added literals + vector<int> levels; // decision levels in learned clause + vector<int> analyzed; // analyzed literals in 'analyze' + vector<int> unit_analyzed; // to avoid duplicate units in lrat_chain + vector<int> sign_marked; // literals skipped in 'decompose' + vector<int> minimized; // removable or poison in 'minimize' + vector<int> shrinkable; // removable or poison in 'shrink' + Reap reap; // radix heap for shrink + + vector<int> sweep_schedule; // remember sweep varibles to reschedule + bool sweep_incomplete; // sweep + + cadical_kitten *citten; + + size_t num_assigned; // check for satisfied + + vector<int> probes; // remaining scheduled probes + vector<Level> control; // 'level + 1 == control.size ()' + vector<Clause *> clauses; // ordered collection of all clauses + Averages averages; // glue, size, jump moving averages + Delay delay[2]; // Delay certain functions + Delay congruence_delay; // Delay congruence if not successful recently + Limit lim; // limits for various phases + Last last; // statistics at last occurrence + Inc inc; // increments on limits + + Delay delaying_vivify_irredundant; + Delay delaying_sweep; + + Proof *proof; // abstraction layer between solver and tracers + vector<Tracer *> + tracers; // proof tracing objects (ie interpolant calculator) + vector<FileTracer *> + file_tracers; // file proof tracers (ie DRAT, LRAT...) + vector<StatTracer *> stat_tracers; // checkers + + Options opts; // run-time options + Stats stats; // statistics +#ifndef CADICAL_QUIET + Profiles profiles; // time profiles for various functions + bool force_phase_messages; // force 'phase (...)' messages +#endif + Arena arena; // memory arena for moving garbage collector + Format error_message; // provide persistent error message + string prefix; // verbose messages prefix + + Internal *internal; // proxy to 'this' in macros + External *external; // proxy to 'external' buddy in 'Solver' + + const unsigned max_used = 255; // must fix into the header of the clause! + /*----------------------------------------------------------------------*/ + + // Asynchronous termination flag written by 'terminate' and read by + // 'terminated_asynchronously' (the latter at the end of this header). + // + volatile bool termination_forced; + + /*----------------------------------------------------------------------*/ + + const Range vars; // Provides safe variable iteration. + const Sange lits; // Provides safe literal iteration. + + /*----------------------------------------------------------------------*/ + + Internal (); + ~Internal (); + + /*----------------------------------------------------------------------*/ + + // Internal delegates and helpers for corresponding functions in + // 'External' and 'Solver'. The 'init_vars' function initializes + // variables up to and including the requested variable index. + // + void init_vars (int new_max_var); + + void init_enqueue (int idx); + void init_queue (int old_max_var, int new_max_var); + + void init_scores (int old_max_var, int new_max_var); + + void add_original_lit (int lit); + + // only able to restore irredundant clause + void finish_added_clause_with_id (int64_t id, bool restore = false); + + // Reserve ids for original clauses to produce lrat + void reserve_ids (int number); + + // Enlarge tables. + // + void enlarge_vals (size_t new_vsize); + void enlarge (int new_max_var); + + // A variable is 'active' if it is not eliminated nor fixed. + // + bool active (int lit) { return flags (lit).active (); } + + int active () const { + int res = stats.active; +#ifndef CADICAL_NDEBUG + int tmp = max_var; + tmp -= stats.unused; + tmp -= stats.now.fixed; + tmp -= stats.now.eliminated; + tmp -= stats.now.substituted; + tmp -= stats.now.pure; + CADICAL_assert (tmp >= 0); + CADICAL_assert (tmp == res); +#endif + return res; + } + + void reactivate (int lit); // During 'restore'. + + // Currently remaining active redundant and irredundant clauses. + + int64_t redundant () const { return stats.current.redundant; } + + int64_t irredundant () const { return stats.current.irredundant; } + + double clause_variable_ratio () const { + return relative (irredundant (), active ()); + } + + // Scale values relative to clause variable ratio. + // + double scale (double v) const; + + // Unsigned literals (abs) with checks. + // + int vidx (int lit) const { + int idx; + CADICAL_assert (lit); + CADICAL_assert (lit != INT_MIN); + idx = abs (lit); + CADICAL_assert (idx <= max_var); + return idx; + } + + // Unsigned version with LSB denoting sign. This is used in indexing + // arrays by literals. The idea is to keep the elements in such an array + // for both the positive and negated version of a literal close together. + // + unsigned vlit (int lit) const { + return (lit < 0) + 2u * (unsigned) vidx (lit); + } + + int u2i (unsigned u) { + CADICAL_assert (u > 1); + int res = u / 2; + CADICAL_assert (res <= max_var); + if (u & 1) + res = -res; + return res; + } + + int citten2lit (unsigned ulit) { + int res = (ulit / 2) + 1; + CADICAL_assert (res <= max_var); + if (ulit & 1) + res = -res; + return res; + } + + unsigned lit2citten (int lit) { + int idx = vidx (lit) - 1; + return (lit < 0) + 2u * (unsigned) idx; + } + + int64_t unit_id (int lit) const { + CADICAL_assert (lrat || frat); + CADICAL_assert (val (lit) > 0); + const unsigned uidx = vlit (lit); + int64_t id = unit_clauses_idx[uidx]; + CADICAL_assert (id); + return id; + } + + inline int64_t &unit_clauses (int uidx) { + CADICAL_assert (lrat || frat); + CADICAL_assert (uidx > 0); + CADICAL_assert ((size_t) uidx < unit_clauses_idx.size ()); + return unit_clauses_idx[uidx]; + } + + // Helper functions to access variable and literal data. + // + Var &var (int lit) { return vtab[vidx (lit)]; } + Link &link (int lit) { return links[vidx (lit)]; } + Flags &flags (int lit) { return ftab[vidx (lit)]; } + int64_t &bumped (int lit) { return btab[vidx (lit)]; } + int &propfixed (int lit) { return ptab[vlit (lit)]; } + double &score (int lit) { return stab[vidx (lit)]; } + + const Flags &flags (int lit) const { return ftab[vidx (lit)]; } + + bool occurring () const { return !otab.empty (); } + bool watching () const { return !wtab.empty (); } + + Bins &bins (int lit) { return big[vlit (lit)]; } + Occs &occs (int lit) { return otab[vlit (lit)]; } + int64_t &noccs (int lit) { return ntab[vlit (lit)]; } + Watches &watches (int lit) { return wtab[vlit (lit)]; } + + // Variable bumping through exponential VSIDS (EVSIDS) as in MiniSAT. + // + bool use_scores () const { return opts.score && stable; } + void bump_variable_score (int lit); + void bump_variable_score_inc (); + void rescale_variable_scores (); + + // Marking variables with a sign (positive or negative). + // + signed char marked (int lit) const { + signed char res = marks[vidx (lit)]; + if (lit < 0) + res = -res; + return res; + } + void mark (int lit) { + CADICAL_assert (!marked (lit)); + marks[vidx (lit)] = sign (lit); + CADICAL_assert (marked (lit) > 0); + CADICAL_assert (marked (-lit) < 0); + } + void unmark (int lit) { + marks[vidx (lit)] = 0; + CADICAL_assert (!marked (lit)); + } + + // Use only bits 6 and 7 to store the sign or zero. The remaining + // bits can be used as additional flags. + // + signed char marked67 (int lit) const { + signed char res = marks[vidx (lit)] >> 6; + if (lit < 0) + res = -res; + return res; + } + void mark67 (int lit) { + signed char &m = marks[vidx (lit)]; + const signed char mask = 0x3f; +#ifndef CADICAL_NDEBUG + const signed char bits = m & mask; +#endif + m = (m & mask) | (sign (lit) << 6); + CADICAL_assert (marked (lit) > 0); + CADICAL_assert (marked (-lit) < 0); + CADICAL_assert ((m & mask) == bits); + CADICAL_assert (marked67 (lit) > 0); + CADICAL_assert (marked67 (-lit) < 0); + } + void unmark67 (int lit) { + signed char &m = marks[vidx (lit)]; + const signed char mask = 0x3f; +#ifndef CADICAL_NDEBUG + const signed bits = m & mask; +#endif + m &= mask; + CADICAL_assert ((m & mask) == bits); + } + + void unmark (vector<int> &lits) { + for (const auto &lit : lits) + unmark (lit); + } + + // The other 6 bits of the 'marks' bytes can be used as additional + // (unsigned) marking bits. Currently we only use the least significant + // bit in 'condition' to mark variables in the conditional part. + // + bool getbit (int lit, int bit) const { + CADICAL_assert (0 <= bit), CADICAL_assert (bit < 6); + return marks[vidx (lit)] & (1 << bit); + } + void setbit (int lit, int bit) { + CADICAL_assert (0 <= bit), CADICAL_assert (bit < 6); + CADICAL_assert (!getbit (lit, bit)); + marks[vidx (lit)] |= (1 << bit); + CADICAL_assert (getbit (lit, bit)); + } + void unsetbit (int lit, int bit) { + CADICAL_assert (0 <= bit), CADICAL_assert (bit < 6); + CADICAL_assert (getbit (lit, bit)); + marks[vidx (lit)] &= ~(1 << bit); + CADICAL_assert (!getbit (lit, bit)); + } + + // Marking individual literals. + // + bool marked2 (int lit) const { + unsigned res = marks[vidx (lit)]; + CADICAL_assert (res <= 3); + unsigned bit = bign (lit); + return (res & bit) != 0; + } + void mark2 (int lit) { + marks[vidx (lit)] |= bign (lit); + CADICAL_assert (marked2 (lit)); + } + + // marks bits 1,2,3 and 4,5,6 depending on fact and sign of lit + // + bool getfact (int lit, int fact) const { + CADICAL_assert (fact == 1 || fact == 2 || fact == 4); + int res = marks[vidx (lit)]; + if (lit < 0) { + res >>= 3; + } else { + res &= 7; + } + // CADICAL_assert (!res || res == 1 || res == 2 || res == 4); + return res & fact; + } + + void markfact (int lit, int fact) { + CADICAL_assert (fact == 1 || fact == 2 || fact == 4); + CADICAL_assert (!getfact (lit, fact)); +#ifndef CADICAL_NDEBUG + int before = getfact (-lit, fact); +#endif + int res = marks[vidx (lit)]; + if (lit < 0) { + res |= fact << 3; + } else { + res |= fact; + } + marks[vidx (lit)] = res; + CADICAL_assert (getfact (lit, fact)); +#ifndef CADICAL_NDEBUG + CADICAL_assert (getfact (-lit, fact) == before); +#endif + } + + void unmarkfact (int lit, int fact) { + CADICAL_assert (fact == 1 || fact == 2 || fact == 4); + CADICAL_assert (getfact (lit, fact)); + int res = marks[vidx (lit)]; + if (lit < 0) { + res &= ~(fact << 3); + } else { + res &= ~fact; + } + marks[vidx (lit)] = res; + CADICAL_assert (!getfact (lit, fact)); + } + + // Marking and unmarking of all literals in a clause. + // + void mark_clause (); // mark 'this->clause' + void mark (Clause *); + void mark2 (Clause *); + void unmark_clause (); // unmark 'this->clause' + void unmark (Clause *); + + // Watch literal 'lit' in clause with blocking literal 'blit'. + // Inlined here, since it occurs in the tight inner loop of 'propagate'. + // + inline void watch_literal (int lit, int blit, Clause *c) { + CADICAL_assert (lit != blit); + Watches &ws = watches (lit); + ws.push_back (Watch (blit, c)); + LOG (c, "watch %d blit %d in", lit, blit); + } + + // Add two watches to a clause. This is used initially during allocation + // of a clause and during connecting back all watches after preprocessing. + // + inline void watch_clause (Clause *c) { + const int l0 = c->literals[0]; + const int l1 = c->literals[1]; + watch_literal (l0, l1, c); + watch_literal (l1, l0, c); + } + + inline void unwatch_clause (Clause *c) { + const int l0 = c->literals[0]; + const int l1 = c->literals[1]; + remove_watch (watches (l0), c); + remove_watch (watches (l1), c); + } + + // Update queue to point to last potentially still unassigned variable. + // All variables after 'queue.unassigned' in bump order are assumed to be + // assigned. Then update the 'queue.bumped' field and log it. This is + // inlined here since it occurs in several inner loops. + // + inline void update_queue_unassigned (int idx) { + CADICAL_assert (0 < idx); + CADICAL_assert (idx <= max_var); + queue.unassigned = idx; + queue.bumped = btab[idx]; + LOG ("queue unassigned now %d bumped %" PRId64 "", idx, btab[idx]); + } + + void bump_queue (int idx); + + // Mark (active) variables as eliminated, substituted, pure or fixed, + // which turns them into inactive variables. + // + void mark_eliminated (int); + void mark_substituted (int); + void mark_active (int); + void mark_fixed (int); + void mark_pure (int); + + // Managing clauses in 'clause.cpp'. Without explicit 'Clause' argument + // these functions work on the global temporary 'clause'. + // + Clause *new_clause (bool red, int glue = 0); + void promote_clause (Clause *, int new_glue); + void promote_clause_glue_only (Clause *, int new_glue); + size_t shrink_clause (Clause *, int new_size); + void minimize_sort_clause (); + void shrink_and_minimize_clause (); + void reset_shrinkable (); + void mark_shrinkable_as_removable (int, std::vector<int>::size_type); + int shrink_literal (int, int, unsigned); + unsigned shrunken_block_uip (int, int, + std::vector<int>::reverse_iterator &, + std::vector<int>::reverse_iterator &, + std::vector<int>::size_type, const int); + void shrunken_block_no_uip (const std::vector<int>::reverse_iterator &, + const std::vector<int>::reverse_iterator &, + unsigned &, const int); + void push_literals_of_block (const std::vector<int>::reverse_iterator &, + const std::vector<int>::reverse_iterator &, + int, unsigned); + unsigned shrink_next (int, unsigned &, unsigned &); + std::vector<int>::reverse_iterator + minimize_and_shrink_block (std::vector<int>::reverse_iterator &, + unsigned int &, unsigned int &, const int); + unsigned shrink_block (std::vector<int>::reverse_iterator &, + std::vector<int>::reverse_iterator &, int, + unsigned &, unsigned &, const int, unsigned); + unsigned shrink_along_reason (int, int, bool, bool &, unsigned); + + void deallocate_clause (Clause *); + void delete_clause (Clause *); + void mark_garbage (Clause *); + void assign_original_unit (int64_t, int); + void add_new_original_clause (int64_t); + Clause *new_learned_redundant_clause (int glue); + Clause *new_hyper_binary_resolved_clause (bool red, int glue); + Clause *new_clause_as (const Clause *orig); + Clause *new_resolved_irredundant_clause (); + + // Forward reasoning through propagation in 'propagate.cpp'. + // + int assignment_level (int lit, Clause *); + void build_chain_for_units (int lit, Clause *reason, bool forced); + void build_chain_for_empty (); + void search_assign (int lit, Clause *); + void search_assign_driving (int lit, Clause *reason); + void search_assign_external (int lit); + void search_assume_decision (int decision); + void assign_unit (int lit); + int64_t cache_lines (size_t bytes) { return (bytes + 127) / 128; } + int64_t cache_lines (size_t n, size_t bytes) { + return cache_lines (n * bytes); + } + bool propagate (); + +#ifdef PROFILE_MODE + bool propagate_wrapper (); + bool propagate_unstable (); + bool propagate_stable (); + void analyze_wrapper (); + void analyze_unstable (); + void analyze_stable (); + int decide_wrapper (); + int decide_stable (); + int decide_unstable (); +#else +#define propagate_wrapper propagate +#define analyze_wrapper analyze +#define decide_wrapper decide +#endif + + void propergate (); // Repropagate without blocking literals. + + // Undo and restart in 'backtrack.cpp'. + // + void unassign (int lit); + void update_target_and_best (); + void backtrack (int target_level = 0); + void backtrack_without_updating_phases (int target_level = 0); + + // Minimized learned clauses in 'minimize.cpp'. + // + bool minimize_literal (int lit, int depth = 0); + void minimize_clause (); + void calculate_minimize_chain (int lit, std::vector<int> &stack); + + // Learning from conflicts in 'analyze.cc'. + // + void learn_empty_clause (); + void learn_unit_clause (int lit); + + void bump_variable (int lit); + void bump_variables (); + int recompute_glue (Clause *); + void bump_clause (Clause *); + void bump_clause2 (Clause *); + void clear_unit_analyzed_literals (); + void clear_analyzed_literals (); + void clear_analyzed_levels (); + void clear_minimized_literals (); + bool bump_also_reason_literal (int lit); + void bump_also_reason_literals (int lit, int depth_limit, + size_t size_limit); + void bump_also_all_reason_literals (); + void analyze_literal (int lit, int &open, int &resolvent_size, + int &antecedent_size); + void analyze_reason (int lit, Clause *, int &open, int &resolvent_size, + int &antecedent_size); + Clause *new_driving_clause (const int glue, int &jump); + int find_conflict_level (int &forced); + int determine_actual_backtrack_level (int jump); + void otfs_strengthen_clause (Clause *, int, int, + const std::vector<int> &); + void otfs_subsume_clause (Clause *subsuming, Clause *subsumed); + int otfs_find_backtrack_level (int &forced); + Clause *on_the_fly_strengthen (Clause *conflict, int lit); + void update_decision_rate_average (); + void analyze (); + void iterate (); // report learned unit clause + + // Learning from external propagator in 'external_propagate.cpp' + // + bool external_propagate (); + bool external_check_solution (); + void add_external_clause (int propagated_lit = 0, + bool no_backtrack = false); + Clause *learn_external_reason_clause (int lit, int falsified_elit = 0, + bool no_backtrack = false); + Clause *wrapped_learn_external_reason_clause (int lit); + void explain_external_propagations (); + void explain_reason (int lit, Clause *, int &open); + void move_literals_to_watch (); + void handle_external_clause (Clause *); + void notify_assignments (); + void notify_decision (); + void notify_backtrack (size_t new_level); + void force_backtrack (size_t new_level); + int ask_decision (); + bool ask_external_clause (); + void add_observed_var (int ilit); + void remove_observed_var (int ilit); + bool observed (int ilit) const; + bool is_decision (int ilit); + void check_watched_literal_invariants (); + void set_tainted_literal (); + void renotify_trail_after_ilb (); + void renotify_trail_after_local_search (); + void renotify_full_trail (); + void connect_propagator (); + void mark_garbage_external_forgettable (int64_t id); + bool is_external_forgettable (int64_t id); +#ifndef CADICAL_NDEBUG + bool get_merged_literals (std::vector<int> &); + void get_all_fixed_literals (std::vector<int> &); +#endif + + void recompute_tier (); + // Use last learned clause to subsume some more. + // + void eagerly_subsume_recently_learned_clauses (Clause *); + + // Restarting policy in 'restart.cc'. + // + bool stabilizing (); + bool restarting (); + int reuse_trail (); + void restart (); + + // Functions to set and reset certain 'phases'. + // + void clear_phases (vector<signed char> &); // reset argument to zero + void copy_phases (vector<signed char> &); // copy 'saved' to argument + + // Resetting the saved phased in 'rephase.cpp'. + // + bool rephasing (); + char rephase_best (); + char rephase_flipping (); + char rephase_inverted (); + char rephase_original (); + char rephase_random (); + char rephase_walk (); + void shuffle_scores (); + void shuffle_queue (); + void rephase (); + + // Lucky feasible case checking. + // + int unlucky (int res); + bool lucky_propagate_discrepency (int); + int trivially_false_satisfiable (); + int trivially_true_satisfiable (); + int forward_false_satisfiable (); + int forward_true_satisfiable (); + int backward_false_satisfiable (); + int backward_true_satisfiable (); + int positive_horn_satisfiable (); + int negative_horn_satisfiable (); + + // Asynchronous terminating check. + // + bool terminated_asynchronously (int factor = 1); + + bool search_limits_hit (); + + void terminate () { + LOG ("forcing asynchronous termination"); + termination_forced = true; + } + + // Reducing means determining useless clauses with 'reduce' in + // 'reduce.cpp' as well as root level satisfied clause and then removing + // those which are not used as reason anymore with garbage collection. + // + bool flushing (); + bool reducing (); + void protect_reasons (); + void mark_clauses_to_be_flushed (); + void mark_useless_redundant_clauses_as_garbage (); + bool propagate_out_of_order_units (); + void unprotect_reasons (); + void reduce (); + + // Garbage collection in 'collect.cpp' called from 'reduce' and during + // inprocessing and preprocessing. + // + int clause_contains_fixed_literal (Clause *); + void remove_falsified_literals (Clause *); + void mark_satisfied_clauses_as_garbage (); + void copy_clause (Clause *); + void flush_watches (int lit, Watches &); + size_t flush_occs (int lit); + void flush_all_occs_and_watches (); + void update_reason_references (); + void copy_non_garbage_clauses (); + void delete_garbage_clauses (); + void check_clause_stats (); + void check_var_stats (); + bool arenaing (); + void garbage_collection (); + + // only remove binary clauses from the watches + void remove_garbage_binaries (); + + // Set-up occurrence list counters and containers. + // + void init_occs (); + void init_bins (); + void init_noccs (); + void clear_noccs (); + void clear_occs (); + void reset_occs (); + void reset_bins (); + void reset_noccs (); + + // Operators on watches. + // + void init_watches (); + void connect_watches (bool irredundant_only = false); + void connect_binary_watches (); + void sort_watches (); + void clear_watches (); + void reset_watches (); + + // Regular forward subsumption checking in 'subsume.cpp'. + // + void strengthen_clause (Clause *, int); + void subsume_clause (Clause *subsuming, Clause *subsumed); + int subsume_check (Clause *subsuming, Clause *subsumed); + int try_to_subsume_clause (Clause *, vector<Clause *> &shrunken); + void reset_subsume_bits (); + bool subsume_round (); + void subsume (); + + // Covered clause elimination of large clauses. + // + void covered_literal_addition (int lit, Coveror &); + void asymmetric_literal_addition (int lit, Coveror &); + void cover_push_extension (int lit, Coveror &); + bool cover_propagate_asymmetric (int lit, Clause *ignore, Coveror &); + bool cover_propagate_covered (int lit, Coveror &); + bool cover_clause (Clause *c, Coveror &); + int64_t cover_round (); + bool cover (); + + // Strengthening through vivification in 'vivify.cpp'. + // + void demote_clause (Clause *); + void flush_vivification_schedule (std::vector<Clause *> &, int64_t &); + void vivify_increment_stats (const Vivifier &vivifier); + void vivify_subsume_clause (Clause *subsuming, Clause *subsumed); + void compute_tier_limits (Vivifier &); + void vivify_initialize (Vivifier &vivifier, int64_t &ticks); + inline void vivify_prioritize_leftovers (char, size_t prioritized, + std::vector<Clause *> &schedule); + bool consider_to_vivify_clause (Clause *candidate); + void vivify_sort_watched (Clause *c); + bool vivify_instantiate ( + const std::vector<int> &, Clause *, + std::vector<std::tuple<int, Clause *, bool>> &lrat_stack, + int64_t &ticks); + void vivify_analyze_redundant (Vivifier &, Clause *start, bool &); + void vivify_build_lrat (int, Clause *, + std::vector<std::tuple<int, Clause *, bool>> &); + void vivify_chain_for_units (int lit, Clause *reason); + void vivify_strengthen (Clause *candidate); + void vivify_assign (int lit, Clause *); + void vivify_assume (int lit); + bool vivify_propagate (int64_t &); + void vivify_deduce (Clause *candidate, Clause *conflct, int implied, + Clause **, bool &); + bool vivify_clause (Vivifier &, Clause *candidate); + void vivify_analyze (Clause *start, bool &, Clause **, + const Clause *const, int implied, bool &); + bool vivify_shrinkable (const std::vector<int> &sorted, Clause *c); + void vivify_round (Vivifier &, int64_t delta); + bool vivify (); + + // Compacting (shrinking internal variable tables) in 'compact.cpp' + // + bool compacting (); + void compact (); + + // Transitive reduction of binary implication graph in 'transred.cpp' + // + void transred (); + + // We monitor the maximum size and glue of clauses during 'reduce' and + // thus can predict if a redundant extended clause is likely to be kept in + // the next 'reduce' phase. These clauses are target of subsumption and + // vivification checks, in addition to irredundant clauses. Their + // variables are also marked as being 'added'. + // + bool likely_to_be_kept_clause (Clause *c) { + if (!c->redundant) + return true; + if (c->glue <= tier2[false]) + return true; + if (c->glue > lim.keptglue) + return false; + if (c->size > lim.keptsize) + return false; + return true; + } + + // We mark variables in added or shrunken clauses as 'subsume' candidates + // if the clause is likely to be kept in the next 'reduce' phase (see last + // function above). This gives a persistent (across consecutive + // interleaved search and inprocessing phases) set of variables which have + // to be reconsidered in subsumption checks, i.e., only clauses with + // 'subsume' marked variables are checked to be forward subsumed. + // A similar technique is used to reduce the effort in hyper ternary + // resolution to focus on variables in new ternary clauses. + // + void mark_subsume (int lit) { + Flags &f = flags (lit); + if (f.subsume) + return; + LOG ("marking %d as subsuming literal candidate", abs (lit)); + stats.mark.subsume++; + f.subsume = true; + } + void mark_ternary (int lit) { + Flags &f = flags (lit); + if (f.ternary) + return; + LOG ("marking %d as ternary resolution literal candidate", abs (lit)); + stats.mark.ternary++; + f.ternary = true; + } + void mark_factor (int lit) { + Flags &f = flags (lit); + const unsigned bit = bign (lit); + if (f.factor & bit) + return; + LOG ("marking %d as factor literal candidate", lit); + stats.mark.factor++; + f.factor |= bit; + } + void mark_added (int lit, int size, bool redundant); + void mark_added (Clause *); + + bool marked_subsume (int lit) const { return flags (lit).subsume; } + + // If irredundant clauses are removed or literals in clauses are removed, + // then variables in such clauses should be reconsidered to be eliminated + // through bounded variable elimination. In contrast to 'subsume' the + // 'elim' flag is restricted to 'irredundant' clauses only. For blocked + // clause elimination it is better to have a more precise signed version, + // which allows to independently mark positive and negative literals. + // + void mark_elim (int lit) { + Flags &f = flags (lit); + if (f.elim) + return; + LOG ("marking %d as elimination literal candidate", lit); + stats.mark.elim++; + f.elim = true; + } + void mark_block (int lit) { + Flags &f = flags (lit); + const unsigned bit = bign (lit); + if (f.block & bit) + return; + LOG ("marking %d as blocking literal candidate", lit); + stats.mark.block++; + f.block |= bit; + } + void mark_removed (int lit) { + mark_elim (lit); + mark_block (-lit); + } + void mark_removed (Clause *, int except = 0); + + // The following two functions are only used for testing & debugging. + + bool marked_block (int lit) const { + const Flags &f = flags (lit); + const unsigned bit = bign (lit); + return (f.block & bit) != 0; + } + void unmark_block (int lit) { + Flags &f = flags (lit); + const unsigned bit = bign (lit); + f.block &= ~bit; + } + + // During scheduling literals for blocked clause elimination we skip those + // literals which occur negated in a too large clause. + // + void mark_skip (int lit) { + Flags &f = flags (lit); + const unsigned bit = bign (lit); + if (f.skip & bit) + return; + LOG ("marking %d to be skipped as blocking literal", lit); + f.skip |= bit; + } + bool marked_skip (int lit) { + const Flags &f = flags (lit); + const unsigned bit = bign (lit); + return (f.skip & bit) != 0; + } + + // During decompose ignore literals where we already built LRAT chains + // + void mark_decomposed (int lit) { + Flags &f = flags (lit); + const unsigned bit = bign (lit); + CADICAL_assert ((f.marked_signed & bit) == 0); + sign_marked.push_back (lit); + f.marked_signed |= bit; + } + void unmark_decomposed (int lit) { + Flags &f = flags (lit); + const unsigned bit = bign (lit); + f.marked_signed &= ~bit; + } + bool marked_decomposed (int lit) { + const Flags &f = flags (lit); + const unsigned bit = bign (lit); + return (f.marked_signed & bit) != 0; + } + void clear_sign_marked_literals (); + + // Blocked Clause elimination in 'block.cpp'. + // + bool is_blocked_clause (Clause *c, int pivot); + void block_schedule (Blocker &); + size_t block_candidates (Blocker &, int lit); + Clause *block_impossible (Blocker &, int lit); + void block_literal_with_at_least_two_negative_occs (Blocker &, int lit); + void block_literal_with_one_negative_occ (Blocker &, int lit); + void block_pure_literal (Blocker &, int lit); + void block_reschedule_clause (Blocker &, int lit, Clause *); + void block_reschedule (Blocker &, int lit); + void block_literal (Blocker &, int lit); + bool block (); + + // Find gates in 'gates.cpp' for bounded variable substitution. + // + int second_literal_in_binary_clause_lrat (Clause *, int first); + int second_literal_in_binary_clause (Eliminator &, Clause *, int first); + void mark_binary_literals (Eliminator &, int pivot); + void find_and_gate (Eliminator &, int pivot); + void find_equivalence (Eliminator &, int pivot); + + bool get_ternary_clause (Clause *, int &, int &, int &); + bool match_ternary_clause (Clause *, int, int, int); + Clause *find_ternary_clause (int, int, int); + + bool get_clause (Clause *, vector<int> &); + bool is_clause (Clause *, const vector<int> &); + Clause *find_clause (const vector<int> &); + void find_xor_gate (Eliminator &, int pivot); + + void find_if_then_else (Eliminator &, int pivot); + + Clause *find_binary_clause (int, int); + void find_gate_clauses (Eliminator &, int pivot); + void unmark_gate_clauses (Eliminator &); + + // mine definitions for cadical_kitten in 'definition.cpp' + // + void find_definition (Eliminator &, int); + void init_citten (); + void reset_citten (); + void citten_clear_track_log_terminate (); + + // Bounded variable elimination in 'elim.cpp'. + // + bool ineliminating (); + double compute_elim_score (unsigned lit); + void mark_redundant_clauses_with_eliminated_variables_as_garbage (); + void unmark_binary_literals (Eliminator &); + bool resolve_clauses (Eliminator &, Clause *, int pivot, Clause *, bool); + void mark_eliminated_clauses_as_garbage (Eliminator &, int pivot, bool &); + bool elim_resolvents_are_bounded (Eliminator &, int pivot); + void elim_update_removed_lit (Eliminator &, int lit); + void elim_update_removed_clause (Eliminator &, Clause *, int except = 0); + void elim_update_added_clause (Eliminator &, Clause *); + void elim_add_resolvents (Eliminator &, int pivot); + void elim_backward_clause (Eliminator &, Clause *); + void elim_backward_clauses (Eliminator &); + void elim_propagate (Eliminator &, int unit); + void elim_on_the_fly_self_subsumption (Eliminator &, Clause *, int); + void try_to_eliminate_variable (Eliminator &, int pivot, bool &); + void increase_elimination_bound (); + int elim_round (bool &completed, bool &); + void elim (bool update_limits = true); + + int64_t flush_elimfast_occs (int lit); + void elimfast_add_resolvents (Eliminator &, int pivot); + bool elimfast_resolvents_are_bounded (Eliminator &, int pivot); + void try_to_fasteliminate_variable (Eliminator &, int pivot, bool &); + int elimfast_round (bool &completed, bool &); + void elimfast (); + + // sweeping in 'sweep.cpp' + int sweep_solve (); + void sweep_set_cadical_kitten_ticks_limit (Sweeper &sweeper); + bool cadical_kitten_ticks_limit_hit (Sweeper &sweeper, const char *when); + void init_sweeper (Sweeper &sweeper); + void release_sweeper (Sweeper &sweeper); + void clear_sweeper (Sweeper &sweeper); + int sweep_repr (Sweeper &sweeper, int lit); + void add_literal_to_environment (Sweeper &sweeper, unsigned depth, int); + void sweep_clause (Sweeper &sweeper, unsigned depth, Clause *); + void sweep_add_clause (Sweeper &sweeper, unsigned depth); + void add_core (Sweeper &sweeper, unsigned core_idx); + void save_core (Sweeper &sweeper, unsigned core); + void clear_core (Sweeper &sweeper, unsigned core_idx); + void save_add_clear_core (Sweeper &sweeper); + void init_backbone_and_partition (Sweeper &sweeper); + void sweep_empty_clause (Sweeper &sweeper); + void sweep_refine_partition (Sweeper &sweeper); + void sweep_refine_backbone (Sweeper &sweeper); + void sweep_refine (Sweeper &sweeper); + void flip_backbone_literals (struct Sweeper &sweeper); + bool sweep_backbone_candidate (Sweeper &sweeper, int lit); + int64_t add_sweep_binary (sweep_proof_clause, int lit, int other); + bool scheduled_variable (Sweeper &sweeper, int idx); + void schedule_inner (Sweeper &sweeper, int idx); + void schedule_outer (Sweeper &sweeper, int idx); + int next_scheduled (Sweeper &sweeper); + void substitute_connected_clauses (Sweeper &sweeper, int lit, int other, + int64_t id); + void sweep_remove (Sweeper &sweeper, int lit); + void flip_partition_literals (struct Sweeper &sweeper); + const char *sweep_variable (Sweeper &sweeper, int idx); + bool scheduable_variable (Sweeper &sweeper, int idx, size_t *occ_ptr); + unsigned schedule_all_other_not_scheduled_yet (Sweeper &sweeper); + bool sweep_equivalence_candidates (Sweeper &sweeper, int lit, int other); + unsigned reschedule_previously_remaining (Sweeper &sweeper); + unsigned incomplete_variables (); + void mark_incomplete (Sweeper &sweeper); + unsigned schedule_sweeping (Sweeper &sweeper); + void unschedule_sweeping (Sweeper &sweeper, unsigned swept, + unsigned scheduled); + bool sweep (); + void sweep_dense_propagate (Sweeper &sweeper); + void sweep_sparse_mode (); + void sweep_dense_mode_and_watch_irredundant (); + void sweep_substitute_lrat (Clause *c, int64_t id); + void sweep_substitute_new_equivalences (Sweeper &sweeper); + void sweep_update_noccs (Clause *c); + void delete_sweep_binary (const sweep_binary &sb); + bool can_sweep_clause (Clause *c); + bool sweep_flip (int); + int sweep_flip_and_implicant (int); + bool sweep_extract_fixed (Sweeper &sweeper, int lit); + + // factor + void factor_mode (); + void reset_factor_mode (); + double tied_next_factor_score (int); + Quotient *new_quotient (Factoring &, int); + void release_quotients (Factoring &); + size_t first_factor (Factoring &, int); + void clear_nounted (vector<int> &); + void clear_flauses (vector<Clause *> &); + Quotient *best_quotient (Factoring &, size_t *); + int next_factor (Factoring &, unsigned *); + void factorize_next (Factoring &, int, unsigned); + void resize_factoring (Factoring &factoring, int lit); + void flush_unmatched_clauses (Quotient *); + void add_self_subsuming_factor (Quotient *, Quotient *); + bool self_subsuming_factor (Quotient *); + void add_factored_divider (Quotient *, int); + void blocked_clause (Quotient *q, int); + void add_factored_quotient (Quotient *, int not_fresh); + void eagerly_remove_from_occurences (Clause *c); + void delete_unfactored (Quotient *q); + void update_factored (Factoring &factoring, Quotient *q); + bool apply_factoring (Factoring &factoring, Quotient *q); + void update_factor_candidate (Factoring &, int); + void schedule_factorization (Factoring &); + bool run_factorization (int64_t limit); + bool factor (); + int get_new_extension_variable (); + Clause *new_factor_clause (); + + // instantiate + // + void inst_assign (int lit); + bool inst_propagate (); + void collect_instantiation_candidates (Instantiator &); + bool instantiate_candidate (int lit, Clause *); + void instantiate (Instantiator &); + + void new_trail_level (int lit); + + // Hyper ternary resolution. + // + bool ternary_find_binary_clause (int, int); + bool ternary_find_ternary_clause (int, int, int); + Clause *new_hyper_ternary_resolved_clause (bool red); + Clause *new_hyper_ternary_resolved_clause_and_watch (bool red, bool); + bool hyper_ternary_resolve (Clause *, int, Clause *); + void ternary_lit (int pivot, int64_t &steps, int64_t &htrs); + void ternary_idx (int idx, int64_t &steps, int64_t &htrs); + bool ternary_round (int64_t &steps, int64_t &htrs); + bool ternary (); + + // Probing in 'probe.cpp'. + // + bool inprobing (); + void failed_literal (int lit); + void probe_lrat_for_units (int lit); + void probe_assign_unit (int lit); + void probe_assign_decision (int lit); + void probe_assign (int lit, int parent); + void mark_duplicated_binary_clauses_as_garbage (); + int get_parent_reason_literal (int lit); + void set_parent_reason_literal (int lit, int reason); + void clean_probehbr_lrat (); + void init_probehbr_lrat (); + void get_probehbr_lrat (int lit, int uip); + void set_probehbr_lrat (int lit, int uip); + void probe_post_dominator_lrat (vector<Clause *> &, int, int); + void probe_dominator_lrat (int dom, Clause *reason); + int probe_dominator (int a, int b); + int hyper_binary_resolve (Clause *); + void probe_propagate2 (); + bool probe_propagate (); + bool is_binary_clause (Clause *c, int &, int &); + void generate_probes (); + void flush_probes (); + int next_probe (); + bool probe (); + void inprobe (bool update_limits = true); + + // ProbSAT/WalkSAT implementation called initially or from 'rephase'. + // + void walk_save_minimum (Walker &); + Clause *walk_pick_clause (Walker &); + unsigned walk_break_value (int lit); + int walk_pick_lit (Walker &, Clause *); + void walk_flip_lit (Walker &, int lit); + int walk_round (int64_t limit, bool prev); + void walk (); + + // Detect strongly connected components in the binary implication graph + // (BIG) and equivalent literal substitution (ELS) in 'decompose.cpp'. + // + void decompose_conflicting_scc_lrat (DFS *dfs, vector<int> &); + void build_lrat_for_clause (const vector<vector<Clause *>> &dfs_chains, + bool invert = false); + vector<Clause *> decompose_analyze_binary_clauses (DFS *dfs, int from); + void decompose_analyze_binary_chain (DFS *dfs, int); + bool decompose_round (); + void decompose (); + + void reset_limits (); // Reset after 'solve' call. + + // Try flipping a literal while not falsifying a model. + + bool flip (int lit); + bool flippable (int lit); + + // Assumption handling. + // + void assume_analyze_literal (int lit); + void assume_analyze_reason (int lit, Clause *reason); + void assume (int); // New assumption literal. + bool failed (int lit); // Literal failed assumption? + void reset_assumptions (); // Reset after 'solve' call. + void sort_and_reuse_assumptions (); // reorder the assumptions in order to + // reuse parts of the trail + void failing (); // Prepare failed assumptions. + + bool assumed (int lit) { // Marked as assumption. + Flags &f = flags (lit); + const unsigned bit = bign (lit); + return (f.assumed & bit) != 0; + } + + // Add temporary clause as constraint. + // + void constrain (int); // Add literal to constraint. + bool + failed_constraint (); // Was constraint used to proof unsatisfiablity? + void reset_constraint (); // Reset after 'solve' call. + + // Propagate the current set of assumptions and return the + // non-witness assigned literals + int propagate_assumptions (); + void implied (std::vector<int> &entrailed); + + // Forcing decision variables to a certain phase. + // + void phase (int lit); + void unphase (int lit); + + // Globally blocked clause elimination. + // + bool is_autarky_literal (int lit) const; + bool is_conditional_literal (int lit) const; + void mark_as_conditional_literal (int lit); + void unmark_as_conditional_literal (int lit); + // + bool is_in_candidate_clause (int lit) const; + void mark_in_candidate_clause (int lit); + void unmark_in_candidate_clause (int lit); + // + void condition_assign (int lit); + void condition_unassign (int lit); + // + bool conditioning (); + long condition_round (long unassigned_literal_propagation_limit); + void condition (bool update_limits = true); + + // Part on picking the next decision in 'decide.cpp'. + // + bool satisfied (); + int next_decision_variable_on_queue (); + int next_decision_variable_with_best_score (); + int next_decision_variable (); + int decide_phase (int idx, bool target); + int likely_phase (int idx); + bool better_decision (int lit, int other); + int decide (); // 0=decision, 20=failed + + // Internal functions to enable explicit search limits. + // + void limit_terminate (int); + void limit_decisions (int); // Force decision limit. + void limit_conflicts (int); // Force conflict limit. + void limit_preprocessing (int); // Enable 'n' preprocessing rounds. + void limit_local_search (int); // Enable 'n' local search rounds. + + // External versions can access limits by 'name'. + // + static bool is_valid_limit (const char *name); + bool limit (const char *name, int); // 'true' if 'name' valid + + // Set all the CDCL search limits and increments for scheduling + // inprocessing, restarts, clause database reductions, etc. + // + void init_report_limits (); + void init_preprocessing_limits (); + void init_search_limits (); + + // The computed averages are local to the 'stable' and 'unstable' phase. + // Their main use is to be reported in 'report', except for the 'glue' + // averages, which are used to schedule (prohibit actually) restarts + // during 'unstable' phases ('stable' phases use reluctant doubling). + // + void init_averages (); + void swap_averages (); + + int try_to_satisfy_formula_by_saved_phases (); + void produce_failed_assumptions (); + + // Main solve & search functions in 'internal.cpp'. + // + // We have three pre-solving techniques. These consist of preprocessing, + // local search and searching for lucky phases, which in full solving + // mode except for the last are usually optional and then followed by + // the main CDCL search loop with inprocessing. If only preprocessing + // is requested from 'External::simplify' only preprocessing is called + // though. This is all orchestrated by the 'solve' function. + // + int already_solved (); + int restore_clauses (); + bool preprocess_round (int round); + void preprocess_quickly (); + int preprocess (); + int local_search_round (int round); + int local_search (); + int lucky_phases (); + int cdcl_loop_with_inprocessing (); + void reset_solving (); + int solve (bool preprocess_only = false); + void finalize (int); + + // + int lookahead (); + CubesWithStatus generate_cubes (int, int); + int most_occurring_literal (); + int lookahead_probing (); + int lookahead_next_probe (); + void lookahead_flush_probes (); + void lookahead_generate_probes (); + std::vector<int> lookahead_populate_locc (); + int lookahead_locc (const std::vector<int> &); + + bool terminating_asked (); + +#ifndef CADICAL_QUIET + // Built in profiling in 'profile.cpp' (see also 'profile.hpp'). + // + void start_profiling (Profile &p, double); + void stop_profiling (Profile &p, double); + + double update_profiles (); // Returns 'time ()'. + void print_profile (); +#endif + + // Get the value of an internal literal: -1=false, 0=unassigned, 1=true. + // We use a redundant table for both negative and positive literals. This + // allows a branch-less check for the value of literal and is considered + // substantially faster than negating the result if the argument is + // negative. We also avoid taking the absolute value. + // + signed char val (int lit) const { + CADICAL_assert (-max_var <= lit); + CADICAL_assert (lit); + CADICAL_assert (lit <= max_var); + return vals[lit]; + } + + // As suggested by Matt Ginsberg it might be useful to factor-out a common + // setter function for setting and resetting the value of a literal. + // + void set_val (int lit, signed char val) { + CADICAL_assert (-1 <= val); + CADICAL_assert (val <= 1); + CADICAL_assert (-max_var <= lit); + CADICAL_assert (lit); + CADICAL_assert (lit <= max_var); + vals[lit] = val; + vals[-lit] = -val; + } + + // As 'val' but restricted to the root-level value of a literal. + // It is not that time critical and also needs to check the decision level + // of the variable anyhow. + // + int fixed (int lit) { + CADICAL_assert (-max_var <= lit); + CADICAL_assert (lit); + CADICAL_assert (lit <= max_var); + const int idx = vidx (lit); + int res = vals[idx]; + if (res && vtab[idx].level) + res = 0; + if (lit < 0) + res = -res; + return res; + } + + // Map back an internal literal to an external. + // + int externalize (int lit) { + CADICAL_assert (lit != INT_MIN); + const int idx = abs (lit); + CADICAL_assert (idx); + CADICAL_assert (idx <= max_var); + int res = i2e[idx]; + if (lit < 0) + res = -res; + return res; + } + + // Explicit freezing and melting of variables. + // + void freeze (int lit) { + int idx = vidx (lit); + if ((size_t) idx >= frozentab.size ()) { + size_t new_vsize = vsize ? 2 * vsize : 1 + (size_t) max_var; + while (new_vsize <= (size_t) max_var) + new_vsize *= 2; + frozentab.resize (new_vsize); + } + unsigned &ref = frozentab[idx]; + if (ref < UINT_MAX) { + ref++; + LOG ("variable %d frozen %u times", idx, ref); + } else + LOG ("variable %d remains frozen forever", idx); + } + void melt (int lit) { + int idx = vidx (lit); + unsigned &ref = frozentab[idx]; + if (ref < UINT_MAX) { + if (!--ref) { + if (relevanttab[idx]) { + LOG ("variable %d is observed, can not be completely molten", + idx); + ref++; + } else + LOG ("variable %d completely molten", idx); + } else + LOG ("variable %d melted once but remains frozen %u times", lit, + ref); + } else + LOG ("variable %d remains frozen forever", idx); + } + bool frozen (int lit) { + return (size_t) vidx (lit) < frozentab.size () && + frozentab[vidx (lit)] > 0; + } + + // Congruence closure + bool extract_gates (); + + // Parsing functions in 'parse.cpp'. + // + const char *parse_dimacs (FILE *); + const char *parse_dimacs (const char *); + const char *parse_solution (const char *); + + // Enable and disable proof logging and checking. + // + void new_proof_on_demand (); + void force_lrat (); // sets lrat=true + void resize_unit_clauses_idx (); // resizes unit_clauses_idx + void close_trace (bool stats = false); // Stop proof tracing. + void flush_trace (bool stats = false); // Flush proof trace file. + void trace (File *); // Start write proof file. + void check (); // Enable online proof checking. + + void connect_proof_tracer (Tracer *tracer, bool antecedents, + bool finalize_clauses = false); + void connect_proof_tracer (InternalTracer *tracer, bool antecedents, + bool finalize_clauses = false); + void connect_proof_tracer (StatTracer *tracer, bool antecedents, + bool finalize_clauses = false); + void connect_proof_tracer (FileTracer *tracer, bool antecedents, + bool finalize_clauses = false); + bool disconnect_proof_tracer (Tracer *tracer); + bool disconnect_proof_tracer (StatTracer *tracer); + bool disconnect_proof_tracer (FileTracer *tracer); + void conclude_unsat (); + void reset_concluded (); + + // Dump to '<stdout>' as DIMACS for debugging. + // + void dump (Clause *); + void dump (); + + // Export and traverse all irredundant (non-unit) clauses. + // + bool traverse_clauses (ClauseIterator &); + + // Export and traverse all irredundant (non-unit) clauses. + // + bool traverse_constraint (ClauseIterator &); + + /*----------------------------------------------------------------------*/ + + double solve_time (); // accumulated time spent in 'solve ()' + + double process_time () const; // since solver was initialized + double real_time () const; // since solver was initialized + + double time () { return opts.realtime ? real_time () : process_time (); } + + // Regularly reports what is going on in 'report.cpp'. + // + void report (char type, int verbose_level = 0); + void report_solving (int); + + void print_statistics (); + void print_resource_usage (); + + /*----------------------------------------------------------------------*/ + +#ifndef CADICAL_QUIET + + void print_prefix (); + + // Non-verbose messages and warnings, i.e., always printed unless 'quiet' + // is set, which disables messages at run-time, or even 'CADICAL_QUIET' is defined + // through the configuration option './configure --quiet', which disables + // such messages completely at compile-time. + // + void vmessage (const char *, va_list &); + void message (const char *, ...) CADICAL_ATTRIBUTE_FORMAT (2, 3); + void message (); // empty line + + // Verbose messages with explicit verbose 'level' controlled by + // 'opts.verbose' (verbose level '0' gives the same as 'message'). + // + void vverbose (int level, const char *fmt, va_list &); + void verbose (int level, const char *fmt, ...) + CADICAL_ATTRIBUTE_FORMAT (3, 4); + void verbose (int level); + + // This is for printing section headers in the form + // + // c ---- [ <title> ] --------------------- + // + // nicely aligned (and of course is ignored if 'quiet' is set). + // + void section (const char *title); + + // Print verbose message about phases if 'opts.verbose > 1' (but not if + // 'quiet' is set). Note that setting 'log' or '-l' forces all verbose + // output (and also ignores 'quiet' set to true'). The 'phase' argument + // is used to print a 'phase' prefix for the message as follows: + // + // c [<phase>] ... + // + void phase (const char *phase, const char *, ...) + CADICAL_ATTRIBUTE_FORMAT (3, 4); + + // Same as the last 'phase' above except that the prefix gets a count: + // + // c [<phase>-<count>] ... + // + void phase (const char *phase, int64_t count, const char *, ...) + CADICAL_ATTRIBUTE_FORMAT (4, 5); +#endif + + // Print error messages which are really always printed (even if 'quiet' + // is set). This leads to exit the current process with exit status '1'. + // + // TODO add possibility to use a call back instead of calling exit. + // + void error_message_end (); + void verror (const char *, va_list &); + void error (const char *, ...) CADICAL_ATTRIBUTE_FORMAT (2, 3); + void error_message_start (); + + // Warning messages. + // + void warning (const char *, ...) CADICAL_ATTRIBUTE_FORMAT (2, 3); +}; + +// Fatal internal error which leads to abort. +// +void fatal_message_start (); +void fatal_message_end (); +void fatal (const char *, ...) CADICAL_ATTRIBUTE_FORMAT (1, 2); + +/*------------------------------------------------------------------------*/ + +// Has to be put here, i.e., not into 'score.hpp', since we need the +// definition of 'Internal::score' above (after '#include "score.hpp"'). + +inline bool score_smaller::operator() (unsigned a, unsigned b) { + + // Avoid computing twice 'abs' in 'score ()'. + // + CADICAL_assert (1 <= a); + CADICAL_assert (a <= (unsigned) internal->max_var); + CADICAL_assert (1 <= b); + CADICAL_assert (b <= (unsigned) internal->max_var); + double s = internal->stab[a]; + double t = internal->stab[b]; + + if (s < t) + return true; + if (s > t) + return false; + + return a > b; +} + +/*------------------------------------------------------------------------*/ + +// Implemented here for keeping it all inline (requires Internal::fixed). + +inline int External::fixed (int elit) const { + CADICAL_assert (elit); + CADICAL_assert (elit != INT_MIN); + int eidx = abs (elit); + if (eidx > max_var) + return 0; + int ilit = e2i[eidx]; + if (!ilit) + return 0; + if (elit < 0) + ilit = -ilit; + return internal->fixed (ilit); +} + +/*------------------------------------------------------------------------*/ + +// We want to have termination checks inlined, particularly the first +// function which appears in preprocessor loops. Even though this first +// 'termination_forced' is set asynchronously, this should not lead to a +// data race issue (it also has been declared 'volatile'). + +inline bool Internal::terminated_asynchronously (int factor) { + // First way of asynchronous termination is through 'terminate' which sets + // the 'termination_forced' flag directly. The second way is through a + // call back to a 'terminator' if it is non-zero, which however is costly. + // + if (termination_forced) { + LOG ("termination asynchronously forced"); + return true; + } + + // This is only for testing and debugging asynchronous termination calls. + // In production code this could be removed but then should not be costly + // and keeping it will allow to test correctness of asynchronous + // termination on the production platform too. After this triggers we + // have to set the 'termination_forced' flag, such that subsequent calls + // to this function do not check this again. + // + if (lim.terminate.forced) { + CADICAL_assert (lim.terminate.forced > 0); + if (lim.terminate.forced-- == 1) { + LOG ("internally forcing termination"); + termination_forced = true; + return true; + } + LOG ("decremented internal forced termination limit to %d", + lim.terminate.forced); + } + + // The second way of asynchronous termination is through registering and + // calling an external 'Terminator' object. This is of course more costly + // than just checking a (volatile though) boolean flag, particularly in + // tight loops. To avoid this cost we only call the terminator in + // intervals of 'opts.terminateint', which in addition can be scaled up by + // the argument 'factor'. If the terminator returns 'true' we set the + // 'termination_forced' flag to 'true' in order to remember the + // termination status and to avoid the terminator again. Setting this + // flag leads to the first test above to succeed in subsequent calls. + // + if (external->terminator && !lim.terminate.check--) { + CADICAL_assert (factor > 0); + CADICAL_assert (INT_MAX / factor > opts.terminateint); + lim.terminate.check = factor * opts.terminateint; + if (external->terminator->terminate ()) { + termination_forced = true; // Cache it. + LOG ("connected terminator forces termination"); + return true; + } + } + + return false; +} + +/*------------------------------------------------------------------------*/ + +inline bool Internal::search_limits_hit () { + CADICAL_assert (!preprocessing); + CADICAL_assert (!localsearching); + + if (lim.conflicts >= 0 && stats.conflicts >= lim.conflicts) { + LOG ("conflict limit %" PRId64 " reached", lim.conflicts); + return true; + } + + if (lim.decisions >= 0 && stats.decisions >= lim.decisions) { + LOG ("decision limit %" PRId64 " reached", lim.decisions); + return true; + } + + return false; +} + +/*------------------------------------------------------------------------*/ + +} // namespace CaDiCaL + +ABC_NAMESPACE_CXX_HEADER_END + +#endif diff --git a/src/sat/cadical/inttypes.hpp b/src/sat/cadical/inttypes.hpp new file mode 100644 index 0000000000..2f7cb74a4d --- /dev/null +++ b/src/sat/cadical/inttypes.hpp @@ -0,0 +1,30 @@ +#ifndef _inttypes_h_INCLUDED +#define _inttypes_h_INCLUDED + +#include "global.h" + +// This is an essence a wrapper around '<cinttypes>' respectively +// 'inttypes.h' in order to please the 'MinGW' cross-compiler (we are using +// 'i686-w64-mingw32-gcc') to produce correct 'printf' style formatting for +// 64-bit numbers as this does not work out-of-the-box (which is also very +// annoying). This also produces lots of warnings (through '-Wformat' and +// the corresponding 'attribute' declaration for 'printf' style functions). +// Again 'MinGW' is not fully standard compliant here and we have to cover +// up for that manually. + +// We repeat the code on making this work which is also contained in +// 'cadical.hpp' as we do not want to require users of the library to +// include another header file (like this one) beside 'cadical.hpp'. + +#ifndef PRINTF_FORMAT +#ifdef __MINGW32__ +#define __USE_MINGW_ANSI_STDIO 1 +#define PRINTF_FORMAT __MINGW_PRINTF_FORMAT +#else +#define PRINTF_FORMAT printf +#endif +#endif + +#include <cinttypes> + +#endif diff --git a/src/sat/cadical/ipasir.h b/src/sat/cadical/ipasir.h new file mode 100644 index 0000000000..ccb6101308 --- /dev/null +++ b/src/sat/cadical/ipasir.h @@ -0,0 +1,35 @@ +#ifndef _ipasir_h_INCLUDED +#define _ipasir_h_INCLUDED + +#include "global.h" + +/*------------------------------------------------------------------------*/ +ABC_NAMESPACE_HEADER_START +/*------------------------------------------------------------------------*/ + +// Here are the declarations for the actual IPASIR functions, which is the +// generic incremental reentrant SAT solver API used for instance in the SAT +// competition. The other 'C' API in 'ccadical.h' is (more) type safe and +// has additional functions only supported by the CaDiCaL library. Please +// also refer to our SAT Race 2015 article in the Journal of AI from 2016. + +const char *ipasir_signature (void); +void *ipasir_init (void); +void ipasir_release (void *solver); +void ipasir_add (void *solver, int lit); +void ipasir_assume (void *solver, int lit); +int ipasir_solve (void *solver); +int ipasir_val (void *solver, int lit); +int ipasir_failed (void *solver, int lit); + +void ipasir_set_terminate (void *solver, void *state, + int (*terminate) (void *state)); + +void ipasir_set_learn (void *solver, void *state, int max_length, + void (*learn) (void *state, int *clause)); + +/*------------------------------------------------------------------------*/ +ABC_NAMESPACE_HEADER_END +/*------------------------------------------------------------------------*/ + +#endif diff --git a/src/sat/cadical/kitten.h b/src/sat/cadical/kitten.h new file mode 100644 index 0000000000..c70d47b0d5 --- /dev/null +++ b/src/sat/cadical/kitten.h @@ -0,0 +1,95 @@ +#ifndef _cadical_kitten_h_INCLUDED +#define _cadical_kitten_h_INCLUDED + +#include "global.h" + +#include <stdbool.h> +#include <stdint.h> +#include <stdio.h> +#include <stdlib.h> + +ABC_NAMESPACE_HEADER_START + +typedef struct cadical_kitten cadical_kitten; + +cadical_kitten *cadical_kitten_init (void); +void cadical_kitten_clear (cadical_kitten *); +void cadical_kitten_release (cadical_kitten *); + +#ifdef LOGGING +void cadical_kitten_set_logging (cadical_kitten *cadical_kitten); +#endif + +void cadical_kitten_track_antecedents (cadical_kitten *); + +void cadical_kitten_shuffle_clauses (cadical_kitten *); +void cadical_kitten_flip_phases (cadical_kitten *); +void cadical_kitten_randomize_phases (cadical_kitten *); + +void cadical_kitten_assume (cadical_kitten *, unsigned lit); +void cadical_kitten_assume_signed (cadical_kitten *, int lit); + +void cadical_kitten_clause (cadical_kitten *, size_t size, unsigned *); +void citten_clause_with_id (cadical_kitten *, unsigned id, size_t size, int *); +void cadical_kitten_unit (cadical_kitten *, unsigned); +void cadical_kitten_binary (cadical_kitten *, unsigned, unsigned); + +void cadical_kitten_clause_with_id_and_exception (cadical_kitten *, unsigned id, + size_t size, const unsigned *, + unsigned except); + +void citten_clause_with_id_and_exception (cadical_kitten *, unsigned id, + size_t size, const int *, + unsigned except); +void citten_clause_with_id_and_equivalence (cadical_kitten *, unsigned id, + size_t size, const int *, + unsigned, unsigned); +void cadical_kitten_no_ticks_limit (cadical_kitten *); +void cadical_kitten_set_ticks_limit (cadical_kitten *, uint64_t); +uint64_t cadical_kitten_current_ticks (cadical_kitten *); + +void cadical_kitten_no_terminator (cadical_kitten *); +void cadical_kitten_set_terminator (cadical_kitten *, void *, int (*) (void *)); + +int cadical_kitten_solve (cadical_kitten *); +int cadical_kitten_status (cadical_kitten *); + +signed char cadical_kitten_value (cadical_kitten *, unsigned); +signed char cadical_kitten_signed_value (cadical_kitten *, int); // converts second argument +signed char cadical_kitten_fixed (cadical_kitten *, unsigned); +signed char cadical_kitten_fixed_signed (cadical_kitten *, int); // converts +bool cadical_kitten_failed (cadical_kitten *, unsigned); +bool cadical_kitten_flip_literal (cadical_kitten *, unsigned); +bool cadical_kitten_flip_signed_literal (cadical_kitten *, int); + +unsigned cadical_kitten_compute_clausal_core (cadical_kitten *, uint64_t *learned); +void cadical_kitten_shrink_to_clausal_core (cadical_kitten *); + +void cadical_kitten_traverse_core_ids (cadical_kitten *, void *state, + void (*traverse) (void *state, unsigned id)); + +void cadical_kitten_traverse_core_clauses (cadical_kitten *, void *state, + void (*traverse) (void *state, + bool learned, size_t, + const unsigned *)); +void cadical_kitten_traverse_core_clauses_with_id ( + cadical_kitten *, void *state, + void (*traverse) (void *state, unsigned, bool learned, size_t, + const unsigned *)); +void cadical_kitten_trace_core (cadical_kitten *, void *state, + void (*trace) (void *, unsigned, unsigned, bool, + size_t, const unsigned *, size_t, + const unsigned *)); + +int cadical_kitten_compute_prime_implicant (cadical_kitten *cadical_kitten, void *state, + bool (*ignore) (void *, unsigned)); + +void cadical_kitten_add_prime_implicant (cadical_kitten *cadical_kitten, void *state, int side, + void (*add_implicant) (void *, int, size_t, + const unsigned *)); + +int cadical_kitten_flip_and_implicant_for_signed_literal (cadical_kitten *cadical_kitten, int elit); + +ABC_NAMESPACE_HEADER_END + +#endif diff --git a/src/sat/cadical/level.hpp b/src/sat/cadical/level.hpp new file mode 100644 index 0000000000..bbfe774ff0 --- /dev/null +++ b/src/sat/cadical/level.hpp @@ -0,0 +1,39 @@ +#ifndef _level_hpp_INCLUDED +#define _level_hpp_INCLUDED + +#include "global.h" + +#include <climits> + +ABC_NAMESPACE_CXX_HEADER_START + +namespace CaDiCaL { + +// For each new decision we increase the decision level and push a 'Level' +// on the 'control' stack. The information gathered here is used in +// 'reuse_trail' and for early aborts in clause minimization. + +struct Level { + + int decision; // decision literal of this level + int trail; // trail start of this level + + struct { + int count; // how many variables seen during 'analyze' + int trail; // smallest trail position seen on this level + } seen; + + void reset () { + seen.count = 0; + seen.trail = INT_MAX; + } + + Level (int d, int t) : decision (d), trail (t) { reset (); } + Level () {} +}; + +} // namespace CaDiCaL + +ABC_NAMESPACE_CXX_HEADER_END + +#endif diff --git a/src/sat/cadical/lidruptracer.hpp b/src/sat/cadical/lidruptracer.hpp new file mode 100644 index 0000000000..02cd33fcd2 --- /dev/null +++ b/src/sat/cadical/lidruptracer.hpp @@ -0,0 +1,123 @@ +#ifndef _lidruptracer_h_INCLUDED +#define _lidruptracer_h_INCLUDED + +#include "global.h" + +ABC_NAMESPACE_CXX_HEADER_START + +class FileTracer; + +namespace CaDiCaL { + +struct LidrupClause { + LidrupClause *next; // collision chain link for hash table + uint64_t hash; // previously computed full 64-bit hash + int64_t id; // id of clause + std::vector<int64_t> chain; + std::vector<int> literals; +}; + +class LidrupTracer : public FileTracer { + + Internal *internal; + File *file; + bool binary; + bool piping; // The 'file' is a pipe and needs eagerly flushing. + + // hash table for conclusion + // + uint64_t num_clauses; // number of clauses in hash table + uint64_t size_clauses; // size of clause hash table + LidrupClause **clauses; // hash table of clauses + vector<int> imported_clause; + vector<int> assumptions; + vector<int64_t> imported_chain; + vector<int64_t> batch_weaken; + vector<int64_t> batch_delete; + vector<int64_t> batch_restore; + + static const unsigned num_nonces = 4; + + uint64_t nonces[num_nonces]; // random numbers for hashing + uint64_t last_hash; // last computed hash value of clause + int64_t last_id; // id of the last added clause + LidrupClause *last_clause; + uint64_t compute_hash (int64_t); // compute and save hash value of clause + + LidrupClause *new_clause (); + void delete_clause (LidrupClause *); + + static uint64_t reduce_hash (uint64_t hash, uint64_t size); + + void enlarge_clauses (); // enlarge hash table for clauses + void insert (); // insert clause in hash table + bool + find_and_delete (const int64_t); // find clause position in hash table + +#ifndef CADICAL_QUIET + int64_t added, deleted, weakened, restore, original, solved, batched; +#endif + + void flush_if_piping (); + + void put_binary_zero (); + void put_binary_lit (int external_lit); + void put_binary_id (int64_t id, bool = true); + + void lidrup_add_derived_clause (int64_t id, const vector<int> &clause, + const vector<int64_t> &chain); + void lidrup_delete_clause (int64_t id); //, const vector<int> &clause); + void + lidrup_add_restored_clause (int64_t id); //, const vector<int> &clause); + void lidrup_add_original_clause (int64_t id, const vector<int> &clause); + void lidrup_conclude_and_delete (const vector<int64_t> &conclusion); + void lidrup_report_status (int status); + void lidrup_conclude_sat (const vector<int> &model); + void lidrup_conclude_unknown (const vector<int> &trail); + void lidrup_solve_query (); + void lidrup_batch_weaken_restore_and_delete (); + +public: + LidrupTracer (Internal *, File *file, bool); + ~LidrupTracer (); + + // proof section: + void add_derived_clause (int64_t, bool, const vector<int> &, + const vector<int64_t> &) override; + void add_assumption_clause (int64_t, const vector<int> &, + const vector<int64_t> &) override; + void weaken_minus (int64_t, const vector<int> &) override; + void delete_clause (int64_t, bool, const vector<int> &) override; + void add_original_clause (int64_t, bool, const vector<int> &, + bool = false) override; + void report_status (int, int64_t) override; + void conclude_sat (const vector<int> &) override; + void conclude_unsat (ConclusionType, const vector<int64_t> &) override; + void conclude_unknown (const vector<int> &) override; + + void solve_query () override; + void add_assumption (int) override; + void reset_assumptions () override; + + // skip + void begin_proof (int64_t) override {} + void finalize_clause (int64_t, const vector<int> &) override {} + void strengthen (int64_t) override {} + void add_constraint (const vector<int> &) override {} + + // logging and file io + void connect_internal (Internal *i) override; + +#ifndef CADICAL_QUIET + void print_statistics (); +#endif + bool closed () override; + void close (bool) override; + void flush (bool) override; +}; + +} // namespace CaDiCaL + +ABC_NAMESPACE_CXX_HEADER_END + +#endif diff --git a/src/sat/cadical/limit.hpp b/src/sat/cadical/limit.hpp new file mode 100644 index 0000000000..0f6923214f --- /dev/null +++ b/src/sat/cadical/limit.hpp @@ -0,0 +1,162 @@ +#ifndef _limit_hpp_INCLUDED +#define _limit_hpp_INCLUDED + +#include "global.h" + +#include <cstdint> +#include <limits> + +ABC_NAMESPACE_CXX_HEADER_START + +namespace CaDiCaL { + +struct Internal; + +struct Limit { + + bool initialized; + + int64_t conflicts; // conflict limit if non-negative + int64_t decisions; // decision limit if non-negative + int64_t preprocessing; // limit on preprocessing rounds + int64_t localsearch; // limit on local search rounds + + int64_t compact; // conflict limit for next 'compact' + int64_t condition; // conflict limit for next 'condition' + int64_t elim; // conflict limit for next 'elim' + int64_t flush; // conflict limit for next 'flush' + int64_t inprobe; // conflict limit for next 'inprobe' + int64_t reduce; // conflict limit for next 'reduce' + int64_t rephase; // conflict limit for next 'rephase' + int64_t report; // report limit for header + int64_t restart; // conflict limit for next 'restart' + int64_t stabilize; // conflict/ticks limit for next 'stabilize' + + int keptsize; // maximum kept size in 'reduce' + int keptglue; // maximum kept glue in 'reduce' + int64_t recompute_tier; // conflict limit for next tier recomputation + + // How often rephased during (1) or out (0) of stabilization. + // + int64_t rephased[2]; + + // Current elimination bound per eliminated variable. + // + int64_t elimbound; + + struct { + int check; // countdown to next terminator call + int forced; // forced termination for testing + } terminate; + + Limit (); +}; + +struct Delay { + struct { + int64_t interval = 0, limit = 0; + bool bypass = 0; + + bool delay () { + if (bypass) + return true; + if (limit) { + --limit; + return true; + } else { + return false; + } + } + + void bump_delay () { + interval += interval < INT64_MAX; + limit = interval; + } + + void reduce_delay () { + if (!interval) + return; + interval /= 2; + limit = interval; + } + + void bypass_delay () { bypass = 1; } + void unbypass_delay () { bypass = 0; } + } bumpreasons; +}; + +struct Last { + struct { + int64_t propagations; + } transred; + struct { + int64_t ticks; + } sweep, vivify, probe; + struct { + int64_t fixed, subsumephases, marked; + } elim; + struct { + int64_t reductions; + } inprobe; + struct { + int64_t conflicts; + } reduce, rephase; + struct { + int64_t ticks; + int64_t marked; + } ternary; + struct { + int64_t fixed; + } collect; + struct { + int64_t marked, ticks; + } factor; + struct { + int64_t conflicts; + int64_t ticks; + } stabilize; + Last (); +}; + +struct Inc { + int64_t flush; // flushing interval in terms of conflicts + int64_t stabilize; // base ticks limit after first mode switch + int64_t conflicts; // next conflict limit if non-negative + int64_t decisions; // next decision limit if non-negative + int64_t preprocessing; // next preprocessing limit if non-negative + int64_t localsearch; // next local search limit if non-negative + Inc (); +}; + +#define SET_EFFORT_LIMIT(LIMIT, NAME, THRESHHOLD) \ + int64_t LIMIT; \ + do { \ + const int64_t OLD_LIMIT = stats.ticks.NAME; \ + const int64_t TICKS = stats.ticks.search[0] + stats.ticks.search[1]; \ + const int64_t LAST = last.NAME.ticks; \ + int64_t REFERENCE = TICKS - LAST; \ + if (!REFERENCE || !stats.conflicts) { \ + VERBOSE (2, "last %" PRId64 " current %" PRId64 " delta %" PRId64, \ + LAST, TICKS, REFERENCE); \ + REFERENCE = opts.preprocessinit; \ + } \ + const double EFFORT = (double) opts.NAME##effort * 1e-3; \ + const int64_t DELTA = EFFORT * REFERENCE; \ + const int64_t THRESH = opts.NAME##thresh * clauses.size (); \ + if (THRESHHOLD && DELTA < THRESH) { \ + VERBOSE (2, \ + "delaying %s with ticklimit %" PRId64 \ + " and threshhold %" PRId64, \ + #NAME, DELTA, THRESH); \ + return false; \ + } \ + last.NAME.ticks = TICKS; \ + const int64_t NEW_LIMIT = OLD_LIMIT + DELTA; \ + LIMIT = NEW_LIMIT; \ + } while (0) + +} // namespace CaDiCaL + +ABC_NAMESPACE_CXX_HEADER_END + +#endif diff --git a/src/sat/cadical/logging.hpp b/src/sat/cadical/logging.hpp new file mode 100644 index 0000000000..edbd60f8c5 --- /dev/null +++ b/src/sat/cadical/logging.hpp @@ -0,0 +1,104 @@ +#ifndef _logging_hpp_INCLUDED +#define _logging_hpp_INCLUDED + +#include "global.h" + +/*------------------------------------------------------------------------*/ +#ifdef LOGGING +/*------------------------------------------------------------------------*/ + +#include <cstdint> +#include <vector> + +ABC_NAMESPACE_CXX_HEADER_START + +namespace CaDiCaL { + +// For debugging purposes and to help understanding what the solver is doing +// there is a logging facility which is compiled in by './configure -l'. It +// still has to be enabled at run-time though (again using the '-l' option +// in the stand-alone solver). It produces quite a bit of information. + +using namespace std; + +struct Clause; +struct Gate; +struct Internal; + +struct Logger { + + static void print_log_prefix (Internal *); + + // Simple logging of a C-style format string. + // + static void log (Internal *, const char *fmt, ...) + CADICAL_ATTRIBUTE_FORMAT (2, 3); + + // Prints the format string (with its argument) and then the clause. The + // clause can also be a zero pointer and then is interpreted as a decision + // (current decision level > 0) or unit clause (zero decision level) and + // printed accordingly. + // + static void log (Internal *, const Clause *, const char *fmt, ...) + CADICAL_ATTRIBUTE_FORMAT (3, 4); + + // Same as before, except that this is meant for the global 'clause' stack + // used for new clauses (and not for reasons). + // + static void log (Internal *, const vector<int> &, const char *fmt, ...) + CADICAL_ATTRIBUTE_FORMAT (3, 4); + + // Another variant, to avoid copying (without logging). + // + static void log (Internal *, const vector<int>::const_iterator &begin, + const vector<int>::const_iterator &end, const char *fmt, + ...) CADICAL_ATTRIBUTE_FORMAT (4, 5); + + // used for logging LRAT proof chains + // + static void log (Internal *, const vector<int64_t> &, const char *fmt, + ...) CADICAL_ATTRIBUTE_FORMAT (3, 4); + + static void log (Internal *, const int *, const unsigned, const char *fmt, + ...) CADICAL_ATTRIBUTE_FORMAT (4, 5); + + static void log_empty_line (Internal *); + + static void log (Internal *, const Gate *, const char *fmt, ...) + CADICAL_ATTRIBUTE_FORMAT (3, 4); + + static string loglit (Internal *, int lit); +}; + +} // namespace CaDiCaL + +/*------------------------------------------------------------------------*/ + +// Make sure that 'logging' code is really not included (second case of the +// '#ifdef') if logging code is not included. + +#define LOG(...) \ + do { \ + if (!internal->opts.log) \ + break; \ + Logger::log (internal, __VA_ARGS__); \ + } while (0) + +#define LOGLIT(lit) Logger::loglit (internal, lit).c_str () + +ABC_NAMESPACE_CXX_HEADER_END + +/*------------------------------------------------------------------------*/ +#else // end of 'then' part of 'ifdef LOGGING' +/*------------------------------------------------------------------------*/ + +#define LOG(...) \ + do { \ + } while (0) + +#define LOGLIT(...) + +/*------------------------------------------------------------------------*/ +#endif // end of 'else' part of 'ifdef LOGGING' +/*------------------------------------------------------------------------*/ +#endif diff --git a/src/sat/cadical/lratchecker.hpp b/src/sat/cadical/lratchecker.hpp new file mode 100644 index 0000000000..0e791e9f4b --- /dev/null +++ b/src/sat/cadical/lratchecker.hpp @@ -0,0 +1,170 @@ +#ifndef _lratchecker_hpp_INCLUDED +#define _lratchecker_hpp_INCLUDED + +#include "global.h" + +/*------------------------------------------------------------------------*/ +#include <unordered_map> + +ABC_NAMESPACE_CXX_HEADER_START + +namespace CaDiCaL { + +/*------------------------------------------------------------------------*/ + +// This checker implements an LRUP checker. +// It requires LRAT-style proof chains for each learned clause +// +// Most of the infrastructure is taken from checker, but without the +// propagation + +/*------------------------------------------------------------------------*/ + +struct LratCheckerClause { + LratCheckerClause *next; // collision chain link for hash table + uint64_t hash; // previously computed full 64-bit hash + int64_t id; // id of clause + bool garbage; // for garbage clauses + unsigned size; + bool used; + bool tautological; + int literals[1]; // 'literals' of length 'size' +}; + +/*------------------------------------------------------------------------*/ + +class LratChecker : public StatTracer { + + Internal *internal; + + // Capacity of variable values. + // + int64_t size_vars; + + // The 'watchers' and 'marks' data structures are not that time critical + // and thus we access them by first mapping a literal to 'unsigned'. + // + static unsigned l2u (int lit); + + signed char &checked_lit (int lit); + signed char &mark (int lit); + + vector<signed char> checked_lits; + vector<signed char> marks; // mark bits of literals + unordered_map<int64_t, vector<int>> clauses_to_reconstruct; + vector<int> assumptions; + vector<int> constraint; + bool concluded; + + uint64_t num_clauses; // number of clauses in hash table + uint64_t num_finalized; + uint64_t num_garbage; // number of garbage clauses + uint64_t size_clauses; // size of clause hash table + LratCheckerClause **clauses; // hash table of clauses + LratCheckerClause *garbage; // linked list of garbage clauses + + vector<int> imported_clause; // original clause for reporting + vector<int64_t> assumption_clauses; + + void enlarge_vars (int64_t idx); + void import_literal (int lit); + void import_clause (const vector<int> &); + + static const unsigned num_nonces = 4; + + uint64_t nonces[num_nonces]; // random numbers for hashing + uint64_t last_hash; // last computed hash value of clause + int64_t last_id; // id of the last added/deleted clause + int64_t current_id; // id of the last added clause + uint64_t compute_hash (int64_t); // compute and save hash value of clause + + // Reduce hash value to the actual size. + // + static uint64_t reduce_hash (uint64_t hash, uint64_t size); + + void enlarge_clauses (); // enlarge hash table for clauses + void insert (); // insert clause in hash table + LratCheckerClause ** + find (const int64_t); // find clause position in hash table + + void add_clause (const char *type); + + void collect_garbage_clauses (); + + LratCheckerClause *new_clause (); + void delete_clause (LratCheckerClause *); + + bool check (vector<int64_t>); // check RUP + bool check_resolution (vector<int64_t>); // check resolution + bool check_blocked (vector<int64_t>); // check ER + + struct { + + int64_t added; // number of added clauses + int64_t original; // number of added original clauses + int64_t derived; // number of added derived clauses + + int64_t deleted; // number of deleted clauses + int64_t finalized; // number of finalized clauses + + int64_t insertions; // number of clauses added to hash table + int64_t collisions; // number of hash collisions in 'find' + int64_t searches; // number of searched clauses in 'find' + + int64_t checks; // number of implication checks + + int64_t collections; // garbage collections + + } stats; + +public: + LratChecker (Internal *); + virtual ~LratChecker (); + + void connect_internal (Internal *i) override; + void begin_proof (int64_t) override; + + void add_original_clause (int64_t, bool, const vector<int> &, + bool restore) override; + void restore_clause (int64_t, const vector<int> &); + + // check the proof chain for the new clause and add it to the checker + void add_derived_clause (int64_t, bool, const vector<int> &, + const vector<int64_t> &) override; + + // check if the clause is present and delete it from the checker + void delete_clause (int64_t, bool, const vector<int> &) override; + // check if the clause is present and delete it from the checker + void weaken_minus (int64_t, const vector<int> &) override; + + // check if the clause is present and delete it from the checker + void finalize_clause (int64_t, const vector<int> &) override; + + // check the proof chain of the assumption clause and delete it + // immediately also check that they contain only assumptions and + // constraints + void add_assumption_clause (int64_t, const vector<int> &, + const vector<int64_t> &) override; + + // mark lit as assumption + void add_assumption (int) override; + + // mark lits as constraint + void add_constraint (const vector<int> &) override; + + void reset_assumptions () override; + + // check if all clauses have been deleted + void report_status (int, int64_t) override; + + void conclude_unsat (ConclusionType, const vector<int64_t> &) override; + + void print_stats () override; + void dump (); // for debugging purposes only +}; + +} // namespace CaDiCaL + +ABC_NAMESPACE_CXX_HEADER_END + +#endif diff --git a/src/sat/cadical/lrattracer.hpp b/src/sat/cadical/lrattracer.hpp new file mode 100644 index 0000000000..9d8e92b6a8 --- /dev/null +++ b/src/sat/cadical/lrattracer.hpp @@ -0,0 +1,63 @@ +#ifndef _lrattracer_h_INCLUDED +#define _lrattracer_h_INCLUDED + +#include "global.h" + +ABC_NAMESPACE_CXX_HEADER_START + +namespace CaDiCaL { + +class LratTracer : public FileTracer { + + Internal *internal; + File *file; + bool binary; + +#ifndef CADICAL_QUIET + int64_t added, deleted; +#endif + int64_t latest_id; + vector<int64_t> delete_ids; + + void put_binary_zero (); + void put_binary_lit (int external_lit); + void put_binary_id (int64_t id); + + // support LRAT + void lrat_add_clause (int64_t, const vector<int> &, + const vector<int64_t> &); + void lrat_delete_clause (int64_t); + +public: + // own and delete 'file' + LratTracer (Internal *, File *file, bool binary); + ~LratTracer (); + + void connect_internal (Internal *i) override; + void begin_proof (int64_t) override; + + void add_original_clause (int64_t, bool, const vector<int> &, + bool = false) override {} // skip + + void add_derived_clause (int64_t, bool, const vector<int> &, + const vector<int64_t> &) override; + + void delete_clause (int64_t, bool, const vector<int> &) override; + + void finalize_clause (int64_t, const vector<int> &) override {} // skip + + void report_status (int, int64_t) override {} // skip + +#ifndef CADICAL_QUIET + void print_statistics (); +#endif + bool closed () override; + void close (bool) override; + void flush (bool) override; +}; + +} // namespace CaDiCaL + +ABC_NAMESPACE_CXX_HEADER_END + +#endif diff --git a/src/sat/cadical/message.hpp b/src/sat/cadical/message.hpp new file mode 100644 index 0000000000..e0209fed89 --- /dev/null +++ b/src/sat/cadical/message.hpp @@ -0,0 +1,71 @@ +#ifndef _message_h_INCLUDED +#define _message_h_INCLUDED + +#include "global.h" + +ABC_NAMESPACE_CXX_HEADER_START + +/*------------------------------------------------------------------------*/ + +// Macros for compact message code. + +#ifndef CADICAL_QUIET + +#define LINE() \ + do { \ + if (internal) \ + internal->message (); \ + } while (0) + +#define MSG(...) \ + do { \ + if (internal) \ + internal->message (__VA_ARGS__); \ + } while (0) + +#define PHASE(...) \ + do { \ + if (internal) \ + internal->phase (__VA_ARGS__); \ + } while (0) + +#define SECTION(...) \ + do { \ + if (internal) \ + internal->section (__VA_ARGS__); \ + } while (0) + +#define VERBOSE(...) \ + do { \ + if (internal) \ + internal->verbose (__VA_ARGS__); \ + } while (0) + +#else + +#define LINE() \ + do { \ + } while (0) +#define MSG(...) \ + do { \ + } while (0) +#define PHASE(...) \ + do { \ + } while (0) +#define SECTION(...) \ + do { \ + } while (0) +#define VERBOSE(...) \ + do { \ + } while (0) + +#endif + +#define FATAL fatal +#define WARNING(...) internal->warning (__VA_ARGS__) + +/*------------------------------------------------------------------------*/ + +ABC_NAMESPACE_CXX_HEADER_END + +#endif // ifndef _message_h_INCLUDED diff --git a/src/sat/cadical/module.make b/src/sat/cadical/module.make new file mode 100644 index 0000000000..74f5d23cb0 --- /dev/null +++ b/src/sat/cadical/module.make @@ -0,0 +1,91 @@ +SRC +=src/sat/cadical/cadicalSolver.c \ +src/sat/cadical/cadicalTest.c \ +src/sat/cadical/cadical_analyze.cpp \ +src/sat/cadical/cadical_arena.cpp \ +src/sat/cadical/cadical_assume.cpp \ +src/sat/cadical/cadical_averages.cpp \ +src/sat/cadical/cadical_backtrack.cpp \ +src/sat/cadical/cadical_backward.cpp \ +src/sat/cadical/cadical_bins.cpp \ +src/sat/cadical/cadical_block.cpp \ +src/sat/cadical/cadical_ccadical.cpp \ +src/sat/cadical/cadical_checker.cpp \ +src/sat/cadical/cadical_clause.cpp \ +src/sat/cadical/cadical_collect.cpp \ +src/sat/cadical/cadical_compact.cpp \ +src/sat/cadical/cadical_condition.cpp \ +src/sat/cadical/cadical_config.cpp \ +src/sat/cadical/cadical_congruence.cpp \ +src/sat/cadical/cadical_constrain.cpp \ +src/sat/cadical/cadical_contract.cpp \ +src/sat/cadical/cadical_cover.cpp \ +src/sat/cadical/cadical_decide.cpp \ +src/sat/cadical/cadical_decompose.cpp \ +src/sat/cadical/cadical_deduplicate.cpp \ +src/sat/cadical/cadical_definition.cpp \ +src/sat/cadical/cadical_drattracer.cpp \ +src/sat/cadical/cadical_elim.cpp \ +src/sat/cadical/cadical_elimfast.cpp \ +src/sat/cadical/cadical_ema.cpp \ +src/sat/cadical/cadical_extend.cpp \ +src/sat/cadical/cadical_external.cpp \ +src/sat/cadical/cadical_external_propagate.cpp \ +src/sat/cadical/cadical_factor.cpp \ +src/sat/cadical/cadical_file.cpp \ +src/sat/cadical/cadical_flags.cpp \ +src/sat/cadical/cadical_flip.cpp \ +src/sat/cadical/cadical_format.cpp \ +src/sat/cadical/cadical_frattracer.cpp \ +src/sat/cadical/cadical_gates.cpp \ +src/sat/cadical/cadical_idruptracer.cpp \ +src/sat/cadical/cadical_instantiate.cpp \ +src/sat/cadical/cadical_internal.cpp \ +src/sat/cadical/cadical_ipasir.cpp \ +src/sat/cadical/cadical_lidruptracer.cpp \ +src/sat/cadical/cadical_limit.cpp \ +src/sat/cadical/cadical_logging.cpp \ +src/sat/cadical/cadical_lookahead.cpp \ +src/sat/cadical/cadical_lratchecker.cpp \ +src/sat/cadical/cadical_lrattracer.cpp \ +src/sat/cadical/cadical_lucky.cpp \ +src/sat/cadical/cadical_message.cpp \ +src/sat/cadical/cadical_minimize.cpp \ +src/sat/cadical/cadical_occs.cpp \ +src/sat/cadical/cadical_options.cpp \ +src/sat/cadical/cadical_parse.cpp \ +src/sat/cadical/cadical_phases.cpp \ +src/sat/cadical/cadical_probe.cpp \ +src/sat/cadical/cadical_profile.cpp \ +src/sat/cadical/cadical_proof.cpp \ +src/sat/cadical/cadical_propagate.cpp \ +src/sat/cadical/cadical_queue.cpp \ +src/sat/cadical/cadical_random.cpp \ +src/sat/cadical/cadical_reap.cpp \ +src/sat/cadical/cadical_reduce.cpp \ +src/sat/cadical/cadical_rephase.cpp \ +src/sat/cadical/cadical_report.cpp \ +src/sat/cadical/cadical_resources.cpp \ +src/sat/cadical/cadical_restart.cpp \ +src/sat/cadical/cadical_restore.cpp \ +src/sat/cadical/cadical_score.cpp \ +src/sat/cadical/cadical_shrink.cpp \ +src/sat/cadical/cadical_signal.cpp \ +src/sat/cadical/cadical_solution.cpp \ +src/sat/cadical/cadical_solver.cpp \ +src/sat/cadical/cadical_stable.cpp \ +src/sat/cadical/cadical_stats.cpp \ +src/sat/cadical/cadical_subsume.cpp \ +src/sat/cadical/cadical_sweep.cpp \ +src/sat/cadical/cadical_terminal.cpp \ +src/sat/cadical/cadical_ternary.cpp \ +src/sat/cadical/cadical_tier.cpp \ +src/sat/cadical/cadical_transred.cpp \ +src/sat/cadical/cadical_unstable.cpp \ +src/sat/cadical/cadical_util.cpp \ +src/sat/cadical/cadical_var.cpp \ +src/sat/cadical/cadical_veripbtracer.cpp \ +src/sat/cadical/cadical_version.cpp \ +src/sat/cadical/cadical_vivify.cpp \ +src/sat/cadical/cadical_walk.cpp \ +src/sat/cadical/cadical_watch.cpp \ +src/sat/cadical/cadical_kitten.c diff --git a/src/sat/cadical/occs.hpp b/src/sat/cadical/occs.hpp new file mode 100644 index 0000000000..28a9246bcf --- /dev/null +++ b/src/sat/cadical/occs.hpp @@ -0,0 +1,42 @@ +#ifndef _occs_h_INCLUDED +#define _occs_h_INCLUDED + +#include "global.h" + +#include <vector> + +ABC_NAMESPACE_CXX_HEADER_START + +namespace CaDiCaL { + +// Full occurrence lists used in a one-watch scheme for all clauses in +// subsumption checking and for irredundant clauses in variable elimination. + +struct Clause; +using namespace std; + +typedef vector<Clause *> Occs; + +inline void shrink_occs (Occs &os) { shrink_vector (os); } +inline void erase_occs (Occs &os) { erase_vector (os); } + +inline void remove_occs (Occs &os, Clause *c) { + const auto end = os.end (); + auto i = os.begin (); + for (auto j = i; j != end; j++) { + const Clause *d = *i++ = *j; + if (c == d) + i--; + } + CADICAL_assert (i + 1 == end); + os.resize (i - os.begin ()); +} + +typedef Occs::iterator occs_iterator; +typedef Occs::const_iterator const_occs_iterator; + +} // namespace CaDiCaL + +ABC_NAMESPACE_CXX_HEADER_END + +#endif diff --git a/src/sat/cadical/options.hpp b/src/sat/cadical/options.hpp new file mode 100644 index 0000000000..1527da2e1a --- /dev/null +++ b/src/sat/cadical/options.hpp @@ -0,0 +1,422 @@ +#ifndef _options_hpp_INCLUDED +#define _options_hpp_INCLUDED + +#include "global.h" + +ABC_NAMESPACE_CXX_HEADER_START + +/*------------------------------------------------------------------------*/ + +// In order to add a new option, simply add a new line below. Make sure that +// options are sorted correctly (with '!}sort -k 2' in 'vi'). Otherwise +// initializing the options will trigger an internal error. For the model +// based tester 'mobical' the policy is that options which become redundant +// because another one is disabled (set to zero) should have the name of the +// latter as prefix. The 'O' column determines the options which are +// target to 'optimize' them ('-O[1-3]'). A zero value in the 'O' column +// means that this option is not optimized. A value of '1' results in +// optimizing its value exponentially with exponent base '2', and a value +// of '2' uses base '10'. The 'P' column determines simplification +// options (disabled with '--plain') and 'R' which values can be reset. + +// clang-format off + +#define OPTIONS \ +\ +/* NAME DEFAULT, LO, HI,O,P,R, USAGE */ \ +\ +OPTION( arena, 1, 0, 1,0,0,1, "allocate clauses in arena") \ +OPTION( arenacompact, 1, 0, 1,0,0,1, "keep clauses compact") \ +OPTION( arenasort, 1, 0, 1,0,0,1, "sort clauses in arena") \ +OPTION( arenatype, 3, 1, 3,0,0,1, "1=clause, 2=var, 3=queue") \ +OPTION( binary, 1, 0, 1,0,0,1, "use binary proof format") \ +OPTION( block, 0, 0, 1,0,1,1, "blocked clause elimination") \ +OPTION( blockmaxclslim, 1e5, 1,2e9,2,0,1, "maximum clause size") \ +OPTION( blockminclslim, 2, 2,2e9,0,0,1, "minimum clause size") \ +OPTION( blockocclim, 1e2, 1,2e9,2,0,1, "occurrence limit") \ +OPTION( bump, 1, 0, 1,0,0,1, "bump variables") \ +OPTION( bumpreason, 1, 0, 1,0,0,1, "bump reason literals too") \ +OPTION( bumpreasondepth, 1, 1, 3,0,0,1, "bump reason depth") \ +OPTION( bumpreasonlimit, 10, 1,2e9,0,0,1, "bump reason limit") \ +OPTION( bumpreasonrate, 100, 1,2e9,0,0,1, "bump reason decision rate") \ +OPTION( check, 0, 0, 1,0,0,0, "enable internal checking") \ +OPTION( checkassumptions, 1, 0, 1,0,0,0, "check assumptions satisfied") \ +OPTION( checkconstraint, 1, 0, 1,0,0,0, "check constraint satisfied") \ +OPTION( checkfailed, 1, 0, 1,0,0,0, "check failed literals form core") \ +OPTION( checkfrozen, 0, 0, 1,0,0,0, "check all frozen semantics") \ +OPTION( checkproof, 3, 0, 3,0,0,0, "1=drat, 2=lrat, 3=both") \ +OPTION( checkwitness, 1, 0, 1,0,0,0, "check witness internally") \ +OPTION( chrono, 1, 0, 2,0,0,1, "chronological backtracking") \ +OPTION( chronoalways, 0, 0, 1,0,0,1, "force always chronological") \ +OPTION( chronolevelim, 1e2, 0,2e9,0,0,1, "chronological level limit") \ +OPTION( chronoreusetrail, 1, 0, 1,0,0,1, "reuse trail chronologically") \ +OPTION( compact, 1, 0, 1,0,1,1, "compact internal variables") \ +OPTION( compactint, 2e3, 1,2e9,0,0,1, "compacting interval") \ +OPTION( compactlim, 1e2, 0,1e3,0,0,1, "inactive limit per mille") \ +OPTION( compactmin, 1e2, 1,2e9,0,0,1, "minimum inactive limit") \ +OPTION( condition, 0, 0, 1,0,1,1, "globally blocked clause elim") \ +OPTION( conditioneffort, 100, 1,1e5,0,0,1, "relative efficiency per mille") \ +OPTION( conditionint, 1e4, 1,2e9,0,0,1, "initial conflict interval") \ +OPTION( conditionmaxeff, 1e7, 0,2e9,1,0,1, "maximum condition efficiency") \ +OPTION( conditionmaxrat, 100, 1,2e9,1,0,1, "maximum clause variable ratio") \ +OPTION( conditionmineff, 0, 0,2e9,1,0,1, "minimum condition efficiency") \ +OPTION( congruence, 1, 0, 1,0,0,1, "congruence closure") \ +OPTION( congruenceand, 1, 0, 1,0,0,1, "extract AND gates") \ +OPTION( congruenceandarity,1e6,2,5e7,0,0,1, "AND gate arity limit") \ +OPTION( congruencebinaries,1, 0, 1,0,0,1, "extract binary and strengthen ternary clauses") \ +OPTION( congruenceite, 1, 0, 1,0,0,1, "extract ITE gates") \ +OPTION( congruencexor, 1, 0, 1,0,0,1, "extract XOR gates") \ +OPTION( congruencexorarity,4, 2, 31,0,0,1, "XOR gate arity limit") \ +OPTION( congruencexorcounts,1, 1,5e6,0,0,1, "XOR gate round") \ +OPTION( cover, 0, 0, 1,0,1,1, "covered clause elimination") \ +OPTION( covereffort, 4, 1,1e5,1,0,1, "relative efficiency per mille") \ +OPTION( covermaxclslim, 1e5, 1,2e9,2,0,1, "maximum clause size") \ +OPTION( covermaxeff, 1e8, 0,2e9,1,0,1, "maximum cover efficiency") \ +OPTION( coverminclslim, 2, 2,2e9,0,0,1, "minimum clause size") \ +OPTION( covermineff, 0, 0,2e9,1,0,1, "minimum cover efficiency") \ +OPTION( decompose, 1, 0, 1,0,1,1, "decompose BIG in SCCs and ELS") \ +OPTION( decomposerounds, 2, 1, 16,1,0,1, "number of decompose rounds") \ +OPTION( deduplicate, 1, 0, 1,0,1,1, "remove duplicated binaries") \ +OPTION( eagersubsume, 1, 0, 1,0,1,1, "subsume recently learned") \ +OPTION( eagersubsumelim, 20, 1,1e3,0,0,1, "limit on subsumed candidates") \ +OPTION( elim, 1, 0, 1,0,1,1, "bounded variable elimination") \ +OPTION( elimands, 1, 0, 1,0,0,1, "find AND gates") \ +OPTION( elimbackward, 1, 0, 1,0,0,1, "eager backward subsumption") \ +OPTION( elimboundmax, 16, -1,2e6,1,0,1, "maximum elimination bound") \ +OPTION( elimboundmin, 0, -1,2e6,0,0,1, "minimum elimination bound") \ +OPTION( elimclslim, 1e2, 2,2e9,2,0,1, "resolvent size limit") \ +OPTION( elimdef, 0, 0, 1,0,0,1, "mine definitions with cadical_kitten") \ +OPTION( elimdefcores, 1, 1,100,0,0,1, "number of unsat cores") \ +OPTION( elimdefticks, 2e5, 0,2e9,1,0,1, "cadical_kitten ticks limit") \ +OPTION( elimeffort, 1e3, 1,1e5,1,0,1, "relative efficiency per mille") \ +OPTION( elimequivs, 1, 0, 1,0,0,1, "find equivalence gates") \ +OPTION( elimint, 2e3, 1,2e9,0,0,1, "elimination interval") \ +OPTION( elimites, 1, 0, 1,0,0,1, "find if-then-else gates") \ +OPTION( elimlimited, 1, 0, 1,0,0,1, "limit resolutions") \ +OPTION( elimmaxeff, 2e9, 0,2e9,1,0,1, "maximum elimination efficiency") \ +OPTION( elimmineff, 1e7, 0,2e9,1,0,1, "minimum elimination efficiency") \ +OPTION( elimocclim, 1e2, 0,2e9,2,0,1, "occurrence limit") \ +OPTION( elimprod, 1, 0,1e4,0,0,1, "elim score product weight") \ +OPTION( elimrounds, 2, 1,512,1,0,1, "usual number of rounds") \ +OPTION( elimsubst, 1, 0, 1,0,0,1, "elimination by substitution") \ +OPTION( elimsum, 1, 0,1e4,0,0,1, "elimination score sum weight") \ +OPTION( elimxorlim, 5, 2, 27,1,0,1, "maximum XOR size") \ +OPTION( elimxors, 1, 0, 1,0,0,1, "find XOR gates") \ +OPTION( emadecisions, 1e5, 1,2e9,0,0,1, "window decision rate") \ +OPTION( emagluefast, 33, 1,2e9,0,0,1, "window fast glue") \ +OPTION( emaglueslow, 1e5, 1,2e9,0,0,1, "window slow glue") \ +OPTION( emajump, 1e5, 1,2e9,0,0,1, "window back-jump level") \ +OPTION( emalevel, 1e5, 1,2e9,0,0,1, "window back-track level") \ +OPTION( emasize, 1e5, 1,2e9,0,0,1, "window learned clause size") \ +OPTION( ematrailfast, 1e2, 1,2e9,0,0,1, "window fast trail") \ +OPTION( ematrailslow, 1e5, 1,2e9,0,0,1, "window slow trail") \ +OPTION( exteagerreasons, 1, 0, 1,0,0,1, "eagerly ask for all reasons (0: only when needed)") \ +OPTION( exteagerrecalc, 1, 0, 1,0,0,1, "after eagerly asking for reasons recalculate all levels (0: trust the external tool)") \ +OPTION( externallrat, 0, 0, 1,0,0,1, "external lrat") \ +OPTION( factor, 1, 0, 1,0,1,1, "bounded variable addition") \ +OPTION( factorcandrounds, 2, 0,2e9,0,0,1, "candidates reduction rounds") \ +OPTION( factoreffort, 50, 0,1e6,0,0,1, "relative effort per mille") \ +OPTION( factoriniticks, 300, 1,1e6,0,0,1, "initial effort in millions") \ +OPTION( factorsize, 5, 2,2e9,0,0,1, "clause size limit") \ +OPTION( factorthresh, 7, 0,100,1,0,1, "delay if ticks smaller thresh*clauses") \ +OPTION( fastelim, 1, 0, 1,0,1,1, "fast BVE during preprocessing") \ +OPTION( fastelimbound, 8, 1,1e3,1,0,1, "fast BVE bound during preprocessing") \ +OPTION( fastelimclslim, 1e2, 2,2e9,2,0,1, "fast BVE resolvent size limit") \ +OPTION( fastelimocclim, 100, 1,2e9,2,0,1, "fast BVE occurence limit during preprocessing") \ +OPTION( fastelimrounds, 4, 1,512,1,0,1, "number of fastelim rounds") \ +OPTION( flush, 0, 0, 1,0,1,1, "flush redundant clauses") \ +OPTION( flushfactor, 3, 1,1e3,0,0,1, "interval increase") \ +OPTION( flushint, 1e5, 1,2e9,0,0,1, "initial limit") \ +OPTION( forcephase, 0, 0, 1,0,0,1, "always use initial phase") \ +OPTION( frat, 0, 0, 2,0,0,1, "1=frat(lrat), 2=frat(drat)") \ +OPTION( idrup, 0, 0, 1,0,0,1, "incremental proof format") \ +OPTION( ilb, 0, 0, 1,0,0,1, "ILB (incremental lazy backtrack)") \ +OPTION( ilbassumptions, 0, 0, 1,0,0,1, "trail reuse for assumptions (ILB-like)") \ +OPTION( inprobeint, 100, 1,2e9,0,0,1, "inprobing interval" ) \ +OPTION( inprobing, 1, 0, 1,0,1,1, "enable probe inprocessing") \ +OPTION( inprocessing, 1, 0, 1,0,1,1, "enable general inprocessing") \ +OPTION( instantiate, 0, 0, 1,0,1,1, "variable instantiation") \ +OPTION( instantiateclslim, 3, 2,2e9,0,0,1, "minimum clause size") \ +OPTION( instantiateocclim, 1, 1,2e9,2,0,1, "maximum occurrence limit") \ +OPTION( instantiateonce, 1, 0, 1,0,0,1, "instantiate each clause once") \ +OPTION( lidrup, 0, 0, 1,0,0,1, "linear incremental proof format") \ +LOGOPT( log, 0, 0, 1,0,0,0, "enable logging") \ +LOGOPT( logsort, 0, 0, 1,0,0,0, "sort logged clauses") \ +OPTION( lrat, 0, 0, 1,0,0,1, "use LRAT proof format") \ +OPTION( lucky, 1, 0, 1,0,0,1, "search for lucky phases") \ +OPTION( minimize, 1, 0, 1,0,0,1, "minimize learned clauses") \ +OPTION( minimizedepth, 1e3, 0,1e3,0,0,1, "minimization depth") \ +OPTION( minimizeticks, 1, 0, 1,0,0,1, "increment ticks in minimization") \ +OPTION( otfs, 1, 0, 1,0,0,1, "on-the-fly self subsumption") \ +OPTION( phase, 1, 0, 1,0,0,1, "initial phase") \ +OPTION( preprocessinit, 2e6, 0,2e9,2,0,1, "initial preprocessing base limit" ) \ +OPTION( preprocesslight, 1, 0, 1,0,1,1, "lightweight preprocessing" ) \ +OPTION( probe, 1, 0, 1,0,1,1, "failed literal probing" ) \ +OPTION( probeeffort, 8, 1,1e5,1,0,1, "relative efficiency per mille") \ +OPTION( probehbr, 1, 0, 1,0,0,1, "learn hyper binary clauses") \ +OPTION( probethresh, 0, 0,100,1,0,1, "delay if ticks smaller thresh*clauses") \ +OPTION( profile, 2, 0, 4,0,0,0, "profiling level") \ +QUTOPT( quiet, 0, 0, 1,0,0,0, "disable all messages") \ +OPTION( radixsortlim, 32, 0,2e9,0,0,1, "radix sort limit") \ +OPTION( realtime, 0, 0, 1,0,0,0, "real instead of process time") \ +OPTION( recomputetier, 1, 0, 1,0,0,1, "recompute tiers") \ +OPTION( reduce, 1, 0, 1,0,0,1, "reduce useless clauses") \ +OPTION( reduceinit, 300, 1,1e6,0,0,1, "initial interval") \ +OPTION( reduceint, 25, 2,1e6,0,0,1, "reduce interval") \ +OPTION( reduceopt, 1, 0, 2,0,0,1, "0=prct,1=sqrt,2=max") \ +OPTION( reducetarget, 75, 10,1e2,0,0,1, "reduce fraction in percent") \ +OPTION( reducetier1glue, 2, 1,2e9,0,0,1, "glue of kept learned clauses") \ +OPTION( reducetier2glue, 6, 1,2e9,0,0,1, "glue of tier two clauses") \ +OPTION( reluctant, 1024, 0,2e9,0,0,1, "reluctant doubling period") \ +OPTION( reluctantmax,1048576, 0,2e9,0,0,1, "reluctant doubling period") \ +OPTION( rephase, 1, 0, 1,0,0,1, "enable resetting phase") \ +OPTION( rephaseint, 1e3, 1,2e9,0,0,1, "rephase interval") \ +OPTION( report,reportdefault, 0, 1,0,0,1, "enable reporting") \ +OPTION( reportall, 0, 0, 1,0,0,1, "report even if not successful") \ +OPTION( reportsolve, 0, 0, 1,0,0,1, "use solving not process time") \ +OPTION( restart, 1, 0, 1,0,0,1, "enable restarts") \ +OPTION( restartint, 2, 1,2e9,0,0,1, "restart interval") \ +OPTION( restartmargin, 10, 0,1e2,0,0,1, "slow fast margin in percent") \ +OPTION( restartreusetrail, 1, 0, 1,0,0,1, "enable trail reuse") \ +OPTION( restoreall, 0, 0, 2,0,0,1, "restore all clauses (2=really)") \ +OPTION( restoreflush, 0, 0, 1,0,0,1, "remove satisfied clauses") \ +OPTION( reverse, 0, 0, 1,0,0,1, "reverse variable ordering") \ +OPTION( score, 1, 0, 1,0,0,1, "use EVSIDS scores") \ +OPTION( scorefactor, 950,500,1e3,0,0,1, "score factor per mille") \ +OPTION( seed, 0, 0,2e9,0,0,1, "random seed") \ +OPTION( shrink, 3, 0, 3,0,0,1, "shrink conflict clause (1=only with binary, 2=minimize when pulling, 3=full)") \ +OPTION( shrinkreap, 1, 0, 1,0,0,1, "use a reap for shrinking") \ +OPTION( shuffle, 0, 0, 1,0,0,1, "shuffle variables") \ +OPTION( shufflequeue, 1, 0, 1,0,0,1, "shuffle variable queue") \ +OPTION( shufflerandom, 0, 0, 1,0,0,1, "not reverse but random") \ +OPTION( shufflescores, 1, 0, 1,0,0,1, "shuffle variable scores") \ +OPTION( stabilize, 1, 0, 1,0,0,1, "enable stabilizing phases") \ +OPTION( stabilizeinit, 1e3, 1,2e9,0,0,1, "stabilizing interval") \ +OPTION( stabilizeonly, 0, 0, 1,0,0,1, "only stabilizing phases") \ +OPTION( stats, 0, 0, 1,0,0,1, "print all statistics at the end of the run") \ +OPTION( subsume, 1, 0, 1,0,1,1, "enable clause subsumption") \ +OPTION( subsumebinlim, 1e4, 0,2e9,1,0,1, "watch list length limit") \ +OPTION( subsumeclslim, 1e2, 0,2e9,2,0,1, "clause length limit") \ +OPTION( subsumeeffort, 1e3, 1,1e5,1,0,1, "relative efficiency per mille") \ +OPTION( subsumelimited, 1, 0, 1,0,0,1, "limit subsumption checks") \ +OPTION( subsumemaxeff, 1e8, 0,2e9,1,0,1, "maximum subsuming efficiency") \ +OPTION( subsumemineff, 0, 0,2e9,1,0,1, "minimum subsuming efficiency") \ +OPTION( subsumeocclim, 1e2, 0,2e9,1,0,1, "watch list length limit") \ +OPTION( subsumestr, 1, 0, 1,0,0,1, "subsume strenghten") \ +OPTION( sweep, 1, 0, 1,0,1,1, "enable SAT sweeping") \ +OPTION( sweepclauses, 1024, 0,2e9,1,0,1, "environment clauses") \ +OPTION( sweepcomplete, 0, 0, 1,0,0,1, "run SAT sweeping to completion") \ +OPTION( sweepcountbinary, 1, 0, 1,0,0,1, "count binaries to environment") \ +OPTION( sweepdepth, 2, 0,2e9,1,0,1, "environment depth") \ +OPTION( sweepeffort, 1e2, 0,1e4,0,0,1, "relative effort in ticks per mille") \ +OPTION( sweepfliprounds, 1, 0,2e9,1,0,1, "flipping rounds") \ +OPTION( sweepmaxclauses, 3e5, 2,2e9,1,0,1, "maximum environment clauses") \ +OPTION( sweepmaxdepth, 3, 1,2e9,1,0,1, "maximum environment depth") \ +OPTION( sweepmaxvars, 8192, 2,2e9,1,0,1, "maximum environment variables") \ +OPTION( sweeprand, 0, 0, 1,0,0,1, "randomize sweeping environment") \ +OPTION( sweepthresh, 5, 0,100,1,0,1, "delay if ticks smaller thresh*clauses") \ +OPTION( sweepvars, 256, 0,2e9,1,0,1, "environment variables") \ +OPTION( target, 1, 0, 2,0,0,1, "target phases (1=stable only)") \ +OPTION( terminateint, 10, 0,1e4,0,0,1, "termination check interval") \ +OPTION( ternary, 1, 0, 1,0,1,1, "hyper ternary resolution") \ +OPTION( ternaryeffort, 8, 1,1e5,1,0,1, "relative efficiency per mille") \ +OPTION( ternarymaxadd, 1e3, 0,1e4,1,0,1, "max clauses added in percent") \ +OPTION( ternaryocclim, 1e2, 1,2e9,2,0,1, "ternary occurrence limit") \ +OPTION( ternaryrounds, 2, 1, 16,1,0,1, "maximum ternary rounds") \ +OPTION( ternarythresh, 6, 0,100,1,0,1, "delay if ticks smaller thresh*clauses") \ +OPTION( tier1limit, 50, 0,100,0,0,1, "limit of tier1 usage in percentage") \ +OPTION( tier2limit, 90, 0,100,0,0,1, "limit of tier2 usage in percentage") \ +OPTION( transred, 1, 0, 1,0,1,1, "transitive reduction of BIG") \ +OPTION( transredeffort, 1e2, 1,1e5,1,0,1, "relative efficiency per mille") \ +OPTION( transredmaxeff, 1e8, 0,2e9,1,0,1, "maximum efficiency") \ +OPTION( transredmineff, 0, 0,2e9,1,0,1, "minimum efficiency") \ +QUTOPT( verbose, 0, 0, 3,0,0,0, "more verbose messages") \ +OPTION( veripb, 0, 0, 4,0,0,1, "odd=checkdeletions, > 2=drat") \ +OPTION( vivify, 1, 0, 1,0,1,1, "vivification") \ +OPTION( vivifycalctier, 0, 0, 1,0,0,1, "recalculate tier limits") \ +OPTION( vivifydemote, 0, 0, 1,0,1,1, "demote irredundant or delete directly") \ +OPTION( vivifyeffort, 50, 0,1e5,1,0,1, "overall efficiency per mille") \ +OPTION( vivifyflush, 1, 0, 1,1,0,1, "flush subsumed before vivification rounds") \ +OPTION( vivifyinst, 1, 0, 1,0,0,1, "instantiate last literal when vivify") \ +OPTION( vivifyirred, 1, 0, 1,0,1,1, "vivification irred") \ +OPTION( vivifyirredeff, 3, 1,100,1,0,1, "irredundant efficiency per mille") \ +OPTION( vivifyonce, 0, 0, 2,0,0,1, "vivify once: 1=red, 2=red+irr") \ +OPTION( vivifyretry, 0, 0, 5,0,0,1, "re-vivify clause if vivify was successful") \ +OPTION( vivifyschedmax, 5e3, 10,2e9,0,0,1, "maximum schedule size") \ +OPTION( vivifythresh, 20, 0,100,1,0,1, "delay if ticks smaller thresh*clauses") \ +OPTION( vivifytier1, 1, 0, 1,0,1,1, "vivification tier1") \ +OPTION( vivifytier1eff, 4, 0,100,1,0,1, "relative tier1 effort") \ +OPTION( vivifytier2, 1, 0, 1,0,1,1, "vivification tier2") \ +OPTION( vivifytier2eff, 2, 1,100,1,0,1, "relative tier2 effort") \ +OPTION( vivifytier3, 1, 0, 1,0,1,1, "vivification tier3") \ +OPTION( vivifytier3eff, 1, 1,100,1,0,1, "relative tier3 effort") \ +OPTION( walk, 1, 0, 1,0,0,1, "enable random walks") \ +OPTION( walkeffort, 20, 1,1e5,1,0,1, "relative efficiency per mille") \ +OPTION( walkmaxeff, 1e7, 0,2e9,1,0,1, "maximum efficiency") \ +OPTION( walkmineff, 0, 0,1e7,1,0,1, "minimum efficiency") \ +OPTION( walknonstable, 1, 0, 1,0,0,1, "walk in non-stabilizing phase") \ +OPTION( walkredundant, 0, 0, 1,0,0,1, "walk redundant clauses too") \ + +// Note, keep an empty line right before this line because of the last '\'! +// Also keep those single spaces after 'OPTION(' for proper sorting. + +// clang-format on + +/*------------------------------------------------------------------------*/ + +// Some of the 'OPTION' macros above should only be included if certain +// compile time options are enabled. This has the effect, that for instance +// if 'LOGGING' is defined, and thus logging code is included, then also the +// 'log' option is defined. Otherwise the 'log' option is not included. + +#ifdef LOGGING +#define LOGOPT OPTION +#else +#define LOGOPT(...) /**/ +#endif + +#ifdef CADICAL_QUIET +#define QUTOPT(...) /**/ +#else +#define QUTOPT OPTION +#endif + +/*------------------------------------------------------------------------*/ + +namespace CaDiCaL { + +struct Internal; + +/*------------------------------------------------------------------------*/ + +class Options; + +struct Option { + const char *name; + int def, lo, hi; + int optimizable; + bool preprocessing; + const char *description; + int &val (Options *); +}; + +/*------------------------------------------------------------------------*/ + +// Produce a compile time constant for the number of options. + +static const size_t number_of_options = +#define OPTION(N, V, L, H, O, P, R, D) 1 + + OPTIONS +#undef OPTION + + 0; + +/*------------------------------------------------------------------------*/ + +class Options { + + Internal *internal; + + void set (Option *, int val); // Force to [lo,hi] interval. + + friend struct Option; + static Option table[]; + + static void initialize_from_environment (int &val, const char *name, + const int L, const int H); + + friend Config; + + void reset_default_values (); + void disable_preprocessing (); + +public: + // For library usage we disable reporting by default while for the stand + // alone SAT solver we enable it by default. This default value has to + // be set before the constructor of 'Options' is called (which in turn is + // called from the constructor of 'Solver'). If we would simply overwrite + // its initial value while initializing the stand alone solver, we will + // get that change of the default value (from 'false' to 'true') shown + // during calls to 'print ()', which is confusing to the user. + // + static int reportdefault; + + Options (Internal *); + + // Makes options directly accessible, e.g., for instance declares the + // member 'int restart' here. This will give fast access to option values + // internally in the solver and thus can also be used in tight loops. + // +private: + int __start_of_options__; // Used by 'val' below. +public: +#define OPTION(N, V, L, H, O, P, R, D) \ + int N; // Access option values by name. + OPTIONS +#undef OPTION + + // It would be more elegant to use an anonymous 'struct' of the actual + // option values overlayed with an 'int values[number_of_options]' array + // but that is not proper ISO C++ and produces a warning. Instead we use + // the following construction which relies on '__start_of_options__' and + // that the following options are really allocated directly after it. + // + inline int &val (size_t idx) { + CADICAL_assert (idx < number_of_options); + return (&__start_of_options__ + 1)[idx]; + } + + // With the following function we can get rather fast access to the option + // limits, the default value and the description. The code uses binary + // search over the sorted option 'table'. This static data is shared + // among different instances of the solver. The actual current option + // values are here in the 'Options' class. They can be accessed by the + // offset of the static options using 'Option::val' if you have an + // 'Option' or to have even faster access directly by the member function + // (the 'N' above, e.g., 'restart'). + // + static Option *has (const char *name); + + bool set (const char *name, int); // Explicit version. + int get (const char *name); // Get current value. + + void print (); // Print current values in command line form + static void usage (); // Print usage message for all options. + + void optimize (int val); // increase some limits (val=0..31) + + static bool is_preprocessing_option (const char *name); + + // Parse long option argument + // + // --<name> + // --<name>=<val> + // --no-<name> + // + // where '<val>' is as in 'parse_option_value'. If parsing succeeds, + // 'true' is returned and the string will be set to the name of the + // option. Additionally the parsed value is set (last argument). + // + static bool parse_long_option (const char *, string &, int &); + + // Iterating options. + + typedef Option *iterator; + typedef const Option *const_iterator; + + static iterator begin () { return table; } + static iterator end () { return table + number_of_options; } + + void copy (Options &other) const; // Copy 'this' into 'other'. +}; + +inline int &Option::val (Options *opts) { + CADICAL_assert (Options::table <= this && + this < Options::table + number_of_options); + return opts->val (this - Options::table); +} + +} // namespace CaDiCaL + +ABC_NAMESPACE_CXX_HEADER_END + +#endif diff --git a/src/sat/cadical/parse.hpp b/src/sat/cadical/parse.hpp new file mode 100644 index 0000000000..a37117f438 --- /dev/null +++ b/src/sat/cadical/parse.hpp @@ -0,0 +1,78 @@ +#ifndef _parse_hpp_INCLUDED +#define _parse_hpp_INCLUDED + +#include "global.h" + +#include <cassert> +#include <vector> + +ABC_NAMESPACE_CXX_HEADER_START + +namespace CaDiCaL { + +// Factors out common functions for parsing of DIMACS and solution files. + +class File; +struct External; +struct Internal; + +class Parser { + + Solver *solver; + Internal *internal; + External *external; + File *file; + + void perr (const char *fmt, ...) CADICAL_ATTRIBUTE_FORMAT (2, 3); + int parse_char (); + + enum { + FORCED = 0, // Force reading even if header is broken. + RELAXED = 1, // Relaxed white space treatment in header. + STRICT = 2, // Strict white space and header compliance. + }; + + const char *parse_string (const char *str, char prev); + const char *parse_positive_int (int &ch, int &res, const char *name); + const char *parse_lit (int &ch, int &lit, int &vars, int strict); + const char *parse_dimacs_non_profiled (int &vars, int strict); + const char *parse_solution_non_profiled (); + + bool *parse_inccnf_too; + vector<int> *cubes; + +public: + // Parse a DIMACS CNF or ICNF file. + // + // Return zero if successful. Otherwise parse error. + Parser (Solver *s, File *f, bool *i, vector<int> *c) + : solver (s), internal (s->internal), external (s->external), + file (f), parse_inccnf_too (i), cubes (c) {} + + // Parse a DIMACS file. Return zero if successful. Otherwise a parse + // error is return. The parsed clauses are added to the solver and the + // maximum variable index found is returned in the 'vars' argument. The + // 'strict' argument can be '0' in which case the numbers in the header + // can be arbitrary, e.g., 'p cnf 0 0' all the time, without producing a + // parse error. Only for this setting the parsed literals are not checked + // to overflow the maximum variable index of the header. The strictest + // form of parsing is enforced for the value '2' of 'strict', in which + // case the header can not have additional white space, while a value of + // '1' exactly relaxes this, e.g., 'p cnf \t 1 3 \r\n' becomes legal. + // + const char *parse_dimacs (int &vars, int strict); + + // Parse a solution file as used in the SAT competition, e.g., with + // comment lines 'c ...', a status line 's ...' and value lines 'v ...'. + // Returns zero if successful. Otherwise a string is returned describing + // the parse error. The parsed solution is saved in 'solution' and can be + // accessed with 'sol (int lit)'. We use it for checking learned clauses. + // + const char *parse_solution (); +}; + +} // namespace CaDiCaL + +ABC_NAMESPACE_CXX_HEADER_END + +#endif diff --git a/src/sat/cadical/phases.hpp b/src/sat/cadical/phases.hpp new file mode 100644 index 0000000000..98e54d4395 --- /dev/null +++ b/src/sat/cadical/phases.hpp @@ -0,0 +1,24 @@ +#ifndef _phases_hpp_INCLUDED +#define _phases_hpp_INCLUDED + +#include "global.h" + +ABC_NAMESPACE_CXX_HEADER_START + +namespace CaDiCaL { + +struct Phases { + + vector<signed char> best; // The current largest trail phase. + vector<signed char> forced; // Forced through 'phase'. + vector<signed char> min; // The current minimum unsatisfied phase. + vector<signed char> prev; // Previous during local search. + vector<signed char> saved; // The actual saved phase. + vector<signed char> target; // The current target phase. +}; + +} // namespace CaDiCaL + +ABC_NAMESPACE_CXX_HEADER_END + +#endif diff --git a/src/sat/cadical/profile.hpp b/src/sat/cadical/profile.hpp new file mode 100644 index 0000000000..f3799389b4 --- /dev/null +++ b/src/sat/cadical/profile.hpp @@ -0,0 +1,280 @@ +#ifndef _profiles_h_INCLUDED +#define _profiles_h_INCLUDED + +#include "global.h" + +ABC_NAMESPACE_CXX_HEADER_START + +/*------------------------------------------------------------------------*/ +#ifndef CADICAL_QUIET +/*------------------------------------------------------------------------*/ + +namespace CaDiCaL { + +struct Internal; + +/*------------------------------------------------------------------------*/ + +// The solver contains some built in profiling (even for optimized code). +// The idea is that even without using external tools it is possible to get +// an overview of where time is spent. This is enabled with the option +// 'profile', e.g., you might want to use '--profile=3', or even higher +// values for more detailed profiling information. Currently the default is +// '--profile=2', which should only induce a tiny profiling overhead. +// +// Profiling has a Heisenberg effect, since we rely on calling 'getrusage' +// instead of using profile counters and sampling. For functions which are +// executed many times, this overhead is substantial (say 10%-20%). For +// functions which are not executed many times there is in essence no +// overhead in measuring time spent in them. These get a smaller profiling +// level, which is the second argument in the 'PROFILE' macro below. Thus +// using '--profile=1' for instance should not add any penalty to the +// run-time, while '--profile=3' and higher levels slow down the solver. +// +// To profile say 'foo', just add another line 'PROFILE(foo,LEVEL)' and wrap +// the code to be profiled within a 'START (foo)' / 'STOP (foo)' block. + +/*------------------------------------------------------------------------*/ + +// Profile counters for functions which are not compiled in should be +// removed. This is achieved by adding a wrapper macro for them here. + +/*------------------------------------------------------------------------*/ + +#ifdef PROFILE_MODE +#define MROFILE PROFILE +#else +#define MROFILE(...) /**/ +#endif + +#define PROFILES \ + PROFILE (analyze, 3) \ + MROFILE (analyzestable, 4) \ + MROFILE (analyzeunstable, 4) \ + PROFILE (backward, 3) \ + PROFILE (block, 2) \ + PROFILE (bump, 4) \ + PROFILE (checking, 2) \ + PROFILE (cdcl, 1) \ + PROFILE (collect, 3) \ + PROFILE (compact, 3) \ + PROFILE (condition, 2) \ + PROFILE (congruence, 2) \ + PROFILE (congruencemerge, 4) \ + PROFILE (congruencematching, 4) \ + PROFILE (connect, 3) \ + PROFILE (copy, 4) \ + PROFILE (cover, 2) \ + PROFILE (decide, 3) \ + PROFILE (decompose, 3) \ + PROFILE (definition, 2) \ + PROFILE (elim, 2) \ + PROFILE (factor, 2) \ + PROFILE (fastelim, 2) \ + PROFILE (extend, 3) \ + PROFILE (extract, 3) \ + PROFILE (extractands, 4) \ + PROFILE (extractbinaries, 4) \ + PROFILE (extractites, 4) \ + PROFILE (extractxors, 4) \ + PROFILE (instantiate, 2) \ + PROFILE (lucky, 2) \ + PROFILE (lookahead, 2) \ + PROFILE (minimize, 4) \ + PROFILE (shrink, 4) \ + PROFILE (parse, 0) /* As 'opts.profile' might change in parsing*/ \ + PROFILE (probe, 2) \ + PROFILE (deduplicate, 3) \ + PROFILE (propagate, 4) \ + MROFILE (propstable, 4) \ + MROFILE (propunstable, 4) \ + PROFILE (reduce, 3) \ + PROFILE (restart, 3) \ + PROFILE (restore, 2) \ + PROFILE (search, 1) \ + PROFILE (solve, 0) \ + PROFILE (stable, 2) \ + PROFILE (sweep, 2) \ + PROFILE (sweepbackbone, 3) \ + PROFILE (sweepequivalences, 3) \ + PROFILE (sweepflip, 4) \ + PROFILE (sweepimplicant, 4) \ + PROFILE (sweepsolve, 4) \ + PROFILE (preprocess, 2) \ + PROFILE (simplify, 1) \ + PROFILE (subsume, 2) \ + PROFILE (ternary, 2) \ + PROFILE (transred, 3) \ + PROFILE (unstable, 2) \ + PROFILE (vivify, 2) \ + PROFILE (walk, 2) + +/*------------------------------------------------------------------------*/ + +// See 'START' and 'STOP' in 'macros.hpp' too. + +struct Profile { + + bool active; + double value; // accumulated time + double started; // started time if active + const char *name; // name of the profiled function (or 'phase') + const int level; // allows to cheaply test if profiling is enabled + + Profile (const char *n, int l) + : active (false), value (0), name (n), level (l) {} +}; + +struct Profiles { + Internal *internal; +#define PROFILE(NAME, LEVEL) Profile NAME; + PROFILES +#undef PROFILE + Profiles (Internal *); +}; + +} // namespace CaDiCaL + +#define NON_CADICAL_QUIET_PROFILE_CODE(CODE) CODE + +#else // !defined(CADICAL_QUIET) + +#define NON_CADICAL_QUIET_PROFILE_CODE(CODE) /**/ + +#endif + +/*------------------------------------------------------------------------*/ + +// Macros for Profiling support and checking and changing the mode. + +#define START(P) \ + do { \ + NON_CADICAL_QUIET_PROFILE_CODE ( \ + if (internal->profiles.P.level <= internal->opts.profile) \ + internal->start_profiling (internal->profiles.P, \ + internal->time ());) \ + } while (0) + +#define STOP(P) \ + do { \ + NON_CADICAL_QUIET_PROFILE_CODE ( \ + if (internal->profiles.P.level <= internal->opts.profile) \ + internal->stop_profiling (internal->profiles.P, \ + internal->time ());) \ + } while (0) + +#define PROFILE_ACTIVE(P) \ + ((internal->profiles.P.level <= internal->opts.profile) && \ + (internal->profiles.P.active)) + +/*------------------------------------------------------------------------*/ + +#define START_SIMPLIFIER(S, M) \ + do { \ + NON_CADICAL_QUIET_PROFILE_CODE (const double N = time (); \ + const int L = internal->opts.profile;) \ + if (!preprocessing && !lookingahead) { \ + NON_CADICAL_QUIET_PROFILE_CODE ( \ + if (stable && internal->profiles.stable.level <= L) \ + internal->stop_profiling (internal->profiles.stable, N); \ + if (!stable && internal->profiles.unstable.level <= L) \ + internal->stop_profiling (internal->profiles.unstable, N); \ + if (internal->profiles.search.level <= L) \ + internal->stop_profiling (internal->profiles.search, N);) \ + reset_mode (SEARCH); \ + } \ + NON_CADICAL_QUIET_PROFILE_CODE ( \ + if (internal->profiles.simplify.level <= L) \ + internal->start_profiling (internal->profiles.simplify, N); \ + if (internal->profiles.S.level <= L) \ + internal->start_profiling (internal->profiles.S, N);) \ + set_mode (SIMPLIFY); \ + set_mode (M); \ + } while (0) + +/*------------------------------------------------------------------------*/ + +#define STOP_SIMPLIFIER(S, M) \ + do { \ + NON_CADICAL_QUIET_PROFILE_CODE ( \ + const double N = time (); const int L = internal->opts.profile; \ + if (internal->profiles.S.level <= L) \ + internal->stop_profiling (internal->profiles.S, N); \ + if (internal->profiles.simplify.level <= L) \ + internal->stop_profiling (internal->profiles.simplify, N);) \ + reset_mode (M); \ + reset_mode (SIMPLIFY); \ + if (!preprocessing && !lookingahead) { \ + NON_CADICAL_QUIET_PROFILE_CODE ( \ + if (internal->profiles.search.level <= L) \ + internal->start_profiling (internal->profiles.search, N); \ + if (stable && internal->profiles.stable.level <= L) \ + internal->start_profiling (internal->profiles.stable, N); \ + if (!stable && internal->profiles.unstable.level <= L) \ + internal->start_profiling (internal->profiles.unstable, N);) \ + set_mode (SEARCH); \ + } \ + } while (0) + +/*------------------------------------------------------------------------*/ +// Used in 'walk' before calling 'walk_round' within the CDCL loop. + +#define START_INNER_WALK() \ + do { \ + require_mode (SEARCH); \ + CADICAL_assert (!preprocessing); \ + NON_CADICAL_QUIET_PROFILE_CODE ( \ + const double N = time (); const int L = internal->opts.profile; \ + if (stable && internal->profiles.stable.level <= L) \ + internal->stop_profiling (internal->profiles.stable, N); \ + if (!stable && internal->profiles.unstable.level <= L) \ + internal->stop_profiling (internal->profiles.unstable, N); \ + if (internal->profiles.walk.level <= L) \ + internal->start_profiling (internal->profiles.walk, N);) \ + set_mode (WALK); \ + } while (0) + +/*------------------------------------------------------------------------*/ +// Used in 'walk' after calling 'walk_round' within the CDCL loop. + +#define STOP_INNER_WALK() \ + do { \ + require_mode (SEARCH); \ + CADICAL_assert (!preprocessing); \ + reset_mode (WALK); \ + NON_CADICAL_QUIET_PROFILE_CODE ( \ + const double N = time (); const int L = internal->opts.profile; \ + if (internal->profiles.walk.level <= L) \ + internal->stop_profiling (internal->profiles.walk, N); \ + if (stable && internal->profiles.stable.level <= L) \ + internal->start_profiling (internal->profiles.stable, N); \ + if (!stable && internal->profiles.unstable.level <= L) \ + internal->start_profiling (internal->profiles.unstable, N); \ + internal->profiles.walk.started = (N);) \ + } while (0) + +/*------------------------------------------------------------------------*/ +// Used in 'local_search' before calling 'walk_round'. + +#define START_OUTER_WALK() \ + do { \ + require_mode (SEARCH); \ + CADICAL_assert (!preprocessing); \ + NON_CADICAL_QUIET_PROFILE_CODE (START (walk);) \ + set_mode (WALK); \ + } while (0) + +/*------------------------------------------------------------------------*/ +// Used in 'local_search' after calling 'walk_round'. + +#define STOP_OUTER_WALK() \ + do { \ + require_mode (SEARCH); \ + CADICAL_assert (!preprocessing); \ + reset_mode (WALK); \ + NON_CADICAL_QUIET_PROFILE_CODE (STOP (walk);) \ + } while (0) + +ABC_NAMESPACE_CXX_HEADER_END + +#endif // ifndef _profiles_h_INCLUDED diff --git a/src/sat/cadical/proof.hpp b/src/sat/cadical/proof.hpp new file mode 100644 index 0000000000..69be6b0807 --- /dev/null +++ b/src/sat/cadical/proof.hpp @@ -0,0 +1,120 @@ +#ifndef _proof_h_INCLUDED +#define _proof_h_INCLUDED + +#include "global.h" + +ABC_NAMESPACE_CXX_HEADER_START + +namespace CaDiCaL { + +/*------------------------------------------------------------------------*/ + +class File; +struct Clause; +struct Internal; +class Tracer; +class FileTracer; + +/*------------------------------------------------------------------------*/ + +// Provides proof checking and writing. + +class Proof { + + Internal *internal; + + vector<int> clause; // of external literals + vector<int64_t> proof_chain; // LRAT style proof chain of clause + int64_t clause_id; // id of added clause + bool redundant; + + // the 'tracers' + vector<Tracer *> tracers; // tracers (ie checker) + vector<FileTracer *> file_tracers; // file tracers (ie LRAT tracer) + + void add_literal (int internal_lit); // add to 'clause' + void add_literals (Clause *); // add to 'clause' + + void add_literals (const vector<int> &); // ditto + + void add_original_clause ( + bool restore = false); // notify observers of original clauses + void add_derived_clause (); + void add_assumption_clause (); + void delete_clause (); + void demote_clause (); + void weaken_minus (); + void strengthen (); + void finalize_clause (); + void add_assumption (); + void add_constraint (); + +public: + Proof (Internal *); + ~Proof (); + + void connect (Tracer *t) { tracers.push_back (t); } + void disconnect (Tracer *t); + // Add original clauses to the proof (for online proof checking). + // + void add_original_clause (int64_t, bool, const vector<int> &); + + void add_assumption_clause (int64_t, const vector<int> &, + const vector<int64_t> &); + void add_assumption_clause (int64_t, int, const vector<int64_t> &); + void add_assumption (int); + void add_constraint (const vector<int> &); + void reset_assumptions (); + + // Add/delete original clauses to/from the proof using their original + // external literals (from external->eclause) + // + void add_external_original_clause (int64_t, bool, const vector<int> &, + bool restore = false); + void delete_external_original_clause (int64_t, bool, const vector<int> &); + + // Add derived (such as learned) clauses to the proof. + // + void add_derived_empty_clause (int64_t, const vector<int64_t> &); + void add_derived_unit_clause (int64_t, int unit, const vector<int64_t> &); + void add_derived_clause (Clause *c, const vector<int64_t> &); + void add_derived_clause (int64_t, bool, const vector<int> &, + const vector<int64_t> &); + + // deletion of clauses. It comes in several variants, depending if the + // clause should be restored or not + void delete_clause (int64_t, bool, const vector<int> &); + void weaken_minus (int64_t, const vector<int> &); + void weaken_plus (int64_t, const vector<int> &); + void delete_unit_clause (int64_t id, const int lit); + void delete_clause (Clause *); + void weaken_minus (Clause *); + void weaken_plus (Clause *); + void strengthen (int64_t); + + void finalize_unit (int64_t, int); + void finalize_external_unit (int64_t, int); + void finalize_clause (int64_t, const vector<int> &c); + void finalize_clause (Clause *); + + void report_status (int, int64_t); + void begin_proof (int64_t); + void conclude_unsat (ConclusionType, const vector<int64_t> &); + void conclude_sat (const vector<int> &model); + void conclude_unknown (const vector<int> &trace); + void solve_query (); + // These two actually pretend to add and remove a clause. + // + void flush_clause (Clause *); // remove falsified literals + void strengthen_clause (Clause *, int, const vector<int64_t> &); + void otfs_strengthen_clause (Clause *, const vector<int> &, + const vector<int64_t> &); + + void flush (); +}; + +} // namespace CaDiCaL + +ABC_NAMESPACE_CXX_HEADER_END + +#endif diff --git a/src/sat/cadical/queue.hpp b/src/sat/cadical/queue.hpp new file mode 100644 index 0000000000..14e1f2da7b --- /dev/null +++ b/src/sat/cadical/queue.hpp @@ -0,0 +1,70 @@ +#ifndef _queue_hpp_INCLUDED +#define _queue_hpp_INCLUDED + +#include "global.h" + +#include <vector> + +ABC_NAMESPACE_CXX_HEADER_START + +namespace CaDiCaL { + +// Links for double linked decision queue. + +struct Link { + + int prev, next; // variable indices + + // initialized explicitly in 'init_queue' +}; + +typedef std::vector<Link> Links; + +// Variable move to front (VMTF) decision queue ordered by 'bumped'. See +// our SAT'15 paper for an explanation on how this works. + +struct Queue { + + // We use integers instead of variable pointers. This is more compact and + // also avoids issues due to moving the variable table during 'resize'. + + int first, last; // anchors (head/tail) for doubly linked list + int unassigned; // all variables after this one are assigned + int64_t bumped; // see 'Internal.update_queue_unassigned' + + Queue () : first (0), last (0), unassigned (0), bumped (0) {} + + // We explicitly provide the mapping of integer indices to links to the + // following two (inlined) functions. This avoids including + // 'internal.hpp' and breaks a cyclic dependency, so we can keep their + // code here in this header file. Otherwise they are just ordinary doubly + // linked list 'dequeue' and 'enqueue' operations. + + inline void dequeue (Links &links, int idx) { + Link &l = links[idx]; + if (l.prev) + links[l.prev].next = l.next; + else + first = l.next; + if (l.next) + links[l.next].prev = l.prev; + else + last = l.prev; + } + + inline void enqueue (Links &links, int idx) { + Link &l = links[idx]; + if ((l.prev = last)) + links[last].next = idx; + else + first = idx; + last = idx; + l.next = 0; + } +}; + +} // namespace CaDiCaL + +ABC_NAMESPACE_CXX_HEADER_END + +#endif diff --git a/src/sat/cadical/radix.hpp b/src/sat/cadical/radix.hpp new file mode 100644 index 0000000000..4c4d7b574e --- /dev/null +++ b/src/sat/cadical/radix.hpp @@ -0,0 +1,186 @@ +#ifndef _radix_hpp_INCLUDED +#define _radix_hpp_INCLUDED + +#include "global.h" + +#include <cassert> +#include <cstring> +#include <iterator> +#include <vector> + +ABC_NAMESPACE_CXX_HEADER_START + +namespace CaDiCaL { + +using namespace std; + +// This provides an implementation of a generic radix sort algorithm. The +// reason for having it is that for certain benchmarks and certain parts of +// CaDiCaL where sorting is used, the standard sorting algorithm 'sort' +// turned out to be a hot-spot. Up to 30% of the total running time was for +// instance used for some benchmarks in sorting variables during bumping +// to make sure to bump them in 'enqueued' order. +// +// Further, in most cases, where we need to sort something, sorting is +// actually performed on positive numbers (such as the 'enqueued' time stamp +// during bumping), which allows to use radix sort or variants. At least +// starting with medium sized arrays to be sorted (say above 1000 elements, +// but see discussion on 'MSORT' below), radix sort can be way faster. +// +// Finally it is stable, which is actually preferred most of the time too. +// +// This template algorithm 'rsort' takes as first template parameter the +// iterator class similar to the standard 'sort' algorithm template, but +// then as second parameter a function class (similar to the second 'less +// than' parameter of 'sort') which can obtain a 'rank' from each element, +// on which they are compared. The 'rank' should be able to turn an element +// into a number. The type of these ranks is determined automatically but +// should be 'unsigned'. + +struct pointer_rank { + typedef size_t Type; + Type operator() (void *ptr) { return (size_t) ptr; } +}; + +template <class I, class Rank> void rsort (I first, I last, Rank rank) { + typedef typename iterator_traits<I>::value_type T; + typedef typename Rank::Type R; + + CADICAL_assert (first <= last); + const size_t n = last - first; + if (n <= 1) + return; + + const size_t l = 8; // Radix 8, thus byte-wise. + const size_t w = (1 << l); // So many buckets. + + const unsigned mask = w - 1; // Fast mod 'w'. + +// Uncomment the following define for large values of 'w' in order to keep +// the large bucket array 'count' on the heap instead of the stack. +// +// #define CADICAL_RADIX_BUCKETS_ON_THE_HEAP +// +#ifdef CADICAL_RADIX_BUCKETS_ON_THE_HEAP + size_t *count = new size_t[w]; // Put buckets on the heap. +#else + size_t count[w]; // Put buckets on the stack. +#endif + + I a = first, b = last, c = a; + bool initialized = false; + std::vector<T> v; + + R upper = 0, lower = ~upper; + R shifted = mask; + bool bounded = false; + + R masked_lower = 0, masked_upper = mask; + + for (size_t i = 0; i < 8 * sizeof (rank (*first)); + i += l, shifted <<= l) { + + if (bounded && (lower & shifted) == (upper & shifted)) + continue; + + memset (count + masked_lower, 0, + (masked_upper - masked_lower + 1) * sizeof *count); + + const I end = c + n; + bool sorted = true; + R last = 0; + + for (I p = c; p != end; p++) { + const auto r = rank (*p); + if (!bounded) + lower &= r, upper |= r; + const auto s = r >> i; + const auto m = s & mask; + if (sorted && last > m) + sorted = false; + else + last = m; + count[m]++; + } + + masked_lower = (lower >> i) & mask; + masked_upper = (upper >> i) & mask; + + if (!bounded) { + bounded = true; + if ((lower & shifted) == (upper & shifted)) + continue; + } + + if (sorted) + continue; + + size_t pos = 0; + for (R j = masked_lower; j <= masked_upper; j++) { + const size_t delta = count[j]; + count[j] = pos; + pos += delta; + } + + if (!initialized) { + CADICAL_assert (&*c == &*a); // MS VC++ + v.resize (n); + b = v.begin (); + initialized = true; + } + + I d = (&*c == &*a) ? b : a; // MS VC++ + + for (I p = c; p != end; p++) { + const auto r = rank (*p); + const auto s = r >> i; + const auto m = s & mask; + d[count[m]++] = *p; + } + c = d; + } + + if (c == b) { + for (size_t i = 0; i < n; i++) + a[i] = b[i]; + } + +#ifdef CADICAL_RADIX_BUCKETS_ON_THE_HEAP + delete[] count; +#endif + +#ifndef CADICAL_NDEBUG + for (I p = first; p + 1 != last; p++) + CADICAL_assert (rank (p[0]) <= rank (p[1])); +#endif +} + +// It turns out that for small number of elements (like '100') and in +// particular for large value ranges the standard sorting function is +// considerably faster than our radix sort (like 2.5x). This negative effect +// vanishes at around 800 elements (sorting integers) and thus we provide a +// function 'MSORT' which selects between standard sort and radix sort based +// on the number of elements. However we failed to put this into proper C++ +// style template code and thus have to use a macro instead. We also do not +// use it everywhere instead of 'rsort' since it requires a fourth +// parameter, which is awkward, particular in those situation where we +// expect large arrays to be sorted anyhow (such as during sorting the +// clauses in an arena or the probes during probing). The first argument +// is the limit up to which we use the standard sort. Above the limit we +// use radix sort. As usual we do not want to hard code it here (default +// is '800') in order to make fuzzing and delta debugging more effective. + +#define MSORT(LIMIT, FIRST, LAST, RANK, LESS) \ + do { \ + const size_t N = LAST - FIRST; \ + if (N <= (size_t) (LIMIT)) \ + sort (FIRST, LAST, LESS); \ + else \ + rsort (FIRST, LAST, RANK); \ + } while (0) + +} // namespace CaDiCaL + +ABC_NAMESPACE_CXX_HEADER_END + +#endif diff --git a/src/sat/cadical/random.h b/src/sat/cadical/random.h new file mode 100644 index 0000000000..62fe5ed19c --- /dev/null +++ b/src/sat/cadical/random.h @@ -0,0 +1,50 @@ +#ifndef _random_h_INCLUDED +#define _random_h_INCLUDED + +#include "global.h" + +#include <assert.h> +#include <stdbool.h> +#include <stdint.h> + +ABC_NAMESPACE_HEADER_START + +typedef uint64_t generator; + +static inline uint64_t kissat_next_random64 (generator *rng) { + *rng *= 6364136223846793005ul; + *rng += 1442695040888963407ul; + return *rng; +} + +static inline unsigned kissat_next_random32 (generator *rng) { + return kissat_next_random64 (rng) >> 32; +} + +static inline unsigned kissat_pick_random (generator *rng, unsigned l, + unsigned r) { + CADICAL_assert (l <= r); + if (l == r) + return l; + const unsigned delta = r - l; + const unsigned tmp = kissat_next_random32 (rng); + const double fraction = tmp / 4294967296.0; + CADICAL_assert (0 <= fraction), CADICAL_assert (fraction < 1); + const unsigned scaled = delta * fraction; + CADICAL_assert (scaled < delta); + const unsigned res = l + scaled; + CADICAL_assert (l <= res), CADICAL_assert (res < r); + return res; +} + +static inline bool kissat_pick_bool (generator *rng) { + return kissat_pick_random (rng, 0, 2); +} + +static inline double kissat_pick_double (generator *rng) { + return kissat_next_random32 (rng) / 4294967296.0; +} + +ABC_NAMESPACE_HEADER_END + +#endif diff --git a/src/sat/cadical/random.hpp b/src/sat/cadical/random.hpp new file mode 100644 index 0000000000..a5c78d38aa --- /dev/null +++ b/src/sat/cadical/random.hpp @@ -0,0 +1,104 @@ +#ifndef _random_hpp_INCLUDED +#define _random_hpp_INCLUDED + +#include "global.h" + +#include <cstdint> + +ABC_NAMESPACE_CXX_HEADER_START + +// Random number generator. + +namespace CaDiCaL { + +class Random { + + uint64_t state; + + void add (uint64_t a) { + if (!(state += a)) + state = 1; + next (); + } + +public: + // Without argument use a machine, process and time dependent seed. + // + Random (); + + Random (uint64_t seed) : state (seed) {} + void operator= (uint64_t seed) { state = seed; } + Random (const Random &other) : state (other.seed ()) {} + + void operator+= (uint64_t a) { add (a); } + uint64_t seed () const { return state; } + + uint64_t next () { + state *= 6364136223846793005ul; + state += 1442695040888963407ul; + CADICAL_assert (state); + return state; + } + + uint32_t generate () { + next (); + return state >> 32; + } + int generate_int () { return (int) generate (); } + bool generate_bool () { return generate () < 2147483648u; } + + // Generate 'double' value in the range '[0,1]' excluding '1'. + // + double generate_double () { return generate () / 4294967295.0; } + + // Generate 'int' value in the range '[l,r]'. + // + int pick_int (int l, int r) { + CADICAL_assert (l <= r); + const unsigned delta = 1 + r - (unsigned) l; + unsigned tmp = generate (), scaled; + if (delta) { + const double fraction = tmp / 4294967296.0; + scaled = delta * fraction; + } else + scaled = tmp; + const int res = scaled + l; + CADICAL_assert (l <= res); + CADICAL_assert (res <= r); + return res; + } + + int pick_log (int l, int r) { + CADICAL_assert (l <= r); + const unsigned delta = 1 + r - (unsigned) l; + int log_delta = delta ? 0 : 32; + while (log_delta < 32 && (1u << log_delta) < delta) + log_delta++; + const int log_res = pick_int (0, log_delta); + unsigned tmp = generate (); + if (log_res < 32) + tmp &= (1u << log_res) - 1; + if (delta) + tmp %= delta; + const int res = l + tmp; + CADICAL_assert (l <= res), CADICAL_assert (res <= r); + return res; + } + + // Generate 'double' value in the range '[l,r]'. + // + double pick_double (double l, double r) { + CADICAL_assert (l <= r); + double res = (r - l) * generate_double (); + res += l; + CADICAL_assert (l <= res); + CADICAL_assert (res <= r); + return res; + } +}; + +} // namespace CaDiCaL + +ABC_NAMESPACE_CXX_HEADER_END + +#endif diff --git a/src/sat/cadical/range.hpp b/src/sat/cadical/range.hpp new file mode 100644 index 0000000000..7049dab42a --- /dev/null +++ b/src/sat/cadical/range.hpp @@ -0,0 +1,105 @@ +#ifndef _range_hpp_INCLUDED +#define _range_hpp_INCLUDED + +#include "global.h" + +#include <cassert> + +ABC_NAMESPACE_CXX_HEADER_START + +namespace CaDiCaL { + +struct Clause; + +/*----------------------------------------------------------------------*/ + +// Used for compact and safe iteration over positive ranges of integers, +// particularly for iterating over all variable indices. +// +// Range vars (max_var); +// for (auto idx : vars) ... +// +// This iterates over '1, ..., max_var' and is safe for non-negative +// numbers, thus also for 'max_var == 0' or 'max_var == INT_MAX'. +// +// Note that +// +// for (int idx = 1; idx <= max_var; idx++) ... +// +// leads to an overflow if 'max_var == INT_MAX' and thus depending on what +// the compiler does ('int' overflow is undefined) might lead to any +// behaviour (infinite loop or worse array access way out of bounds). +// +// If we make 'idx' in this last 'for' loop an 'unsigned' then it is safe to +// use this idiom, but we would need to cast 'max_var' explicitly to 'int' +// in order to avoid a warning in the loop condition and actually everywhere +// where 'idx' is compared to a 'signed' expression. Worse for instance +// 'vals[-idx]' will lead to out of bounds access too. This is awkward and +// using the range iterator provided here is safer in general. +// +// Another issue is that the dereferencing operator '*' below is required to +// return a reference to the internal index of the iterator. Thus the 'idx' +// in the auto loop is actually of the same type as the internal state of +// the iterator. To keep it 'signed' and still avoid overflow issues we +// just have to make sure to use the proper increment (with two implicit +// casts, i.e., from 'int' to 'unsigned', then 'unsigned' addition and the +// result is cast back from 'unsigned' to 'int'). +// +// For simplicity we keep a reference to the actual maximum integer, e.g., +// 'max_var', which makes the idiom 'for (auto idx : vars) ...' possible. +// Further note that the referenced integer has to be non-negative before +// starting to iterate (it can be zero though), otherwise it breaks. + +class Range { + static unsigned inc (unsigned u) { return u + 1u; } + class iterator { + int idx; + + public: + iterator (int i) : idx (i) {} + void operator++ () { idx = inc (idx); } + const int &operator* () const { return idx; } + friend bool operator!= (const iterator &a, const iterator &b) { + return a.idx != b.idx; + } + }; + int &n; + +public: + iterator begin () const { return CADICAL_assert (n >= 0), iterator (inc (0)); } + iterator end () const { return CADICAL_assert (n >= 0), iterator (inc (n)); } + Range (int &m) : n (m) { CADICAL_assert (m >= 0); } +}; + +// Same, but iterating over literals '-1,1,-2,2,....,-max_var,max_var'. +// +// The only difference to 'Range' is the 'inc' function, but I am too lazy +// to figure out how to properly factor the code into a generic range +// template with 'inc' as only parameter. This gives at least clean code. + +class Sange { + static unsigned inc (unsigned u) { return ~u + (u >> 31); } + class iterator { + int lit; + + public: + iterator (int i) : lit (i) {} + void operator++ () { lit = inc (lit); } + const int &operator* () const { return lit; } + friend bool operator!= (const iterator &a, const iterator &b) { + return a.lit != b.lit; + } + }; + int &n; + +public: + iterator begin () const { return CADICAL_assert (n >= 0), iterator (inc (0)); } + iterator end () const { return CADICAL_assert (n >= 0), iterator (inc (n)); } + Sange (int &m) : n (m) { CADICAL_assert (m >= 0); } +}; + +} // namespace CaDiCaL + +ABC_NAMESPACE_CXX_HEADER_END + +#endif diff --git a/src/sat/cadical/reap.hpp b/src/sat/cadical/reap.hpp new file mode 100644 index 0000000000..1ea10e6e34 --- /dev/null +++ b/src/sat/cadical/reap.hpp @@ -0,0 +1,34 @@ +#ifndef _reap_h_INCLUDED +#define _reap_h_INCLUDED + +#include "global.h" + +#include <cstddef> +#include <vector> + +ABC_NAMESPACE_CXX_HEADER_START + +class Reap { +public: + Reap (); + void init (); + void release (); + inline bool empty () { return !num_elements; } + + inline size_t size () { return num_elements; } + + void push (unsigned); + void clear (); + unsigned pop (); + +private: + size_t num_elements; + unsigned last_deleted; + unsigned min_bucket; + unsigned max_bucket; + std::vector<unsigned> buckets[33]; +}; + +ABC_NAMESPACE_CXX_HEADER_END + +#endif diff --git a/src/sat/cadical/reluctant.hpp b/src/sat/cadical/reluctant.hpp new file mode 100644 index 0000000000..495382b56b --- /dev/null +++ b/src/sat/cadical/reluctant.hpp @@ -0,0 +1,88 @@ +#ifndef _reluctant_hpp_INCLUDED +#define _reluctant_hpp_INCLUDED + +#include "global.h" + +#include <cassert> +#include <cstdint> + +ABC_NAMESPACE_CXX_HEADER_START + +namespace CaDiCaL { + +// This is Donald Knuth's version of the Luby restart sequence which he +// called 'reluctant doubling'. His bit-twiddling formulation in line (DK) +// requires to keep two words around which are updated every time the +// reluctant doubling sequence is advanced. The original version in the +// literature uses a complex recursive function which computes the length of +// the next inactive sub-sequence every time (but is state-less). +// +// In our code we incorporate a base interval 'period' and only after period +// many calls to 'tick' times the current sequence value we update the +// reluctant doubling sequence value. The 'tick' call is decoupled from +// the activation signal of the sequence (the 'bool ()' operator) through +// 'trigger'. It is also possible to set an upper limit to the length of an +// inactive sub-sequence. If that limit is reached the whole reluctant +// doubling sequence starts over with the initial values. + +class Reluctant { + + uint64_t u, v, limit; + uint64_t period, countdown; + bool trigger, limited; + +public: + Reluctant () : period (0), trigger (false) {} + + void enable (int p, int64_t l) { + CADICAL_assert (p > 0); + u = v = 1; + period = countdown = p; + trigger = false; + if (l <= 0) + limited = false; + else + limited = true, limit = l; + }; + + void disable () { period = 0, trigger = false; } + + // Increments the count until the 'period' is hit. Then it performs the + // actual increment of reluctant doubling. This gives the common 'Luby' + // sequence with the specified base interval period. As soon the limit is + // reached (countdown goes to zero) we remember this event and then + // disable updating the reluctant sequence until the signal is delivered. + + void tick () { + + if (!period) + return; // disabled + if (trigger) + return; // already triggered + if (--countdown) + return; // not there yet + + if ((u & -u) == v) + u = u + 1, v = 1; + else + v = 2 * v; // (DK) + + if (limited && v >= limit) + u = v = 1; + countdown = v * period; + trigger = true; + } + + operator bool () { + if (!trigger) + return false; + trigger = false; + return true; + } +}; + +} // namespace CaDiCaL + +ABC_NAMESPACE_CXX_HEADER_END + +#endif diff --git a/src/sat/cadical/resources.hpp b/src/sat/cadical/resources.hpp new file mode 100644 index 0000000000..852668b320 --- /dev/null +++ b/src/sat/cadical/resources.hpp @@ -0,0 +1,22 @@ +#ifndef _resources_hpp_INCLUDED +#define _resources_hpp_INCLUDED + +#include "global.h" + +#include <cstdint> + +ABC_NAMESPACE_CXX_HEADER_START + +namespace CaDiCaL { + +double absolute_real_time (); +double absolute_process_time (); + +uint64_t maximum_resident_set_size (); +uint64_t current_resident_set_size (); + +} // namespace CaDiCaL + +ABC_NAMESPACE_CXX_HEADER_END + +#endif // ifndef _resources_hpp_INCLUDED diff --git a/src/sat/cadical/score.hpp b/src/sat/cadical/score.hpp new file mode 100644 index 0000000000..88196121e8 --- /dev/null +++ b/src/sat/cadical/score.hpp @@ -0,0 +1,22 @@ +#ifndef _score_hpp_INCLUDED +#define _score_hpp_INCLUDED + +#include "global.h" + +ABC_NAMESPACE_CXX_HEADER_START + +namespace CaDiCaL { + +struct score_smaller { + Internal *internal; + score_smaller (Internal *i) : internal (i) {} + bool operator() (unsigned a, unsigned b); +}; + +typedef heap<score_smaller> ScoreSchedule; + +} // namespace CaDiCaL + +ABC_NAMESPACE_CXX_HEADER_END + +#endif diff --git a/src/sat/cadical/signal.hpp b/src/sat/cadical/signal.hpp new file mode 100644 index 0000000000..1dc2fb3b70 --- /dev/null +++ b/src/sat/cadical/signal.hpp @@ -0,0 +1,39 @@ +#ifndef _signal_hpp_INCLUDED +#define _signal_hpp_INCLUDED + +#include "global.h" + +ABC_NAMESPACE_CXX_HEADER_START + +namespace CaDiCaL { + +// Helper class for handling signals in applications. + +class Handler { +public: + Handler () {} + virtual ~Handler () {} + virtual void catch_signal (int sig) = 0; +#ifndef WIN32 + virtual void catch_alarm (); +#endif +}; + +class Signal { + +public: + static void set (Handler *); + static void reset (); +#ifndef WIN32 + static void alarm (int seconds); + static void reset_alarm (); +#endif + + static const char *name (int sig); +}; + +} // namespace CaDiCaL + +ABC_NAMESPACE_CXX_HEADER_END + +#endif diff --git a/src/sat/cadical/stack.h b/src/sat/cadical/stack.h new file mode 100644 index 0000000000..89f270388a --- /dev/null +++ b/src/sat/cadical/stack.h @@ -0,0 +1,116 @@ +#ifndef _stack_h_INCLUDED +#define _stack_h_INCLUDED + +#include "global.h" + +#include <stdlib.h> + +ABC_NAMESPACE_HEADER_START + +#define STACK(TYPE) \ + struct { \ + TYPE *begin; \ + TYPE *end; \ + TYPE *allocated; \ + } + +#define FULL_STACK(S) ((S).end == (S).allocated) +#define EMPTY_STACK(S) ((S).begin == (S).end) +#define SIZE_STACK(S) ((size_t) ((S).end - (S).begin)) +#define CAPACITY_STACK(S) ((size_t) ((S).allocated - (S).begin)) + +#define INIT_STACK(S) \ + do { \ + (S).begin = (S).end = (S).allocated = 0; \ + } while (0) + +#define TOP_STACK(S) (END_STACK (S)[CADICAL_assert (!EMPTY_STACK (S)), -1]) + +#define PEEK_STACK(S, N) \ + (BEGIN_STACK (S)[CADICAL_assert ((N) < SIZE_STACK (S)), (N)]) + +#define POKE_STACK(S, N, E) \ + do { \ + PEEK_STACK (S, N) = (E); \ + } while (0) + +#define POP_STACK(S) (CADICAL_assert (!EMPTY_STACK (S)), *--(S).end) + +#define PUSH_STACK(S, E) \ + do { \ + if (FULL_STACK (S)) \ + ENLARGE_STACK (S); \ + *(S).end++ = (E); \ + } while (0) + +#define BEGIN_STACK(S) (S).begin + +#define END_STACK(S) (S).end + +#define CLEAR_STACK(S) \ + do { \ + (S).end = (S).begin; \ + } while (0) + +#define RESIZE_STACK(S, NEW_SIZE) \ + do { \ + const size_t TMP_NEW_SIZE = (NEW_SIZE); \ + CADICAL_assert (TMP_NEW_SIZE <= SIZE_STACK (S)); \ + (S).end = (S).begin + TMP_NEW_SIZE; \ + } while (0) + +#define SET_END_OF_STACK(S, P) \ + do { \ + CADICAL_assert (BEGIN_STACK (S) <= (P)); \ + CADICAL_assert ((P) <= END_STACK (S)); \ + if ((P) == END_STACK (S)) \ + break; \ + (S).end = (P); \ + } while (0) + +#define RELEASE_STACK(S) \ + do { \ + DEALLOC ((S).begin, CAPACITY_STACK (S)); \ + INIT_STACK (S); \ + } while (0) + +#define REMOVE_STACK(T, S, E) \ + do { \ + CADICAL_assert (!EMPTY_STACK (S)); \ + T *END_REMOVE_STACK = END_STACK (S); \ + T *P_REMOVE_STACK = BEGIN_STACK (S); \ + while (*P_REMOVE_STACK != (E)) { \ + P_REMOVE_STACK++; \ + CADICAL_assert (P_REMOVE_STACK != END_REMOVE_STACK); \ + } \ + P_REMOVE_STACK++; \ + while (P_REMOVE_STACK != END_REMOVE_STACK) { \ + P_REMOVE_STACK[-1] = *P_REMOVE_STACK; \ + P_REMOVE_STACK++; \ + } \ + (S).end--; \ + } while (0) + +#define all_stack(T, E, S) \ + T E, *E##_PTR = BEGIN_STACK (S), *const E##_END = END_STACK (S); \ + E##_PTR != E##_END && (E = *E##_PTR, true); \ + ++E##_PTR + +#define all_pointers(T, E, S) \ + T *E, *const *E##_PTR = BEGIN_STACK (S), \ + *const *const E##_END = END_STACK (S); \ + E##_PTR != E##_END && (E = *E##_PTR, true); \ + ++E##_PTR + +// clang-format off + +typedef STACK (char) chars; +typedef STACK (int) ints; +typedef STACK (size_t) sizes; +typedef STACK (unsigned) unsigneds; + +// clang-format on + +ABC_NAMESPACE_HEADER_END + +#endif diff --git a/src/sat/cadical/stats.hpp b/src/sat/cadical/stats.hpp new file mode 100644 index 0000000000..4a6afa9c9f --- /dev/null +++ b/src/sat/cadical/stats.hpp @@ -0,0 +1,376 @@ +#ifndef _stats_hpp_INCLUDED +#define _stats_hpp_INCLUDED + +#include "global.h" + +#include <cstdint> +#include <cstdlib> +#include <vector> + +ABC_NAMESPACE_CXX_HEADER_START + +namespace CaDiCaL { + +struct Internal; + +struct Stats { + + Internal *internal; + + int64_t vars = 0; // internal initialized variables + + int64_t conflicts = 0; // generated conflicts in 'propagate' + int64_t decisions = 0; // number of decisions in 'decide' + + struct { + int64_t cover = 0; // propagated during covered clause elimination + int64_t instantiate = 0; // propagated during variable instantiation + int64_t probe = 0; // propagated during probing + int64_t search = 0; // propagated literals during search + int64_t transred = 0; // propagated during transitive reduction + int64_t vivify = 0; // propagated during vivification + int64_t walk = 0; // propagated during local search + } propagations; + + struct { + int64_t search[2] = {0}; + int64_t factor = 0; + int64_t probe = 0; + int64_t sweep = 0; + int64_t ternary = 0; + int64_t vivify = 0; + } ticks; + + struct { + int64_t ext_cb = 0; // number of times any external callback was called + int64_t eprop_call = 0; // number of times external_propagate was called + int64_t eprop_prop = 0; // number of times external propagate propagated + int64_t eprop_conf = + 0; // number of times ex-propagate was already falsified + int64_t eprop_expl = + 0; // number of times external propagate was explained + int64_t elearn_call = + 0; // number of times external clause learning was tried + int64_t elearned = + 0; // learned external clauses (incl. eprop explanations) + int64_t elearn_prop = + 0; // number of learned and propagating external clauses + int64_t elearn_conf = + 0; // number of learned and conflicting external clauses + int64_t echeck_call = 0; // number of checking found complete solutions + } ext_prop; + + int64_t condassinit = 0; // initial assigned literals + int64_t condassirem = 0; // initial assigned literals for blocked + int64_t condassrem = 0; // remaining assigned literals for blocked + int64_t condassvars = 0; // sum of active variables at initial assignment + int64_t condautinit = 0; // initial literals in autarky part + int64_t condautrem = 0; // remaining literals in autarky part for blocked + int64_t condcands = 0; // globally blocked candidate clauses + int64_t condcondinit = 0; // initial literals in conditional part + int64_t condcondrem = + 0; // remaining literals in conditional part for blocked + int64_t conditioned = 0; // globally blocked clauses eliminated + int64_t conditionings = 0; // globally blocked clause eliminations + int64_t condprops = 0; // propagated unassigned literals + + struct { + int64_t block = 0; // block marked literals + int64_t elim = 0; // elim marked variables + int64_t subsume = 0; // subsume marked variables + int64_t ternary = 0; // ternary marked variables + int64_t factor = 0; + } mark; + + struct { + int64_t total = 0; + int64_t redundant = 0; + int64_t irredundant = 0; + } current, added; // Clauses. + + struct { + double process = 0, real = 0; + } time; + + struct { + int64_t count = 0; // number of covered clause elimination rounds + int64_t asymmetric = 0; // number of asymmetric tautologies in CCE + int64_t blocked = 0; // number of blocked covered tautologies + int64_t total = 0; // total number of eliminated clauses + } cover; + + struct { + int64_t tried = 0; + int64_t succeeded = 0; + struct { + int64_t one = 0, zero = 0; + } constant, forward, backward; + struct { + int64_t positive = 0, negative = 0; + } horn; + } lucky; + + struct { + int64_t total = 0; // total number of happened rephases + int64_t best = 0; // how often reset to best phases + int64_t flipped = 0; // how often reset phases by flipping + int64_t inverted = 0; // how often reset to inverted phases + int64_t original = 0; // how often reset to original phases + int64_t random = 0; // how often randomly reset phases + int64_t walk = 0; // phases improved through random walked + } rephased; + + struct { + int64_t count = 0; + int64_t broken = 0; + int64_t flips = 0; + int64_t minimum = 0; + } walk; + + struct { + int64_t count = 0; // flushings of learned clauses counter + int64_t learned = 0; // flushed learned clauses + int64_t hyper = 0; // flushed hyper binary/ternary clauses + } flush; + + int64_t compacts = 0; // number of compactifications + int64_t shuffled = 0; // shuffled queues and scores + int64_t restarts = 0; // actual number of happened restarts + int64_t restartlevels = 0; // levels at restart + int64_t restartstable = 0; // actual number of happened restarts + int64_t stabphases = 0; // number of stabilization phases + int64_t stabconflicts = + 0; // number of search conflicts during stabilizing + int64_t rescored = 0; // number of times scores were rescored + int64_t reused = 0; // number of reused trails + int64_t reusedlevels = 0; // reused levels at restart + int64_t reusedstable = 0; // number of reused trails during stabilizing + int64_t sections = 0; // 'section' counter + int64_t chrono = 0; // chronological backtracks + int64_t backtracks = 0; // number of backtracks + int64_t improvedglue = 0; // improved glue during bumping + int64_t promoted1 = 0; // promoted clauses to tier one + int64_t promoted2 = 0; // promoted clauses to tier two + int64_t bumped = 0; // seen and bumped variables in 'analyze' + int64_t recomputed = 0; // recomputed glues 'recompute_glue' + int64_t searched = 0; // searched decisions in 'decide' + int64_t reductions = 0; // 'reduce' counter + int64_t reduced = 0; // number of reduced clauses + int64_t reduced_sqrt = 0; + int64_t reduced_prct = 0; + int64_t collected = 0; // number of collected bytes + int64_t collections = 0; // number of garbage collections + int64_t hbrs = 0; // hyper binary resolvents + int64_t hbrsizes = 0; // sum of hyper resolved base clauses + int64_t hbreds = 0; // redundant hyper binary resolvents + int64_t hbrsubs = 0; // subsuming hyper binary resolvents + int64_t instried = 0; // number of tried instantiations + int64_t instantiated = 0; // number of successful instantiations + int64_t instrounds = 0; // number of instantiation rounds + int64_t subsumed = 0; // number of subsumed clauses + int64_t deduplicated = 0; // number of removed duplicated binary clauses + int64_t deduplications = 0; // number of deduplication phases + int64_t strengthened = 0; // number of strengthened clauses + + int64_t eliminated_equi = + 0; // number of successful equivalence eliminations + int64_t eliminated_and = 0; // number of successful AND gate eliminations + int64_t eliminated_ite = 0; // number of successful ITE gate eliminations + int64_t eliminated_xor = 0; // number of successful XOR gate eliminations + int64_t eliminated_def = + 0; // number of successful definition eliminations + + int64_t definitions_checked = 0; + int64_t definitions_extracted = 0; + int64_t definition_units = 0; + int64_t definition_ticks = 0; + + int64_t factor = 0; + int64_t factored = 0; + int64_t factor_added = 0; + int64_t variables_extension = 0; + int64_t variables_original = 0; + int64_t literals_factored = 0; + int64_t clauses_unfactored = 0; + int64_t literals_unfactored = 0; + + int64_t elimotfstr = + 0; // number of on-the-fly strengthened during elimination + int64_t subirr = 0; // number of subsumed irredundant clauses + int64_t subred = 0; // number of subsumed redundant clauses + int64_t subtried = 0; // number of tried subsumptions + int64_t subchecks = 0; // number of pair-wise subsumption checks + int64_t subchecks2 = 0; // same but restricted to binary clauses + int64_t elimotfsub = + 0; // number of on-the-fly subsumed during elimination + int64_t subsumerounds = 0; // number of subsumption rounds + int64_t subsumephases = 0; // number of scheduled subsumption phases + int64_t eagertried = 0; // number of traversed eager subsumed candidates + int64_t eagersub = + 0; // number of eagerly subsumed recently learned clauses + int64_t elimres = 0; // number of resolved clauses in BVE + int64_t elimrestried = 0; // number of tried resolved clauses in BVE + int64_t elimfastrounds = 0; // number of elimination rounds + int64_t elimrounds = 0; // number of elimination rounds + int64_t elimphases = 0; // number of scheduled elimination phases + int64_t elimfastphases = 0; // number of scheduled elimination phases + int64_t elimcompleted = 0; // number complete elimination procedures + int64_t elimtried = 0; // number of variable elimination attempts + int64_t elimsubst = 0; // number of eliminations through substitutions + int64_t elimgates = 0; // number of gates found during elimination + int64_t elimequivs = 0; // number of equivalences found during elimination + int64_t elimands = 0; // number of AND gates found during elimination + int64_t elimites = 0; // number of ITE gates found during elimination + int64_t elimxors = 0; // number of XOR gates found during elimination + int64_t elimbwsub = 0; // number of eager backward subsumed clauses + int64_t elimbwstr = 0; // number of eager backward strengthened clauses + int64_t ternary = 0; // number of ternary resolution phases + int64_t ternres = 0; // number of ternary resolutions + int64_t htrs = 0; // number of hyper ternary resolvents + int64_t htrs2 = 0; // number of binary hyper ternary resolvents + int64_t htrs3 = 0; // number of ternary hyper ternary resolvents + int64_t decompositions = 0; // number of SCC + ELS + int64_t vivifications = 0; // number of vivifications + int64_t vivifychecks = 0; // checked clauses during vivification + int64_t vivifydecs = 0; // vivification decisions + int64_t vivifyreused = 0; // reused vivification decisions + int64_t vivifysched = 0; // scheduled clauses for vivification + int64_t vivifysubs = 0; // subsumed clauses during vivification + int64_t vivifysubred = 0; // subsumed clauses during vivification + int64_t vivifysubirr = 0; // subsumed clauses during vivification + int64_t vivifystrs = 0; // strengthened clauses during vivification + int64_t vivifystrirr = 0; // strengthened irredundant clause + int64_t vivifystred1 = 0; // strengthened redundant clause (1) + int64_t vivifystred2 = 0; // strengthened redundant clause (2) + int64_t vivifystred3 = 0; // strengthened redundant clause (3) + int64_t vivifyunits = 0; // units during vivification + int64_t vivifyimplied = 0; // implied during vivification + int64_t vivifyinst = 0; // instantiation during vivification + int64_t vivifydemote = 0; // demoting during vivification + int64_t transreds = 0; + int64_t transitive = 0; + struct { + int64_t literals = 0; + int64_t clauses = 0; + } learned; + int64_t minimized = 0; // minimized literals + int64_t shrunken = 0; // shrunken literals + int64_t minishrunken = 0; // shrunken during minimization literals + + int64_t irrlits = 0; // literals in irredundant clauses + struct { + int64_t bytes = 0; + int64_t clauses = 0; + int64_t literals = 0; + } garbage; + + int64_t sweep_units = 0; + int64_t sweep_flip_backbone = 0; + int64_t sweep_fixed_backbone = 0; + int64_t sweep_flipped_backbone = 0; + int64_t sweep_solved_backbone = 0; + int64_t sweep_sat_backbone = 0; + int64_t sweep_unsat_backbone = 0; + int64_t sweep_unknown_backbone = 0; + int64_t sweep_flip_equivalences = 0; + int64_t sweep_flipped_equivalences = 0; + int64_t sweep_sat_equivalences = 0; + int64_t sweep_unsat_equivalences = 0; + int64_t sweep_unknown_equivalences = 0; + int64_t sweep_solved_equivalences = 0; + int64_t sweep_equivalences = 0; + int64_t sweep_variables = 0; + int64_t sweep_completed = 0; + int64_t sweep_solved = 0; + int64_t sweep_sat = 0; + int64_t sweep_unsat = 0; + int64_t sweep_depth = 0; + int64_t sweep_environment = 0; + int64_t sweep_clauses = 0; + int64_t sweep = 0; + + int64_t units = 0; // learned unit clauses + int64_t binaries = 0; // learned binary clauses + int64_t inprobingphases = 0; // number of scheduled probing phases + int64_t probingrounds = 0; // number of probing rounds + int64_t inprobesuccess = 0; // number successful probing phases + int64_t probed = 0; // number of probed literals + int64_t failed = 0; // number of failed literals + int64_t hyperunary = 0; // hyper unary resolved unit clauses + int64_t probefailed = 0; // failed literals from probing + int64_t transredunits = 0; // units derived in transitive reduction + int64_t blockings = 0; // number of blocked clause eliminations + int64_t blocked = 0; // number of actually blocked clauses + int64_t blockres = 0; // number of resolutions during blocking + int64_t blockcands = 0; // number of clause / pivot pairs tried + int64_t blockpured = 0; // number of clauses blocked through pure literals + int64_t blockpurelits = 0; // number of pure literals + int64_t extensions = 0; // number of extended witnesses + int64_t extended = 0; // number of flipped literals during extension + int64_t weakened = 0; // number of clauses pushed to extension stack + int64_t weakenedlen = 0; // lengths of weakened clauses + int64_t restorations = 0; // number of restore calls + int64_t restored = 0; // number of restored clauses + int64_t reactivated = 0; // number of reactivated clauses + int64_t restoredlits = 0; // number of restored literals + + int64_t preprocessings = 0; + + int64_t ilbtriggers = 0; + int64_t ilbsuccess = 0; + int64_t levelsreused = 0; + int64_t literalsreused = 0; + int64_t assumptionsreused = 0; + int64_t tierecomputed = 0; // number of tier recomputation; + + struct { + int64_t fixed = 0; // number of top level assigned variables + int64_t eliminated = 0; // number of eliminated variables + int64_t fasteliminated = 0; // number of fast eliminated variables only + int64_t substituted = 0; // number of substituted variables + int64_t pure = 0; // number of pure literals + } all, now; + + struct { + int64_t strengthened = 0; // number of clauses strengthened during OTFS + int64_t subsumed = 0; // number of clauses subsumed by OTFS + } otfs; + + int64_t unused = 0; // number of unused variables + int64_t active = 0; // number of active variables + int64_t inactive = 0; // number of inactive variables + std::vector<uint64_t> bump_used = {0, 0}; + std::vector<std::vector<uint64_t>> used; // used clauses in focused mode + + struct { + int64_t gates = 0; + int64_t ands = 0; + int64_t ites = 0; + int64_t xors = 0; + int64_t units = 0; + int64_t congruent = 0; + int64_t rounds = 0; + int64_t unary_and = 0; + int64_t unaries = 0; + int64_t rewritten_ands = 0; + int64_t simplified = 0; + int64_t simplified_ands = 0; + int64_t simplified_xors = 0; + int64_t simplified_ites = 0; + int64_t subsumed = 0; + int64_t trivial_ite = 0; + int64_t unary_ites = 0; + } congruence; + + Stats (); + + void print (Internal *); +}; + +/*------------------------------------------------------------------------*/ + +} // namespace CaDiCaL + +ABC_NAMESPACE_CXX_HEADER_END + +#endif diff --git a/src/sat/cadical/sweep.hpp b/src/sat/cadical/sweep.hpp new file mode 100644 index 0000000000..ad093e070a --- /dev/null +++ b/src/sat/cadical/sweep.hpp @@ -0,0 +1,67 @@ +#ifndef _sweep_hpp_INCLUDED +#define _sweep_hpp_INCLUDED + +#include "global.h" + +ABC_NAMESPACE_CXX_HEADER_START + +namespace CaDiCaL { + +struct Internal; + +struct sweep_proof_clause { + unsigned sweep_id; // index for sweeper.clauses + int64_t cad_id; // cadical id + unsigned kit_id; // cadical_kitten id + bool learned; + vector<int> literals; + vector<unsigned> chain; // lrat +}; + +struct sweep_blocked_clause { + int blit; + int64_t id; + vector<int> literals; +}; + +struct sweep_binary { + int lit; + int other; + int64_t id; +}; + +struct Sweeper { + Sweeper (Internal *internal); + ~Sweeper (); + Internal *internal; + Random random; + vector<unsigned> depths; + int *reprs; + vector<int> next, prev; + int first, last, blit; + unsigned encoded; + unsigned save; + vector<int> vars; + vector<Clause *> clauses; + vector<sweep_blocked_clause> blocked_clauses; + bool flush_blocked_clauses; + vector<int> blockable; + vector<int> clause; + vector<int> propagate; + vector<int> backbone; + vector<int> partition; + vector<bool> prev_units; + vector<sweep_binary> binaries; + vector<sweep_proof_clause> core[2]; + uint64_t current_ticks; + struct { + uint64_t ticks; + unsigned clauses, depth, vars; + } limit; +}; + +} // namespace CaDiCaL + +ABC_NAMESPACE_CXX_HEADER_END + +#endif diff --git a/src/sat/cadical/terminal.hpp b/src/sat/cadical/terminal.hpp new file mode 100644 index 0000000000..a99346b7cd --- /dev/null +++ b/src/sat/cadical/terminal.hpp @@ -0,0 +1,104 @@ +#ifndef _terminal_hpp_INCLUDED +#define _terminal_hpp_INCLUDED + +#include "global.h" + +ABC_NAMESPACE_CXX_HEADER_START + +namespace CaDiCaL { + +class Terminal { + + FILE *file; // 'stdout' or 'stderr' + bool connected; // Connected to terminal. + bool use_colors; // Use colors. + bool reset_on_exit; // Reset on exit. + + void escape () { + CADICAL_assert (connected); + fputs ("\033[", file); + } + + void color (int color, bool bright) { + if (!use_colors) + return; + CADICAL_assert (connected); + escape (); + fputc (bright ? '1' : '0', file); + fprintf (file, ";%dm", color); + fflush (file); + } + + void code (const char *str) { + if (!use_colors) + return; + if (!connected) + return; + escape (); + fputs (str, file); + fflush (file); + } + +public: + Terminal (FILE *file); + ~Terminal (); + + void disable (); // Assume disconnected in any case. + void force_colors (); + void force_no_colors (); + void force_reset_on_exit (); + + bool colors () { return use_colors; } + + operator bool () const { return connected; } + + void red (bool bright = false) { color (31, bright); } + void green (bool bright = false) { color (32, bright); } + void yellow (bool bright = false) { color (33, bright); } + void blue (bool bright = false) { color (34, bright); } + void magenta (bool bright = false) { color (35, bright); } + void black (bool bright = false) { color (90, bright); } + void cyan (bool bright = false) { color (96, bright); } + + void bold () { code ("1m"); } + void normal () { code ("0m"); } + void inverse () { code ("7m"); } + void underline () { code ("4m"); } + +#define MODIFY(CODE) (use_colors ? "\033[" CODE "m" : "") + + const char *bright_magenta_code () { return MODIFY ("1;35"); } + const char *magenta_code () { return MODIFY ("0;35"); } + const char *blue_code () { return MODIFY ("0;34"); } + const char *bright_blue_code () { return MODIFY ("1;34"); } + const char *yellow_code () { return MODIFY ("0;33"); } + const char *bright_yellow_code () { return MODIFY ("1;33"); } + const char *green_code () { return MODIFY ("0;32"); } + const char *red_code () { return MODIFY ("0;31"); } + const char *cyan_code () { return MODIFY ("0;96"); } + const char *bright_red_code () { return MODIFY ("1;31"); } + const char *normal_code () { return MODIFY ("0"); } + const char *bold_code () { return MODIFY ("1"); } + + void cursor (bool on) { code (on ? "?25h" : "?25l"); } + + void erase_until_end_of_line () { code ("K"); } + + void erase_line_if_connected_otherwise_new_line () { + if (connected) + code ("1G"); + else + fputc ('\n', file), fflush (file); + } + + void reset (); +}; + +extern Terminal tout; // Terminal of 'stdout' (file descriptor '1') +extern Terminal terr; // Terminal of 'stderr' (file descriptor '2') + +} // namespace CaDiCaL + +ABC_NAMESPACE_CXX_HEADER_END + +#endif diff --git a/src/sat/cadical/testing.hpp b/src/sat/cadical/testing.hpp new file mode 100644 index 0000000000..f90569e5c5 --- /dev/null +++ b/src/sat/cadical/testing.hpp @@ -0,0 +1,30 @@ +#ifndef _testing_hpp_INCLUDED +#define _testing_hpp_INCLUDED + +#include "global.h" + +// This class provides access to 'internal' which should only be used for +// internal testing and not for any other purpose as 'internal' is supposed +// to be hidden. + +#include "cadical.hpp" + +ABC_NAMESPACE_CXX_HEADER_START + +namespace CaDiCaL { + +class Testing { + Solver *solver; + +public: + Testing (Solver &s) : solver (&s) {} + Testing (Solver *s) : solver (s) {} + Internal *internal () { return solver->internal; } + External *external () { return solver->external; } +}; + +} // namespace CaDiCaL + +ABC_NAMESPACE_CXX_HEADER_END + +#endif diff --git a/src/sat/cadical/tracer.hpp b/src/sat/cadical/tracer.hpp new file mode 100644 index 0000000000..d1a9f1137b --- /dev/null +++ b/src/sat/cadical/tracer.hpp @@ -0,0 +1,183 @@ +#ifndef _tracer_hpp_INCLUDED +#define _tracer_hpp_INCLUDED + +#include "global.h" + +#include <cstdint> +#include <vector> + +ABC_NAMESPACE_CXX_HEADER_START + +namespace CaDiCaL { + +struct Internal; + +enum ConclusionType { CONFLICT = 1, ASSUMPTIONS = 2, CONSTRAINT = 4 }; + +// Proof tracer class to observer all possible proof events, +// such as added or deleted clauses. +// An implementation can decide on which events to act. +// +class Tracer { + +public: + Tracer () {} + virtual ~Tracer () {} + + /*------------------------------------------------------------------------*/ + /* */ + /* Basic Events */ + /* */ + /*------------------------------------------------------------------------*/ + + // Notify the tracer that a original clause has been added. + // Includes ID and whether the clause is redundant or irredundant + // Arguments: ID, redundant, clause, restored + // + virtual void add_original_clause (int64_t, bool, const std::vector<int> &, + bool = false) {} + + // Notify the observer that a new clause has been derived. + // Includes ID and whether the clause is redundant or irredundant + // If antecedents are derived they will be included here. + // Arguments: ID, redundant, clause, antecedents + // + virtual void add_derived_clause (int64_t, bool, const std::vector<int> &, + const std::vector<int64_t> &) {} + + // Notify the observer that a clause is deleted. + // Includes ID and redundant/irredundant + // Arguments: ID, redundant, clause + // + virtual void delete_clause (int64_t, bool, const std::vector<int> &) {} + + // Notify the observer that a clause is deleted. + // Includes ID and redundant/irredundant + // Arguments: ID, redundant, clause + // + virtual void demote_clause (uint64_t, const std::vector<int> &) {} + + // Notify the observer to remember that the clause might be restored later + // Arguments: ID, clause + // + virtual void weaken_minus (int64_t, const std::vector<int> &) {} + + // Notify the observer that a clause is strengthened + // Arguments: ID + // + virtual void strengthen (int64_t) {} + + // Notify the observer that the solve call ends with status StatusType + // If the status is UNSAT and an empty clause has been derived, the second + // argument will contain its id. + // Note that the empty clause is already added through add_derived_clause + // and finalized with finalize_clause + // Arguments: int, ID + // + virtual void report_status (int, int64_t) {} + + /*------------------------------------------------------------------------*/ + /* */ + /* Specifically non-incremental */ + /* */ + /*------------------------------------------------------------------------*/ + + // Notify the observer that a clause is finalized. + // Arguments: ID, clause + // + virtual void finalize_clause (int64_t, const std::vector<int> &) {} + + // Notify the observer that the proof begins with a set of reserved ids + // for original clauses. Given ID is the first derived clause ID. + // Arguments: ID + // + virtual void begin_proof (int64_t) {} + + /*------------------------------------------------------------------------*/ + /* */ + /* Specifically incremental */ + /* */ + /*------------------------------------------------------------------------*/ + + // Notify the observer that an assumption has been added + // Arguments: assumption_literal + // + virtual void solve_query () {} + + // Notify the observer that an assumption has been added + // Arguments: assumption_literal + // + virtual void add_assumption (int) {} + + // Notify the observer that a constraint has been added + // Arguments: constraint_clause + // + virtual void add_constraint (const std::vector<int> &) {} + + // Notify the observer that assumptions and constraints are reset + // + virtual void reset_assumptions () {} + + // Notify the observer that this clause could be derived, which + // is the negation of a core of failing assumptions/constraints. + // If antecedents are derived they will be included here. + // Arguments: ID, clause, antecedents + // + virtual void add_assumption_clause (int64_t, const std::vector<int> &, + const std::vector<int64_t> &) {} + + // Notify the observer that conclude unsat was requested. + // will give either the id of the empty clause, the id of a failing + // assumption clause or the ids of the failing constrain clauses + // Arguments: conclusion_type, clause_ids + // + virtual void conclude_unsat (ConclusionType, + const std::vector<int64_t> &) {} + + // Notify the observer that conclude sat was requested. + // will give the complete model as a vector. + // + virtual void conclude_sat (const std::vector<int> &) {} + + // Notify the observer that conclude unknown was requested. + // will give the current trail as a vector. + // + virtual void conclude_unknown (const std::vector<int> &) {} +}; + +/*--------------------------------------------------------------------------*/ + +// Following tracers for internal use. + +struct InternalTracer : public Tracer { +public: + InternalTracer () {} + virtual ~InternalTracer () {} + + virtual void connect_internal (Internal *) {} +}; + +class StatTracer : public InternalTracer { +public: + StatTracer () {} + virtual ~StatTracer () {} + + virtual void print_stats () {} +}; + +class FileTracer : public InternalTracer { + +public: + FileTracer () {} + virtual ~FileTracer () {} + + virtual bool closed () = 0; + virtual void close (bool print = false) = 0; + virtual void flush (bool print = false) = 0; +}; + +} // namespace CaDiCaL + +ABC_NAMESPACE_CXX_HEADER_END + +#endif diff --git a/src/sat/cadical/util.hpp b/src/sat/cadical/util.hpp new file mode 100644 index 0000000000..3882c6ede8 --- /dev/null +++ b/src/sat/cadical/util.hpp @@ -0,0 +1,173 @@ +#ifndef _util_hpp_INCLUDED +#define _util_hpp_INCLUDED + +#include "global.h" + +#include <cassert> +#include <cstdint> +#include <vector> + +ABC_NAMESPACE_CXX_HEADER_START + +namespace CaDiCaL { + +using namespace std; + +// Common simple utility functions independent from 'Internal'. + +/*------------------------------------------------------------------------*/ + +inline double relative (double a, double b) { return b ? a / b : 0; } +inline double percent (double a, double b) { return relative (100 * a, b); } +inline int sign (int lit) { return (lit > 0) - (lit < 0); } +inline unsigned bign (int lit) { return 1 + (lit < 0); } + +/*------------------------------------------------------------------------*/ + +bool has_suffix (const char *str, const char *suffix); +bool has_prefix (const char *str, const char *prefix); + +/*------------------------------------------------------------------------*/ + +// Parse integer string in the form of +// +// true +// false +// [-]<mantissa>[e<exponent>] +// +// and in the latter case '<val>' has to be within [-INT_MAX,INT_MAX]. +// +// The function returns true if parsing is successful and then also sets +// the second argument to the parsed value. + +bool parse_int_str (const char *str, int &); + +/*------------------------------------------------------------------------*/ + +inline bool is_power_of_two (unsigned n) { return n && !(n & (n - 1)); } + +inline bool contained (int64_t c, int64_t l, int64_t u) { + return l <= c && c <= u; +} + +inline bool aligned (size_t n, size_t alignment) { + CADICAL_assert (is_power_of_two (alignment)); + const size_t mask = alignment - 1; + return !(n & mask); +} + +inline size_t align (size_t n, size_t alignment) { + size_t res = n; + CADICAL_assert (is_power_of_two (alignment)); + const size_t mask = alignment - 1; + if (res & mask) + res = (res | mask) + 1; + CADICAL_assert (aligned (res, alignment)); + return res; +} + +/*------------------------------------------------------------------------*/ + +inline bool parity (unsigned a) { + CADICAL_assert (sizeof a == 4); + unsigned tmp = a; + tmp ^= (tmp >> 16); + tmp ^= (tmp >> 8); + tmp ^= (tmp >> 4); + tmp ^= (tmp >> 2); + tmp ^= (tmp >> 1); + return tmp & 1; +} + +/*------------------------------------------------------------------------*/ + +// The standard 'Effective STL' way (though not guaranteed) to clear a +// vector and reduce its capacity to zero, thus deallocating all its +// internal memory. This is quite important for keeping the actual +// allocated size of watched and occurrence lists small particularly during +// bounded variable elimination where many clauses are added and removed. + +template <class T> void erase_vector (std::vector<T> &v) { + if (v.capacity ()) { + std::vector<T> ().swap (v); + } + CADICAL_assert (!v.capacity ()); // not guaranteed though +} + +// The standard 'Effective STL' way (though not guaranteed) to shrink the +// capacity of a vector to its size thus kind of releasing all the internal +// excess memory not needed at the moment any more. + +template <class T> void shrink_vector (std::vector<T> &v) { + if (v.capacity () > v.size ()) { + std::vector<T> (v).swap (v); + } + CADICAL_assert (v.capacity () == v.size ()); // not guaranteed though +} + +template <class T> +static void enlarge_init (vector<T> &v, size_t N, const T &i) { + if (v.size () < N) + v.resize (N, i); +} + +template <class T> static void enlarge_only (vector<T> &v, size_t N) { + if (v.size () < N) + v.resize (N, T ()); +} + +template <class T> static void enlarge_zero (vector<T> &v, size_t N) { + enlarge_init (v, N, (const T &) 0); +} + +// Clean-up class for bad_alloc error safety. + +template <typename T> struct DeferDeleteArray { + T *data; + DeferDeleteArray (T *t) : data (t) {} + ~DeferDeleteArray () { delete[] data; } + void release () { data = nullptr; } + void free () { + delete[] data; + data = nullptr; + } +}; + +template <typename T> struct DeferDeletePtr { + T *data; + DeferDeletePtr (T *t) : data (t) {} + ~DeferDeletePtr () { delete data; } + void release () { data = nullptr; } + void free () { + delete data; + data = nullptr; + } +}; + +/*------------------------------------------------------------------------*/ + +template <class T> inline void clear_n (T *base, size_t n) { + memset (base, 0, sizeof (T) * n); +} + +/*------------------------------------------------------------------------*/ + +// These are options both to 'cadical' and 'mobical'. After wasting some +// on not remembering the spelling (British vs American), nor singular vs +// plural and then wanted to use '--color=false', and '--colours=0' too, I +// just factored this out into these two utility functions. + +bool is_color_option (const char *arg); // --color, --colour, ... +bool is_no_color_option (const char *arg); // --no-color, --no-colour, ... + +/*------------------------------------------------------------------------*/ + +uint64_t hash_string (const char *str); + +/*------------------------------------------------------------------------*/ + +} // namespace CaDiCaL + +ABC_NAMESPACE_CXX_HEADER_END + +#endif diff --git a/src/sat/cadical/var.hpp b/src/sat/cadical/var.hpp new file mode 100644 index 0000000000..8793afbc4c --- /dev/null +++ b/src/sat/cadical/var.hpp @@ -0,0 +1,28 @@ +#ifndef _var_hpp_INCLUDED +#define _var_hpp_INCLUDED + +#include "global.h" + +ABC_NAMESPACE_CXX_HEADER_START + +namespace CaDiCaL { + +struct Clause; + +// This structure captures data associated with an assigned variable. + +struct Var { + + // Note that none of these members is valid unless the variable is + // assigned. During unassigning a variable we do not reset it. + + int level; // decision level + int trail; // trail height at assignment + Clause *reason; // implication graph edge during search +}; + +} // namespace CaDiCaL + +ABC_NAMESPACE_CXX_HEADER_END + +#endif diff --git a/src/sat/cadical/veripbtracer.hpp b/src/sat/cadical/veripbtracer.hpp new file mode 100644 index 0000000000..b9c47362fe --- /dev/null +++ b/src/sat/cadical/veripbtracer.hpp @@ -0,0 +1,108 @@ +#ifndef _veripbtracer_h_INCLUDED +#define _veripbtracer_h_INCLUDED + +#include "global.h" + +ABC_NAMESPACE_CXX_HEADER_START + +class FileTracer; + +namespace CaDiCaL { + +struct HashId { + HashId *next; // collision chain link for hash table + uint64_t hash; // previously computed full 64-bit hash + int64_t id; // id of clause +}; + +class VeripbTracer : public FileTracer { + + Internal *internal; + File *file; +#ifndef CADICAL_NDEBUG + bool binary; +#endif + bool with_antecedents; + bool checked_deletions; + + // hash table for checked deletions + // + uint64_t num_clauses; // number of clauses in hash table + uint64_t size_clauses; // size of clause hash table + HashId **clauses; // hash table of clauses + + static const unsigned num_nonces = 4; + + uint64_t nonces[num_nonces]; // random numbers for hashing + uint64_t last_hash; // last computed hash value of clause + int64_t last_id; // id of the last added clause + HashId *last_clause; + uint64_t compute_hash (int64_t); // compute and save hash value of clause + + HashId *new_clause (); + void delete_clause (HashId *); + + // Reduce hash value to the actual size. + // + static uint64_t reduce_hash (uint64_t hash, uint64_t size); + + void enlarge_clauses (); // enlarge hash table for clauses + void insert (); // insert clause in hash table + bool + find_and_delete (const int64_t); // find clause position in hash table + +#ifndef CADICAL_QUIET + int64_t added, deleted; +#endif + vector<int64_t> delete_ids; + + void put_binary_zero (); + void put_binary_lit (int external_lit); + void put_binary_id (int64_t id, bool = false); + + // support veriPB + void veripb_add_derived_clause (int64_t, bool redundant, + const vector<int> &clause, + const vector<int64_t> &chain); + void veripb_add_derived_clause (int64_t, bool redundant, + const vector<int> &clause); + void veripb_begin_proof (int64_t reserved_ids); + void veripb_delete_clause (int64_t id, bool redundant); + void veripb_report_status (bool unsat, int64_t conflict_id); + void veripb_strengthen (int64_t); + +public: + // own and delete 'file' + VeripbTracer (Internal *, File *file, bool, bool, bool); + ~VeripbTracer (); + + void connect_internal (Internal *i) override; + void begin_proof (int64_t) override; + + void add_original_clause (int64_t, bool, const vector<int> &, + bool = false) override {} // skip + + void add_derived_clause (int64_t, bool, const vector<int> &, + const vector<int64_t> &) override; + + void delete_clause (int64_t, bool, const vector<int> &) override; + void finalize_clause (int64_t, const vector<int> &) override {} // skip + + void report_status (int, int64_t) override; + + void weaken_minus (int64_t, const vector<int> &) override; + void strengthen (int64_t) override; + +#ifndef CADICAL_QUIET + void print_statistics (); +#endif + bool closed () override; + void close (bool) override; + void flush (bool) override; +}; + +} // namespace CaDiCaL + +ABC_NAMESPACE_CXX_HEADER_END + +#endif diff --git a/src/sat/cadical/version.hpp b/src/sat/cadical/version.hpp new file mode 100644 index 0000000000..d63aefa35d --- /dev/null +++ b/src/sat/cadical/version.hpp @@ -0,0 +1,19 @@ +#include "global.h" + +ABC_NAMESPACE_CXX_HEADER_START + +namespace CaDiCaL { + +const char *version (); +const char *copyright (); +const char *authors (); +const char *affiliations (); +const char *signature (); +const char *identifier (); +const char *compiler (); +const char *date (); +const char *flags (); + +} // namespace CaDiCaL + +ABC_NAMESPACE_CXX_HEADER_END diff --git a/src/sat/cadical/vivify.hpp b/src/sat/cadical/vivify.hpp new file mode 100644 index 0000000000..a5c6c0e109 --- /dev/null +++ b/src/sat/cadical/vivify.hpp @@ -0,0 +1,68 @@ +#ifndef _vivify_hpp_INCLUDED +#define _vivify_hpp_INCLUDED + +#include "global.h" + +#include "util.hpp" + +#include <array> +#include <cstdint> +#include <vector> + +ABC_NAMESPACE_CXX_HEADER_START + +namespace CaDiCaL { + +struct Clause; + +enum class Vivify_Mode { TIER1, TIER2, TIER3, IRREDUNDANT }; + +#define COUNTREF_COUNTS 2 + +struct vivify_ref { + bool vivify; + std::size_t size; + uint64_t count[COUNTREF_COUNTS]; + Clause *clause; +}; + +// In the vivifier structure, we put the schedules in an array in order to +// be able to iterate over them, but we provide the reference to them to +// make sure that you do need to remember the order. +struct Vivifier { + std::array<std::vector<vivify_ref>, 4> refs_schedules; + std::vector<vivify_ref> &refs_schedule_tier1, &refs_schedule_tier2, + &refs_schedule_tier3, &refs_schedule_irred; + std::array<std::vector<Clause *>, 4> schedules; + std::vector<Clause *> &schedule_tier1, &schedule_tier2, &schedule_tier3, + &schedule_irred; + std::vector<int> sorted; + Vivify_Mode tier; + char tag; + int tier1_limit; + int tier2_limit; + int64_t ticks; + std::vector<std::tuple<int, Clause *, bool>> lrat_stack; + Vivifier (Vivify_Mode mode_tier) + : refs_schedule_tier1 (refs_schedules[0]), + refs_schedule_tier2 (refs_schedules[1]), + refs_schedule_tier3 (refs_schedules[2]), + refs_schedule_irred (refs_schedules[3]), + schedule_tier1 (schedules[0]), schedule_tier2 (schedules[1]), + schedule_tier3 (schedules[2]), schedule_irred (schedules[3]), + tier (mode_tier) {} + + void erase () { + erase_vector (refs_schedule_tier1); + erase_vector (refs_schedule_tier2); + erase_vector (refs_schedule_tier3); + erase_vector (refs_schedule_irred); + erase_vector (sorted); + } +}; + +} // namespace CaDiCaL + +ABC_NAMESPACE_CXX_HEADER_END + +#endif diff --git a/src/sat/cadical/watch.hpp b/src/sat/cadical/watch.hpp new file mode 100644 index 0000000000..1acab094b7 --- /dev/null +++ b/src/sat/cadical/watch.hpp @@ -0,0 +1,78 @@ +#ifndef _watch_hpp_INCLUDED +#define _watch_hpp_INCLUDED + +#include "global.h" + +#include <cassert> +#include <vector> + +ABC_NAMESPACE_CXX_HEADER_START + +namespace CaDiCaL { + +// Watch lists for CDCL search. The blocking literal (see also comments +// related to 'propagate') is a must and thus combining that with a 64 bit +// pointer will give a 16 byte (8 byte aligned) structure anyhow, which +// means the additional 4 bytes for the size come for free. As alternative +// one could use a 32-bit reference instead of the pointer which would +// however limit the number of clauses to '2^32 - 1'. One would also need +// to use at least one more bit (either taken away from the variable space +// or the clauses) to denote whether the watch is binary. + +// in fashion of Intel Sat 10.4230/LIPIcs.SAT.2022.8 we try to +// guarantee the following invariant: +// For both watches: +// if the watched literal is negatively assigned +// either it will be propagated in the future +// or the corresponding blocking literal is positively assigned +// and its level is smaller than the level of the watched literal +// + +struct Clause; + +struct Watch { + + Clause *clause; + int blit; + int size; + + Watch (int b, Clause *c) : clause (c), blit (b), size (c->size) {} + Watch () {} + + bool binary () const { return size == 2; } +}; + +typedef vector<Watch> Watches; // of one literal + +typedef Watches::iterator watch_iterator; +typedef Watches::const_iterator const_watch_iterator; + +inline void remove_watch (Watches &ws, Clause *clause) { + const auto end = ws.end (); + auto i = ws.begin (); + for (auto j = i; j != end; j++) { + const Watch &w = *i++ = *j; + if (w.clause == clause) + i--; + } + CADICAL_assert (i + 1 == end); + ws.resize (i - ws.begin ()); +} + +// search for the clause and updates the size marked in the watch lists +inline void update_watch_size (Watches &ws, int blit, Clause *conflict) { + bool found = false; + const int size = conflict->size; + for (Watch &w : ws) { + if (w.clause == conflict) + w.size = size, w.blit = blit, found = true; + CADICAL_assert (w.clause->garbage || w.size == 2 || w.clause->size != 2); + } + CADICAL_assert (found), (void) found; +} + +} // namespace CaDiCaL + +ABC_NAMESPACE_CXX_HEADER_END + +#endif From 20416c1a7b25dfae3375e4a96cc2b8059a4fe89f Mon Sep 17 00:00:00 2001 From: Miodrag Milanovic <mmicko@gmail.com> Date: Tue, 8 Apr 2025 14:23:55 +0200 Subject: [PATCH 67/79] mingw fixes --- src/sat/cadical/cadical_congruence.cpp | 2 +- src/sat/cadical/cadical_vivify.cpp | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/sat/cadical/cadical_congruence.cpp b/src/sat/cadical/cadical_congruence.cpp index 8fbceee24e..dd5f30b7a9 100644 --- a/src/sat/cadical/cadical_congruence.cpp +++ b/src/sat/cadical/cadical_congruence.cpp @@ -5981,7 +5981,7 @@ void Closure::rewrite_ite_gate (Gate *g, int dst, int src) { #endif } else { CADICAL_assert (false); -#ifdef WIN32 +#if defined(WIN32) && !defined(__MINGW32__) __assume(false); #else __builtin_unreachable (); diff --git a/src/sat/cadical/cadical_vivify.cpp b/src/sat/cadical/cadical_vivify.cpp index 8d99c29323..5ec6401cff 100644 --- a/src/sat/cadical/cadical_vivify.cpp +++ b/src/sat/cadical/cadical_vivify.cpp @@ -1501,7 +1501,7 @@ inline std::vector<vivify_ref> ¤t_refs_schedule (Vivifier &vivifier) { return vivifier.refs_schedule_irred; break; } -#ifdef WIN32 +#if defined(WIN32) && !defined(__MINGW32__) __assume(false); #else __builtin_unreachable (); @@ -1523,7 +1523,7 @@ inline std::vector<Clause *> ¤t_schedule (Vivifier &vivifier) { return vivifier.schedule_irred; break; } -#ifdef WIN32 +#if defined(WIN32) && !defined(__MINGW32__) __assume(false); #else __builtin_unreachable (); From e55d316cc9a7f72a84a76eda555aa6ec083c9d0d Mon Sep 17 00:00:00 2001 From: Miodrag Milanovic <mmicko@gmail.com> Date: Tue, 8 Apr 2025 17:38:54 +0200 Subject: [PATCH 68/79] WASI build fix --- src/opt/eslim/synthesisEngine.tpp | 4 ++++ src/sat/cadical/cadical_file.cpp | 14 +++++++++++--- src/sat/cadical/cadical_random.cpp | 4 ++-- src/sat/cadical/cadical_resources.cpp | 4 ++++ src/sat/cadical/cadical_signal.cpp | 20 ++++++++++++++++---- src/sat/cadical/file.hpp | 2 +- src/sat/cadical/internal.hpp | 2 ++ 7 files changed, 40 insertions(+), 10 deletions(-) diff --git a/src/opt/eslim/synthesisEngine.tpp b/src/opt/eslim/synthesisEngine.tpp index 6a8485f7fb..ef94d92268 100644 --- a/src/opt/eslim/synthesisEngine.tpp +++ b/src/opt/eslim/synthesisEngine.tpp @@ -614,7 +614,11 @@ namespace eSLIM { // sprintf( pCommand, "%s -q %s > %s", pKissat, pFileNameIn, pFileNameOut ); sprintf( pCommand, "%s -q %s > %s", pKissat, pFileNameIn, pFileNameOut ); +#ifdef __wasm + if ( 1 ) { +#else if ( system( pCommand ) == -1 ) { +#endif std::cerr << "Command " << pCommand << " failed\n"; return nullptr; } diff --git a/src/sat/cadical/cadical_file.cpp b/src/sat/cadical/cadical_file.cpp index 98f19f752d..d48193e93c 100644 --- a/src/sat/cadical/cadical_file.cpp +++ b/src/sat/cadical/cadical_file.cpp @@ -33,7 +33,9 @@ extern "C" { #else extern "C" { +#if !defined(__wasm) #include <sys/wait.h> +#endif #include <unistd.h> } @@ -245,6 +247,7 @@ void File::delete_str_vector (std::vector<char *> &argv) { FILE *File::open_pipe (Internal *internal, const char *fmt, const char *path, const char *mode) { +#if !defined(__wasm) #ifdef CADICAL_QUIET (void) internal; #endif @@ -269,6 +272,9 @@ FILE *File::open_pipe (Internal *internal, const char *fmt, FILE *res = popen (cmd, mode); delete[] cmd; return res; +#else + return 0; +#endif } FILE *File::read_pipe (Internal *internal, const char *fmt, const int *sig, @@ -285,7 +291,7 @@ FILE *File::read_pipe (Internal *internal, const char *fmt, const int *sig, return open_pipe (internal, fmt, path, "r"); } -#ifndef _WIN32 +#if !defined(_WIN32) && !defined(__wasm) #if defined(__APPLE__) || defined(__MACH__) static std::mutex compressed_file_writing_mutex; @@ -420,7 +426,7 @@ File *File::read (Internal *internal, const char *path) { File *File::write (Internal *internal, const char *path) { FILE *file; int close_output = 3, child_pid = 0; -#ifndef _WIN32 +#if !defined(_WIN32) && !defined(__wasm) if (has_suffix (path, ".xz")) file = write_pipe (internal, "xz -c", path, child_pid); else if (has_suffix (path, ".bz2")) @@ -456,12 +462,14 @@ void File::close (bool print) { MSG ("closing file '%s'", name ()); fclose (file); } +#if !defined(__wasm) if (close_file == 2) { if (print) MSG ("closing input pipe to read '%s'", name ()); pclose (file); } -#ifndef _WIN32 +#endif +#if !defined(_WIN32) && !defined(__wasm) if (close_file == 3) { if (print) MSG ("closing output pipe to write '%s'", name ()); diff --git a/src/sat/cadical/cadical_random.cpp b/src/sat/cadical/cadical_random.cpp index 85d022e8f9..044f6aba05 100644 --- a/src/sat/cadical/cadical_random.cpp +++ b/src/sat/cadical/cadical_random.cpp @@ -84,7 +84,7 @@ ABC_NAMESPACE_IMPL_END // work. As an additional measure to increase the possibility to get // different seeds we are now also using network addresses (explicitly). -#ifndef WIN32 +#if !defined(_WIN32) && !defined(__wasm) extern "C" { #include <ifaddrs.h> @@ -110,7 +110,7 @@ static uint64_t hash_network_addresses () { // you really need to run 'mobical' on a Windows cluster where each node // has identical IP addresses. -#ifndef WIN32 +#if !defined(_WIN32) && !defined(__wasm) struct ifaddrs *addrs; if (!getifaddrs (&addrs)) { for (struct ifaddrs *addr = addrs; addr; addr = addr->ifa_next) { diff --git a/src/sat/cadical/cadical_resources.cpp b/src/sat/cadical/cadical_resources.cpp index 9a2b421e5b..912a40568c 100644 --- a/src/sat/cadical/cadical_resources.cpp +++ b/src/sat/cadical/cadical_resources.cpp @@ -131,10 +131,14 @@ uint64_t maximum_resident_set_size () { // This seems to work on Linux (man page says since Linux 2.6.32). uint64_t maximum_resident_set_size () { +#ifdef __wasm + return 0; +#else struct rusage u; if (getrusage (RUSAGE_SELF, &u)) return 0; return ((uint64_t) u.ru_maxrss) << 10; +#endif } // Unfortunately 'getrusage' on Linux does not support current resident set diff --git a/src/sat/cadical/cadical_signal.cpp b/src/sat/cadical/cadical_signal.cpp index 099277f035..7e8fc965b5 100644 --- a/src/sat/cadical/cadical_signal.cpp +++ b/src/sat/cadical/cadical_signal.cpp @@ -7,11 +7,13 @@ /*------------------------------------------------------------------------*/ #include <cassert> +#if !defined(__wasm) #include <csignal> +#endif /*------------------------------------------------------------------------*/ -#ifndef WIN32 +#if !defined(_WIN32) && !defined(__wasm) extern "C" { #include <unistd.h> } @@ -28,7 +30,7 @@ namespace CaDiCaL { static volatile bool caught_signal = false; static Handler *signal_handler; -#ifndef WIN32 +#if !defined(_WIN32) && !defined(__wasm) static volatile bool caught_alarm = false; static volatile bool alarm_set = false; @@ -38,6 +40,7 @@ void Handler::catch_alarm () { catch_signal (SIGALRM); } #endif +#if !defined(__wasm) #define SIGNALS \ SIGNAL (SIGABRT) \ SIGNAL (SIGINT) \ @@ -47,8 +50,9 @@ void Handler::catch_alarm () { catch_signal (SIGALRM); } #define SIGNAL(SIG) static void (*SIG##_handler) (int); SIGNALS #undef SIGNAL +#endif -#ifndef WIN32 +#if !defined(_WIN32) && !defined(__wasm) static void (*SIGALRM_handler) (int); @@ -66,6 +70,7 @@ void Signal::reset_alarm () { void Signal::reset () { signal_handler = 0; +#if !defined(__wasm) #define SIGNAL(SIG) \ (void) signal (SIG, SIG##_handler); \ SIG##_handler = 0; @@ -73,11 +78,13 @@ void Signal::reset () { #undef SIGNAL #ifndef WIN32 reset_alarm (); +#endif #endif caught_signal = false; } const char *Signal::name (int sig) { +#if !defined(__wasm) #define SIGNAL(SIG) \ if (sig == SIG) \ return #SIG; @@ -86,6 +93,7 @@ const char *Signal::name (int sig) { #ifndef WIN32 if (sig == SIGALRM) return "SIGALRM"; +#endif #endif return "UNKNOWN"; } @@ -97,6 +105,7 @@ const char *Signal::name (int sig) { // exclusive access to. All these solutions are painful and not elegant. static void catch_signal (int sig) { +#if !defined(__wasm) #ifndef WIN32 if (sig == SIGALRM && absolute_real_time () >= alarm_time) { if (!caught_alarm) { @@ -116,16 +125,19 @@ static void catch_signal (int sig) { Signal::reset (); ::raise (sig); } +#endif } void Signal::set (Handler *h) { +#if !defined(__wasm) signal_handler = h; #define SIGNAL(SIG) SIG##_handler = signal (SIG, catch_signal); SIGNALS #undef SIGNAL +#endif } -#ifndef WIN32 +#if !defined(_WIN32) && !defined(__wasm) void Signal::alarm (int seconds) { CADICAL_assert (seconds >= 0); diff --git a/src/sat/cadical/file.hpp b/src/sat/cadical/file.hpp index 860e9613a7..a8baf82b99 100644 --- a/src/sat/cadical/file.hpp +++ b/src/sat/cadical/file.hpp @@ -66,7 +66,7 @@ class File { const char *mode); static FILE *read_pipe (Internal *, const char *fmt, const int *sig, const char *path); -#ifndef WIN32 +#if !defined(_WIN32) && !defined(__wasm) static FILE *write_pipe (Internal *, const char *fmt, const char *path, int &child_pid); #endif diff --git a/src/sat/cadical/internal.hpp b/src/sat/cadical/internal.hpp index d86479a522..53c698c586 100644 --- a/src/sat/cadical/internal.hpp +++ b/src/sat/cadical/internal.hpp @@ -17,7 +17,9 @@ #include <cctype> #include <climits> #include <cmath> +#if !defined(__wasm) #include <csignal> +#endif #include <cstdio> #include <cstdlib> #include <cstring> From fa7fa163dacdf81bdcb72b2d2713fbe9984b62bb Mon Sep 17 00:00:00 2001 From: Miodrag Milanovic <mmicko@gmail.com> Date: Tue, 5 Aug 2025 12:24:05 +0200 Subject: [PATCH 69/79] Disable this code for now --- src/base/io/ioReadBlifMv.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/base/io/ioReadBlifMv.c b/src/base/io/ioReadBlifMv.c index 51ad65bec3..3059d851b0 100644 --- a/src/base/io/ioReadBlifMv.c +++ b/src/base/io/ioReadBlifMv.c @@ -746,7 +746,7 @@ static void Io_MvReadPreparse( Io_MvMan_t * p ) { char * pCur, * pPrev; int i, fComment = 0; - Io_MvReplaceBuffersByShorts( p->pBuffer ); + //Io_MvReplaceBuffersByShorts( p->pBuffer ); // parse the buffer into lines and remove comments Vec_PtrPush( p->vLines, p->pBuffer ); From 1d7c05988fe90d438457000a35551fe98c4fbf77 Mon Sep 17 00:00:00 2001 From: Ethan Mahintorabi <ethanmoon@google.com> Date: Wed, 13 Sep 2023 18:24:01 +0000 Subject: [PATCH 70/79] Adds unit testing framework to ABC Signed-off-by: Ethan Mahintorabi <ethanmoon@google.com> --- .github/workflows/build-posix-cmake.yml | 4 ++ CMakeLists.txt | 14 +++++++ test/CMakeLists.txt | 1 + test/gia/gia_test.cc | 54 +++++++++++++++++++++++++ 4 files changed, 73 insertions(+) create mode 100644 test/CMakeLists.txt create mode 100644 test/gia/gia_test.cc diff --git a/.github/workflows/build-posix-cmake.yml b/.github/workflows/build-posix-cmake.yml index f8ccde47df..50f9ef902f 100644 --- a/.github/workflows/build-posix-cmake.yml +++ b/.github/workflows/build-posix-cmake.yml @@ -44,6 +44,10 @@ jobs: run: | cmake --build build + - name: Run Unit Tests + run: | + ctest --output-on-failure + - name: Test Executable run: | ./build/abc -c "r i10.aig; b; ps; b; rw -l; rw -lz; b; rw -lz; b; ps; cec" diff --git a/CMakeLists.txt b/CMakeLists.txt index db9d724f2a..bbb153e642 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -114,3 +114,17 @@ add_library(libabc-pic EXCLUDE_FROM_ALL ${ABC_SRC}) abc_properties(libabc-pic PUBLIC) set_property(TARGET libabc-pic PROPERTY POSITION_INDEPENDENT_CODE ON) set_property(TARGET libabc-pic PROPERTY OUTPUT_NAME abc-pic) + +if(NOT DEFINED ABC_SKIP_TESTS) + enable_testing() + include(FetchContent) + FetchContent_Declare( + googletest + DOWNLOAD_EXTRACT_TIMESTAMP TRUE + # Specify the commit you depend on and update it regularly. + URL "https://github.com/google/googletest/archive/refs/tags/v1.14.0.zip" + ) + FetchContent_MakeAvailable(googletest) + include(GoogleTest) + add_subdirectory(test) +endif() \ No newline at end of file diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt new file mode 100644 index 0000000000..5a187fcac7 --- /dev/null +++ b/test/CMakeLists.txt @@ -0,0 +1 @@ +add_subdirectory(gia) \ No newline at end of file diff --git a/test/gia/gia_test.cc b/test/gia/gia_test.cc new file mode 100644 index 0000000000..a6b288f791 --- /dev/null +++ b/test/gia/gia_test.cc @@ -0,0 +1,54 @@ +#include "gtest/gtest.h" + +#include "aig/gia/gia.h" + +ABC_NAMESPACE_IMPL_START + +TEST(GiaTest, CanAllocateGiaManager) { + Gia_Man_t* aig_manager = Gia_ManStart(100); + + EXPECT_TRUE(aig_manager != nullptr); + Gia_ManStop(aig_manager); +} + +TEST(GiaTest, CanAddACi) { + Gia_Man_t* aig_manager = Gia_ManStart(100); + Gia_ManAppendCi(aig_manager); + + EXPECT_EQ(Gia_ManCiNum(aig_manager), 1); + Gia_ManStop(aig_manager); +} + +TEST(GiaTest, CanAddACo) { + Gia_Man_t* aig_manager = Gia_ManStart(100); + int input1 = Gia_ManAppendCi(aig_manager); + Gia_ManAppendCo(aig_manager, input1); + + EXPECT_EQ(Gia_ManCiNum(aig_manager), 1); + EXPECT_EQ(Gia_ManCoNum(aig_manager), 1); + Gia_ManStop(aig_manager); +} + +TEST(GiaTest, CanAddAnAndGate) { + Gia_Man_t* aig_manager = Gia_ManStart(100); + + int input1 = Gia_ManAppendCi(aig_manager); + int input2 = Gia_ManAppendCi(aig_manager); + + int and_output = Gia_ManAppendAnd(aig_manager, input1, input2); + Gia_ManAppendCo(aig_manager, and_output); + + Vec_Wrd_t* stimulus = Vec_WrdAlloc(2); + Vec_WrdPush(stimulus, /*A*/1); + Vec_WrdPush(stimulus, /*B*/1); + Vec_Wrd_t* output = Gia_ManSimPatSimOut(aig_manager, stimulus, /*fouts*/1); + + EXPECT_EQ(Gia_ManCiNum(aig_manager), 2); + EXPECT_EQ(Gia_ManCoNum(aig_manager), 1); + // A = 1, B = 1 -> A & B == 1 + EXPECT_EQ(Vec_WrdGetEntry(output, 0), 1); + Vec_WrdFree(output); + Gia_ManStop(aig_manager); +} + +ABC_NAMESPACE_IMPL_END From 1c5ed1ce378cc04beac30bb31abc4c37c8467042 Mon Sep 17 00:00:00 2001 From: Miodrag Milanovic <mmicko@gmail.com> Date: Thu, 6 Nov 2025 09:53:28 +0100 Subject: [PATCH 71/79] Revert "Extending support of CI/CO timing info." This reverts commit aac61902081c12d91969197defce2c1bdc21f212. --- src/aig/gia/giaAiger.c | 6 ------ src/misc/tim/timMan.c | 28 ++++++++-------------------- 2 files changed, 8 insertions(+), 26 deletions(-) diff --git a/src/aig/gia/giaAiger.c b/src/aig/gia/giaAiger.c index 3c44a566db..0b5e5a110f 100644 --- a/src/aig/gia/giaAiger.c +++ b/src/aig/gia/giaAiger.c @@ -652,9 +652,6 @@ Gia_Man_t * Gia_AigerReadFromMemory( char * pContents, int nFileSize, int fGiaSi pNew->vInArrs = Vec_FltStart( nInputs ); memcpy( Vec_FltArray(pNew->vInArrs), pCur, (size_t)4*nInputs ); pCur += 4*nInputs; if ( fVerbose ) printf( "Finished reading extension \"i\".\n" ); - if ( Vec_FltSize(pNew->vInArrs) == Gia_ManPiNum(pNew) ) - Vec_FltFillExtra(pNew->vInArrs, Gia_ManCiNum(pNew), 0); - assert( Vec_FltSize(pNew->vInArrs) == Gia_ManCiNum(pNew) ); } else if ( *pCur == 'o' ) { @@ -663,9 +660,6 @@ Gia_Man_t * Gia_AigerReadFromMemory( char * pContents, int nFileSize, int fGiaSi pNew->vOutReqs = Vec_FltStart( nOutputs ); memcpy( Vec_FltArray(pNew->vOutReqs), pCur, (size_t)4*nOutputs ); pCur += 4*nOutputs; if ( fVerbose ) printf( "Finished reading extension \"o\".\n" ); - if ( Vec_FltSize(pNew->vOutReqs) == Gia_ManPoNum(pNew) ) - Vec_FltFillExtra(pNew->vOutReqs, Gia_ManCoNum(pNew), 0); - assert( Vec_FltSize(pNew->vOutReqs) == Gia_ManCoNum(pNew) ); } // read equivalence classes else if ( *pCur == 'e' ) diff --git a/src/misc/tim/timMan.c b/src/misc/tim/timMan.c index 9bcdaaae68..7e112669e1 100644 --- a/src/misc/tim/timMan.c +++ b/src/misc/tim/timMan.c @@ -451,31 +451,19 @@ void Tim_ManCreate( Tim_Man_t * p, void * pLib, Vec_Flt_t * vInArrs, Vec_Flt_t * // create arrival times if ( vInArrs ) { - if ( Vec_FltSize(vInArrs) == Tim_ManPiNum(p) ) { - Tim_ManForEachPi( p, pObj, i ) - pObj->timeArr = Vec_FltEntry(vInArrs, i); - } - else if ( Vec_FltSize(vInArrs) == Tim_ManCiNum(p) ) { - Tim_ManForEachCi( p, pObj, i ) - pObj->timeArr = Vec_FltEntry(vInArrs, i); - } - else assert( 0 ); + assert( Vec_FltSize(vInArrs) == Tim_ManPiNum(p) ); + Tim_ManForEachPi( p, pObj, i ) + pObj->timeArr = Vec_FltEntry(vInArrs, i); + } // create required times if ( vOutReqs ) { k = 0; - if ( Vec_FltSize(vOutReqs) == Tim_ManPoNum(p) ) { - Tim_ManForEachPo( p, pObj, i ) - pObj->timeReq = Vec_FltEntry(vOutReqs, k++); - assert( k == Tim_ManPoNum(p) ); - } - else if ( Vec_FltSize(vOutReqs) == Tim_ManCoNum(p) ) { - Tim_ManForEachCo( p, pObj, i ) - pObj->timeReq = Vec_FltEntry(vOutReqs, k++); - assert( k == Tim_ManCoNum(p) ); - } - else assert( 0 ); + assert( Vec_FltSize(vOutReqs) == Tim_ManPoNum(p) ); + Tim_ManForEachPo( p, pObj, i ) + pObj->timeReq = Vec_FltEntry(vOutReqs, k++); + assert( k == Tim_ManPoNum(p) ); } } From ef1a3d0f42d324445c2284c5772072ce0d49b54b Mon Sep 17 00:00:00 2001 From: Miodrag Milanovic <mmicko@gmail.com> Date: Fri, 5 Dec 2025 14:00:39 +0100 Subject: [PATCH 72/79] Revert "Merge pull request #30 from povik/&mfs-fixes" This reverts commit ccc02c44004e4cc85114bc8b57a7b601130eae98, reversing changes made to 0cd90d0d2c5338277d832a1d890bed286486bcf5. --- src/aig/gia/giaMfs.c | 51 +++++++++++--------------------------- src/base/abci/abc.c | 6 +---- src/base/abci/abcMfs.c | 4 +-- src/opt/sfm/sfm.h | 3 +-- src/opt/sfm/sfmCore.c | 13 +++++----- src/opt/sfm/sfmInt.h | 2 -- src/opt/sfm/sfmNtk.c | 6 ++--- src/opt/sfm/sfmWin.c | 56 ++++++++++++++---------------------------- 8 files changed, 45 insertions(+), 96 deletions(-) diff --git a/src/aig/gia/giaMfs.c b/src/aig/gia/giaMfs.c index 81eb298d31..08bcf143b0 100644 --- a/src/aig/gia/giaMfs.c +++ b/src/aig/gia/giaMfs.c @@ -58,7 +58,6 @@ Sfm_Ntk_t * Gia_ManExtractMfs( Gia_Man_t * p ) Vec_Wec_t * vFanins; // mfs data Vec_Str_t * vFixed; // mfs data Vec_Str_t * vEmpty; // mfs data - Vec_Str_t * vDenied; // mfs data Vec_Wrd_t * vTruths; // mfs data Vec_Int_t * vArray; Vec_Int_t * vStarts; @@ -82,14 +81,9 @@ Sfm_Ntk_t * Gia_ManExtractMfs( Gia_Man_t * p ) vFanins = Vec_WecStart( nMfsVars ); vFixed = Vec_StrStart( nMfsVars ); vEmpty = Vec_StrStart( nMfsVars ); - vDenied = Vec_StrStart( nMfsVars ); vTruths = Vec_WrdStart( nMfsVars ); vStarts = Vec_IntStart( nMfsVars ); vTruths2 = Vec_WrdAlloc( 10000 ); - // deny the PIs which are modeling blackbox outputs from being considered - // in substitutions - for (int i = 0; i < nBbOuts; i++) - Vec_StrWriteEntry( vDenied, i, (char)1 ); // set internal PIs Gia_ManCleanCopyArray( p ); Gia_ManForEachCiId( p, Id, i ) @@ -153,16 +147,12 @@ Sfm_Ntk_t * Gia_ManExtractMfs( Gia_Man_t * p ) // skip POs due to box inputs Counter += nBbIns; assert( Counter == nMfsVars ); - // add functions of the boxes if ( p->pAigExtra ) { + int iBbIn = 0, iBbOut = 0; assert( Gia_ManCiNum(p->pAigExtra) < 16 ); Gia_ObjComputeTruthTableStart( p->pAigExtra, Gia_ManCiNum(p->pAigExtra) ); - } - - { - int iBbIn = 0, iBbOut = 0; curCi = nRealPis; curCo = 0; for ( k = 0; k < nBoxes; k++ ) @@ -172,7 +162,6 @@ Sfm_Ntk_t * Gia_ManExtractMfs( Gia_Man_t * p ) // iterate through box outputs if ( !Tim_ManBoxIsBlack(pManTime, k) ) //&& Tim_ManBoxInputNum(pManTime, k) <= 6 ) { - assert(p->pAigExtra); // collect truth table leaves Vec_IntClear( vLeaves ); for ( i = 0; i < nBoxIns; i++ ) @@ -243,7 +232,6 @@ Sfm_Ntk_t * Gia_ManExtractMfs( Gia_Man_t * p ) Vec_IntFill( vArray, 1, iBbOut++ ); Vec_StrWriteEntry( vFixed, Counter, (char)1 ); Vec_StrWriteEntry( vEmpty, Counter, (char)1 ); - Vec_StrWriteEntry( vDenied, Counter, (char)1 ); Vec_WrdWriteEntry( vTruths, Counter, uTruths6[0] ); } for ( i = 0; i < nBoxIns; i++ ) @@ -252,9 +240,7 @@ Sfm_Ntk_t * Gia_ManExtractMfs( Gia_Man_t * p ) pObj = Gia_ManCo( p, curCo + i ); Counter = Gia_ObjCopyArray( p, Gia_ObjId(p, pObj) ); // connect it with the special primary output (iBbIn) - int poNum = nMfsVars - nBbIns + iBbIn++; - vArray = Vec_WecEntry( vFanins, poNum ); - Vec_StrWriteEntry( vFixed, poNum, (char)1 ); + vArray = Vec_WecEntry( vFanins, nMfsVars - nBbIns + iBbIn++ ); assert( Vec_IntSize(vArray) == 0 ); Vec_IntFill( vArray, 1, Counter ); } @@ -272,21 +258,17 @@ Sfm_Ntk_t * Gia_ManExtractMfs( Gia_Man_t * p ) curCi += nBoxOuts; } curCo += nRealPos; + Gia_ObjComputeTruthTableStop( p->pAigExtra ); // verify counts assert( curCi == Gia_ManCiNum(p) ); assert( curCo == Gia_ManCoNum(p) ); + assert( curCi - nRealPis == Gia_ManCoNum(p->pAigExtra) ); assert( iBbIn == nBbIns ); assert( iBbOut == nBbOuts ); } - - if (p->pAigExtra) { - Gia_ObjComputeTruthTableStop( p->pAigExtra ); - assert( curCi - nRealPis == Gia_ManCoNum(p->pAigExtra) ); - } - // finalize Vec_IntFree( vLeaves ); - return Sfm_NtkConstruct( vFanins, nBbOuts + nRealPis, nRealPos + nBbIns, vFixed, vEmpty, vDenied, vTruths, vStarts, vTruths2 ); + return Sfm_NtkConstruct( vFanins, nBbOuts + nRealPis, nRealPos + nBbIns, vFixed, vEmpty, vTruths, vStarts, vTruths2 ); } /**Function************************************************************* @@ -399,15 +381,6 @@ Gia_Man_t * Gia_ManInsertMfs( Gia_Man_t * p, Sfm_Ntk_t * pNtk, int fAllBoxes ) continue; } Vec_IntClear( vLeaves ); - - // handle internal CIs first, before we go looking for vLeaves - if ( iGroup != -1 && Abc_LitIsCompl(iGroup) ) - { - //Dau_DsdPrintFromTruth( pTruth, Vec_IntSize(vLeaves) ); - iLitNew = Gia_ManAppendCi( pNew ); - goto done; - } - Vec_IntForEachEntry( vArray, Fanin, k ) { iLitNew = Vec_IntEntry( vMfs2Gia, Fanin ); assert( iLitNew >= 0 ); @@ -438,13 +411,17 @@ Gia_Man_t * Gia_ManInsertMfs( Gia_Man_t * p, Sfm_Ntk_t * pNtk, int fAllBoxes ) Abc_TtFlipVar5( pTruth, Vec_IntSize(vLeaves) ); } } + else if ( Abc_LitIsCompl(iGroup) ) // internal CI + { + //Dau_DsdPrintFromTruth( pTruth, Vec_IntSize(vLeaves) ); + iLitNew = Gia_ManAppendCi( pNew ); + } else // internal CO { assert( pTruth[0] == uTruthVar || pTruth[0] == ~uTruthVar ); iLitNew = Gia_ManAppendCo( pNew, Abc_LitNotCond(Vec_IntEntry(vLeaves, 0), pTruth[0] == ~uTruthVar) ); //printf("Group = %d. po = %d\n", iGroup>>1, iMfsId ); } -done: Vec_IntWriteEntry( vMfs2Gia, iMfsId, iLitNew ); } Vec_IntFree( vCover ); @@ -527,6 +504,11 @@ Gia_Man_t * Gia_ManPerformMfs( Gia_Man_t * p, Sfm_Par_t * pPars ) int nFaninMax, nNodes = 0; assert( Gia_ManRegNum(p) == 0 ); assert( p->vMapping != NULL ); + if ( p->pManTime != NULL && p->pAigExtra == NULL ) + { + Abc_Print( 1, "Timing manager is given but there is no GIA of boxes.\n" ); + return NULL; + } if ( p->pManTime != NULL && p->pAigExtra != NULL && Gia_ManCiNum(p->pAigExtra) > 15 ) { Abc_Print( 1, "Currently \"&mfs\" cannot process the network containing white-boxes with more than 15 inputs.\n" ); @@ -541,8 +523,6 @@ Gia_Man_t * Gia_ManPerformMfs( Gia_Man_t * p, Sfm_Par_t * pPars ) } // collect information pNtk = Gia_ManExtractMfs( p ); - if (pPars->fTestReimport) - goto reimport; // perform optimization nNodes = Sfm_NtkPerform( pNtk, pPars ); if ( nNodes == 0 ) @@ -555,7 +535,6 @@ Gia_Man_t * Gia_ManPerformMfs( Gia_Man_t * p, Sfm_Par_t * pPars ) } else { - reimport: pNew = Gia_ManInsertMfs( p, pNtk, pPars->fAllBoxes ); if( pPars->fVerbose ) Abc_Print( 1, "The network has %d nodes changed by \"&mfs\".\n", nNodes ); diff --git a/src/base/abci/abc.c b/src/base/abci/abc.c index 48001c76fd..f5b8af1b51 100644 --- a/src/base/abci/abc.c +++ b/src/base/abci/abc.c @@ -53580,7 +53580,7 @@ int Abc_CommandAbc9Mfs( Abc_Frame_t * pAbc, int argc, char ** argv ) pPars->nDepthMax = 100; pPars->nWinSizeMax = 2000; Extra_UtilGetoptReset(); - while ( ( c = Extra_UtilGetopt( argc, argv, "WFDMLCNdaeblvwhr" ) ) != EOF ) + while ( ( c = Extra_UtilGetopt( argc, argv, "WFDMLCNdaeblvwh" ) ) != EOF ) { switch ( c ) { @@ -53676,9 +53676,6 @@ int Abc_CommandAbc9Mfs( Abc_Frame_t * pAbc, int argc, char ** argv ) case 'l': pPars->fUseDcs ^= 1; break; - case 'r': - pPars->fTestReimport ^= 1; - break; case 'v': pPars->fVerbose ^= 1; break; @@ -53748,7 +53745,6 @@ int Abc_CommandAbc9Mfs( Abc_Frame_t * pAbc, int argc, char ** argv ) Abc_Print( -2, "\t-l : toggle deriving don't-cares [default = %s]\n", pPars->fUseDcs? "yes": "no" ); Abc_Print( -2, "\t-v : toggle printing optimization summary [default = %s]\n", pPars->fVerbose? "yes": "no" ); Abc_Print( -2, "\t-w : toggle printing detailed stats for each node [default = %s]\n", pPars->fVeryVerbose? "yes": "no" ); - Abc_Print( -2, "\t-r : toggle testing re-importing the network unchanged [default = %s]\n", pPars->fTestReimport? "yes": "no" ); Abc_Print( -2, "\t-h : print the command usage\n"); return 1; } diff --git a/src/base/abci/abcMfs.c b/src/base/abci/abcMfs.c index ed38f6fc7e..90fa268ed4 100644 --- a/src/base/abci/abcMfs.c +++ b/src/base/abci/abcMfs.c @@ -217,7 +217,7 @@ Sfm_Ntk_t * Abc_NtkExtractMfs( Abc_Ntk_t * pNtk, int nFirstFixed ) // for ( i = Abc_NtkCiNum(pNtk); i + Abc_NtkCoNum(pNtk) < Abc_NtkObjNum(pNtk); i++ ) // if ( rand() % 10 == 0 ) // Vec_StrWriteEntry( vFixed, i, (char)1 ); - return Sfm_NtkConstruct( vFanins, Abc_NtkCiNum(pNtk), Abc_NtkCoNum(pNtk), vFixed, NULL, NULL, vTruths, vStarts, vTruths2 ); + return Sfm_NtkConstruct( vFanins, Abc_NtkCiNum(pNtk), Abc_NtkCoNum(pNtk), vFixed, NULL, vTruths, vStarts, vTruths2 ); } Sfm_Ntk_t * Abc_NtkExtractMfs2( Abc_Ntk_t * pNtk, int iPivot ) { @@ -286,7 +286,7 @@ Sfm_Ntk_t * Abc_NtkExtractMfs2( Abc_Ntk_t * pNtk, int iPivot ) Abc_NtkForEachNode( pNtk, pObj, i ) if ( i >= iPivot ) Vec_StrWriteEntry( vFixed, pObj->iTemp, (char)1 ); - return Sfm_NtkConstruct( vFanins, Abc_NtkCiNum(pNtk), Abc_NtkCoNum(pNtk), vFixed, NULL, NULL, vTruths, vStarts, vTruths2 ); + return Sfm_NtkConstruct( vFanins, Abc_NtkCiNum(pNtk), Abc_NtkCoNum(pNtk), vFixed, NULL, vTruths, vStarts, vTruths2 ); } /**Function************************************************************* diff --git a/src/opt/sfm/sfm.h b/src/opt/sfm/sfm.h index 5fd564fce9..f9c95c83eb 100644 --- a/src/opt/sfm/sfm.h +++ b/src/opt/sfm/sfm.h @@ -73,7 +73,6 @@ struct Sfm_Par_t_ int fDelayVerbose; // enable delay stats int fVerbose; // enable basic stats int fVeryVerbose; // enable detailed stats - int fTestReimport; // enable testing of re-import }; //////////////////////////////////////////////////////////////////////// @@ -89,7 +88,7 @@ struct Sfm_Par_t_ extern void Sfm_ParSetDefault( Sfm_Par_t * pPars ); extern int Sfm_NtkPerform( Sfm_Ntk_t * p, Sfm_Par_t * pPars ); /*=== sfmNtk.c ==========================================================*/ -extern Sfm_Ntk_t * Sfm_NtkConstruct( Vec_Wec_t * vFanins, int nPis, int nPos, Vec_Str_t * vFixed, Vec_Str_t * vEmpty, Vec_Str_t * vDenied, Vec_Wrd_t * vTruths, Vec_Int_t * vStarts, Vec_Wrd_t * vTruths2 ); +extern Sfm_Ntk_t * Sfm_NtkConstruct( Vec_Wec_t * vFanins, int nPis, int nPos, Vec_Str_t * vFixed, Vec_Str_t * vEmpty, Vec_Wrd_t * vTruths, Vec_Int_t * vStarts, Vec_Wrd_t * vTruths2 ); extern void Sfm_NtkFree( Sfm_Ntk_t * p ); extern Vec_Int_t * Sfm_NodeReadFanins( Sfm_Ntk_t * p, int i ); extern word * Sfm_NodeReadTruth( Sfm_Ntk_t * p, int i ); diff --git a/src/opt/sfm/sfmCore.c b/src/opt/sfm/sfmCore.c index a57c5623ab..a988297592 100644 --- a/src/opt/sfm/sfmCore.c +++ b/src/opt/sfm/sfmCore.c @@ -58,7 +58,6 @@ void Sfm_ParSetDefault( Sfm_Par_t * pPars ) pPars->fAllBoxes = 0; // enable preserving all boxes pPars->fVerbose = 0; // enable basic stats pPars->fVeryVerbose = 0; // enable detailed stats - pPars->fTestReimport = 0; // enable testing of re-import } /**Function************************************************************* @@ -115,7 +114,7 @@ int Sfm_NodeResubSolve( Sfm_Ntk_t * p, int iNode, int f, int fRemoveOnly ) int fSkipUpdate = 0; int fVeryVerbose = 0;//p->pPars->fVeryVerbose && Vec_IntSize(p->vDivs) < 200;// || pNode->Id == 556; int i, iFanin, iVar = -1; - int iFaninRem = -1; + int iFaninRem = -1, iFaninSkip = -1; int nFanins = Sfm_ObjFaninNum(p, iNode); word uTruth, uSign, uMask; abctime clk; @@ -138,6 +137,9 @@ int Sfm_NodeResubSolve( Sfm_Ntk_t * p, int iNode, int f, int fRemoveOnly ) else iFaninRem = iFanin; assert( iFaninRem != -1 ); + // find fanin to skip + if ( Sfm_ObjIsFixed(p, iFaninRem) && Sfm_ObjFaninNum(p, iFaninRem) == 1 ) + iFaninSkip = Sfm_ObjFanin(p, iFaninRem, 0); clk = Abc_Clock(); uTruth = Sfm_ComputeInterpolant( p ); p->timeSat += Abc_Clock() - clk; @@ -173,10 +175,11 @@ p->timeSat += Abc_Clock() - clk; // find the next divisor to try uMask = (~(word)0) >> (64 - p->nCexes); Vec_WrdForEachEntry( p->vDivCexes, uSign, iVar ) - if ( uSign == uMask && !Sfm_ObjIsDenied(p, Vec_IntEntry(p->vDivs, iVar)) ) + if ( uSign == uMask && Vec_IntEntry(p->vDivs, iVar) != iFaninSkip ) break; if ( iVar == Vec_IntSize(p->vDivs) ) return 0; + assert( Vec_IntEntry(p->vDivs, iVar) != iFaninSkip ); // try replacing the critical fanin Vec_IntPush( p->vDivIds, Sfm_ObjSatVar(p, Vec_IntEntry(p->vDivs, iVar)) ); clk = Abc_Clock(); @@ -285,10 +288,6 @@ p->timeSat += Abc_Clock() - clk; int Sfm_NodeResub( Sfm_Ntk_t * p, int iNode ) { int i, iFanin; - - if (Sfm_NodeReadFixed(p, iNode)) - return 0; - p->nNodesTried++; // prepare SAT solver if ( !Sfm_NtkCreateWindow( p, iNode, p->pPars->fVeryVerbose ) ) diff --git a/src/opt/sfm/sfmInt.h b/src/opt/sfm/sfmInt.h index 6f86a5c6a3..ae4bb60a80 100644 --- a/src/opt/sfm/sfmInt.h +++ b/src/opt/sfm/sfmInt.h @@ -81,7 +81,6 @@ struct Sfm_Ntk_t_ // user data Vec_Str_t * vFixed; // persistent objects Vec_Str_t * vEmpty; // transparent objects - Vec_Str_t * vDenied; // denied objects Vec_Wrd_t * vTruths; // truth tables Vec_Wec_t vFanins; // fanins Vec_Int_t * vStarts; // offsets @@ -158,7 +157,6 @@ static inline int Sfm_ObjIsPi( Sfm_Ntk_t * p, int i ) { return static inline int Sfm_ObjIsPo( Sfm_Ntk_t * p, int i ) { return i + p->nPos >= p->nObjs; } static inline int Sfm_ObjIsNode( Sfm_Ntk_t * p, int i ) { return i >= p->nPis && i + p->nPos < p->nObjs; } static inline int Sfm_ObjIsFixed( Sfm_Ntk_t * p, int i ) { return Vec_StrEntry(p->vFixed, i); } -static inline int Sfm_ObjIsDenied( Sfm_Ntk_t * p, int i ) { return p->vDenied && Vec_StrEntry(p->vDenied, i); } static inline int Sfm_ObjAddsLevelArray( Vec_Str_t * p, int i ) { return p == NULL || Vec_StrEntry(p, i) == 0; } static inline int Sfm_ObjAddsLevel( Sfm_Ntk_t * p, int i ) { return Sfm_ObjAddsLevelArray(p->vEmpty, i); } diff --git a/src/opt/sfm/sfmNtk.c b/src/opt/sfm/sfmNtk.c index 9f32cbb580..70afd91aae 100644 --- a/src/opt/sfm/sfmNtk.c +++ b/src/opt/sfm/sfmNtk.c @@ -58,7 +58,7 @@ void Sfm_CheckConsistency( Vec_Wec_t * vFanins, int nPis, int nPos, Vec_Str_t * assert( Fanin + nPos < Vec_WecSize(vFanins) ); // POs have one fanout if ( i + nPos >= Vec_WecSize(vFanins) ) - assert( Vec_IntSize(vArray) == 1 ); + assert( Vec_IntSize(vArray) == 1 && Vec_StrEntry(vFixed, i) == (char)0 ); } } @@ -164,7 +164,7 @@ void Sfm_CreateLevelR( Vec_Wec_t * vFanouts, Vec_Int_t * vLevelsR, Vec_Str_t * v SeeAlso [] ***********************************************************************/ -Sfm_Ntk_t * Sfm_NtkConstruct( Vec_Wec_t * vFanins, int nPis, int nPos, Vec_Str_t * vFixed, Vec_Str_t * vEmpty, Vec_Str_t * vDenied, Vec_Wrd_t * vTruths, Vec_Int_t * vStarts, Vec_Wrd_t * vTruths2 ) +Sfm_Ntk_t * Sfm_NtkConstruct( Vec_Wec_t * vFanins, int nPis, int nPos, Vec_Str_t * vFixed, Vec_Str_t * vEmpty, Vec_Wrd_t * vTruths, Vec_Int_t * vStarts, Vec_Wrd_t * vTruths2 ) { Sfm_Ntk_t * p; int i; Sfm_CheckConsistency( vFanins, nPis, nPos, vFixed ); @@ -177,7 +177,6 @@ Sfm_Ntk_t * Sfm_NtkConstruct( Vec_Wec_t * vFanins, int nPis, int nPos, Vec_Str_t p->vFixed = vFixed; p->vEmpty = vEmpty; p->vTruths = vTruths; - p->vDenied = vDenied; p->vFanins = *vFanins; p->vStarts = vStarts; p->vTruths2 = vTruths2; @@ -222,7 +221,6 @@ void Sfm_NtkFree( Sfm_Ntk_t * p ) // user data Vec_StrFree( p->vFixed ); Vec_StrFreeP( &p->vEmpty ); - Vec_StrFreeP( &p->vDenied ); Vec_WrdFree( p->vTruths ); Vec_WecErase( &p->vFanins ); Vec_IntFree( p->vStarts ); diff --git a/src/opt/sfm/sfmWin.c b/src/opt/sfm/sfmWin.c index 2e1681991a..53f9a71efe 100644 --- a/src/opt/sfm/sfmWin.c +++ b/src/opt/sfm/sfmWin.c @@ -142,19 +142,18 @@ void Sfm_NtkDfs_rec( Sfm_Ntk_t * p, int iNode, Vec_Int_t * vNodes, Vec_Wec_t * v return; if ( Vec_IntEntry(vGroupMap, iNode) >= 0 ) { - int iGroup = Abc_Lit2Var(Vec_IntEntry(vGroupMap, iNode)); - Vec_Int_t * vGroup = Vec_WecEntry(vGroups, iGroup); - assert(Abc_LitIsCompl(Vec_IntEntry(vGroupMap, iNode))); - Vec_IntPush(vBoxesLeft, iGroup); - Vec_IntForEachEntry(vGroup, iNode, i) - { - // ignore inputs - if ( !Abc_LitIsCompl(Vec_IntEntry(vGroupMap, iNode))) - continue; - assert(Sfm_ObjIsNode(p, iNode)); - Sfm_ObjSetTravIdCurrent(p, iNode); - Vec_IntPush(vNodes, iNode); - } + int k, iGroup = Abc_Lit2Var( Vec_IntEntry(vGroupMap, iNode) ); + Vec_Int_t * vGroup = Vec_WecEntry( vGroups, iGroup ); + Vec_IntForEachEntry( vGroup, iNode, i ) + assert( Sfm_ObjIsNode(p, iNode) ); + Vec_IntForEachEntry( vGroup, iNode, i ) + Sfm_ObjSetTravIdCurrent( p, iNode ); + Vec_IntForEachEntry( vGroup, iNode, i ) + Sfm_ObjForEachFanin( p, iNode, iFanin, k ) + Sfm_NtkDfs_rec( p, iFanin, vNodes, vGroups, vGroupMap, vBoxesLeft ); + Vec_IntForEachEntry( vGroup, iNode, i ) + Vec_IntPush( vNodes, iNode ); + Vec_IntPush( vBoxesLeft, iGroup ); } else { @@ -171,33 +170,14 @@ Vec_Int_t * Sfm_NtkDfs( Sfm_Ntk_t * p, Vec_Wec_t * vGroups, Vec_Int_t * vGroupMa Vec_IntClear( vBoxesLeft ); vNodes = Vec_IntAlloc( p->nObjs ); Sfm_NtkIncrementTravId( p ); - - assert(!fAllBoxes); // TODO - - Sfm_NtkForEachPo( p, i ) { - int iFanin = Sfm_ObjFanin(p, i, 0); - // detect fake PO modeling blackbox input - if (Vec_IntEntry(vGroupMap, iFanin) >= 0 && !Abc_LitIsCompl(Vec_IntEntry(vGroupMap, iFanin))) - continue; - Sfm_NtkDfs_rec( p, iFanin, vNodes, vGroups, vGroupMap, vBoxesLeft ); - } - - for (i = 0; i < Vec_IntSize( vBoxesLeft ); i++) + if ( fAllBoxes ) { - int k, j, iNode, iFanin; - int iGroup = Vec_IntEntry( vBoxesLeft, i ); - Vec_Int_t * vGroup = Vec_WecEntry( vGroups, iGroup ); - Vec_IntForEachEntry( vGroup, iNode, k ) - { - // ignore outputs - if ( Abc_LitIsCompl( Vec_IntEntry(vGroupMap, iNode)) ) - continue; - Sfm_ObjForEachFanin( p, iNode, iFanin, j ) - Sfm_NtkDfs_rec( p, iFanin, vNodes, vGroups, vGroupMap, vBoxesLeft ); - Vec_IntPush( vNodes, iNode ); - } + Vec_Int_t * vGroup; + Vec_WecForEachLevel( vGroups, vGroup, i ) + Sfm_NtkDfs_rec( p, Vec_IntEntry(vGroup, 0), vNodes, vGroups, vGroupMap, vBoxesLeft ); } - + Sfm_NtkForEachPo( p, i ) + Sfm_NtkDfs_rec( p, Sfm_ObjFanin(p, i, 0), vNodes, vGroups, vGroupMap, vBoxesLeft ); return vNodes; } From bd05a6454e8c157caaa58ceda676ae0249d8e27c Mon Sep 17 00:00:00 2001 From: Miodrag Milanovic <mmicko@gmail.com> Date: Wed, 10 Dec 2025 11:03:11 +0100 Subject: [PATCH 73/79] Fix WASI build --- src/misc/util/utilNet.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/misc/util/utilNet.c b/src/misc/util/utilNet.c index e51a025319..db21c8c3b2 100755 --- a/src/misc/util/utilNet.c +++ b/src/misc/util/utilNet.c @@ -1103,7 +1103,11 @@ tn_vi * Tn_SolveSat( const char * pFileNameIn, const char * pFileNameOut, int Se sprintf( pCommand, "%s --seed=%d %s %s > %s", pKissat, Seed, fVerboseSolver ? "": "-q", pFileNameIn, pFileNameOut ); //if ( fVerbose ) // printf( "Running command line: %s\n", pCommand ); +#if defined(__wasm) + if ( 1 ) +#else if ( system( pCommand ) == -1 ) +#endif { printf( "Command \"%s\" did not succeed.\n", pCommand ); return 0; From 6c34efdc2bc5ee307a82c5d04e4f139ce37a6686 Mon Sep 17 00:00:00 2001 From: Miodrag Milanovic <mmicko@gmail.com> Date: Mon, 22 Dec 2025 12:26:01 +0100 Subject: [PATCH 74/79] Fixing revert/merge difference in code --- src/misc/tim/timMan.c | 28 ++++++++++++++++++++-------- 1 file changed, 20 insertions(+), 8 deletions(-) diff --git a/src/misc/tim/timMan.c b/src/misc/tim/timMan.c index 7e112669e1..9bcdaaae68 100644 --- a/src/misc/tim/timMan.c +++ b/src/misc/tim/timMan.c @@ -451,19 +451,31 @@ void Tim_ManCreate( Tim_Man_t * p, void * pLib, Vec_Flt_t * vInArrs, Vec_Flt_t * // create arrival times if ( vInArrs ) { - assert( Vec_FltSize(vInArrs) == Tim_ManPiNum(p) ); - Tim_ManForEachPi( p, pObj, i ) - pObj->timeArr = Vec_FltEntry(vInArrs, i); - + if ( Vec_FltSize(vInArrs) == Tim_ManPiNum(p) ) { + Tim_ManForEachPi( p, pObj, i ) + pObj->timeArr = Vec_FltEntry(vInArrs, i); + } + else if ( Vec_FltSize(vInArrs) == Tim_ManCiNum(p) ) { + Tim_ManForEachCi( p, pObj, i ) + pObj->timeArr = Vec_FltEntry(vInArrs, i); + } + else assert( 0 ); } // create required times if ( vOutReqs ) { k = 0; - assert( Vec_FltSize(vOutReqs) == Tim_ManPoNum(p) ); - Tim_ManForEachPo( p, pObj, i ) - pObj->timeReq = Vec_FltEntry(vOutReqs, k++); - assert( k == Tim_ManPoNum(p) ); + if ( Vec_FltSize(vOutReqs) == Tim_ManPoNum(p) ) { + Tim_ManForEachPo( p, pObj, i ) + pObj->timeReq = Vec_FltEntry(vOutReqs, k++); + assert( k == Tim_ManPoNum(p) ); + } + else if ( Vec_FltSize(vOutReqs) == Tim_ManCoNum(p) ) { + Tim_ManForEachCo( p, pObj, i ) + pObj->timeReq = Vec_FltEntry(vOutReqs, k++); + assert( k == Tim_ManCoNum(p) ); + } + else assert( 0 ); } } From 9182a8048d0bc86b39a6cb6b0488a7e1d10b2607 Mon Sep 17 00:00:00 2001 From: Miodrag Milanovic <mmicko@gmail.com> Date: Mon, 22 Dec 2025 12:31:08 +0100 Subject: [PATCH 75/79] WASI build fix for solver command --- src/base/cmd/cmd.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/base/cmd/cmd.c b/src/base/cmd/cmd.c index a343c67890..fff4fa9f6b 100644 --- a/src/base/cmd/cmd.c +++ b/src/base/cmd/cmd.c @@ -2844,6 +2844,10 @@ int CmdCommandSolver( Abc_Frame_t * pAbc, int argc, char **argv ) goto usage; } +#if defined(__wasm) + fprintf( pAbc->Err, "Unsupported command.\n" ); + return 1; +#else // Check if solver binary exists in current directory or PATH char * pSolverName; if ( (pFile = fopen( "./solver", "r" )) != NULL ) @@ -2913,7 +2917,7 @@ int CmdCommandSolver( Abc_Frame_t * pAbc, int argc, char **argv ) // Clean up the temporary file unlink( TempFileName ); - +#endif return 0; usage: From ef74590ebd78b3b707eeba56d8284faf018affa6 Mon Sep 17 00:00:00 2001 From: Miodrag Milanovic <mmicko@gmail.com> Date: Tue, 30 Dec 2025 09:22:59 +0100 Subject: [PATCH 76/79] WASI fixes --- src/misc/util/utilAigSim.c | 18 ++++++++++++++++++ src/opt/ufar/UfarMgr.cpp | 2 -- src/opt/ufar/UfarPth.cpp | 4 ++++ src/opt/untk/NtkNtk.cpp | 1 - src/opt/util/util.cpp | 2 ++ 5 files changed, 24 insertions(+), 3 deletions(-) diff --git a/src/misc/util/utilAigSim.c b/src/misc/util/utilAigSim.c index a12d71d817..a05b0f9668 100644 --- a/src/misc/util/utilAigSim.c +++ b/src/misc/util/utilAigSim.c @@ -26,6 +26,8 @@ #include <ctype.h> #include <time.h> #include <unistd.h> // mkstemp(), close(), unlink() +#include <fcntl.h> +#include <sys/stat.h> #define AIGSIM_LIBRARY_ONLY @@ -284,8 +286,14 @@ static int ends_with(const char *s, const char *suf) { static int make_tmp_file(char *path, size_t cap, const char *prefix) { // Creates an existing temp file (for input) +#if defined(__wasm) + static int seq = 0; // no risk of collision since we're in a sandbox + snprintf(path, cap, "%s%08d", prefix, seq++); + int fd = open(path, O_CREAT | O_EXCL | O_RDWR, S_IREAD | S_IWRITE); +#else snprintf(path, cap, "/tmp/%sXXXXXX", prefix); int fd = mkstemp(path); +#endif if (fd < 0) return 0; close(fd); return 1; @@ -293,8 +301,14 @@ static int make_tmp_file(char *path, size_t cap, const char *prefix) { static int make_tmp_path_noexist(char *path, size_t cap, const char *prefix) { // Creates a unique temp path that does not exist (for output) +#if defined(__wasm) + static int seq = 0; // no risk of collision since we're in a sandbox + snprintf(path, cap, "%s%08d", prefix, seq++); + int fd = open(path, O_CREAT | O_EXCL | O_RDWR, S_IREAD | S_IWRITE); +#else snprintf(path, cap, "/tmp/%sXXXXXX", prefix); int fd = mkstemp(path); +#endif if (fd < 0) return 0; close(fd); unlink(path); @@ -527,7 +541,11 @@ static int SimulateCompareAigBin(const AigMan *p1, const char *bin, // Run external binary: "<bin> <inFile> <outFile>" remove(outFile); snprintf(cmd, sizeof(cmd), "%s %s %s", bin, inFile, outFile); +#if defined(__wasm) + int rc = -1; +#else int rc = system(cmd); +#endif if (rc != 0) { fprintf(stderr, "Error: system() failed (rc=%d): %s\n", rc, cmd); goto fail; diff --git a/src/opt/ufar/UfarMgr.cpp b/src/opt/ufar/UfarMgr.cpp index ea565b5e4c..18a9c3ebaf 100755 --- a/src/opt/ufar/UfarMgr.cpp +++ b/src/opt/ufar/UfarMgr.cpp @@ -14,8 +14,6 @@ #include <array> #include <regex> -#include <sys/wait.h> - #include <base/wlc/wlc.h> #include <sat/cnf/cnf.h> #include <aig/gia/giaAig.h> diff --git a/src/opt/ufar/UfarPth.cpp b/src/opt/ufar/UfarPth.cpp index 6df45657fe..6f3486295a 100755 --- a/src/opt/ufar/UfarPth.cpp +++ b/src/opt/ufar/UfarPth.cpp @@ -184,6 +184,10 @@ class PDRWLA : public Solver { Wlc_Par_t _Pars; }; +#if defined(__wasm) +static void pthread_exit(void *retval) __attribute__((noreturn)) { } +#endif + void KillOthers() { pthread_cond_signal( &g_cond ); ++g_nRunIds; diff --git a/src/opt/untk/NtkNtk.cpp b/src/opt/untk/NtkNtk.cpp index 0ee5e5fd78..4634076415 100755 --- a/src/opt/untk/NtkNtk.cpp +++ b/src/opt/untk/NtkNtk.cpp @@ -6,7 +6,6 @@ */ #include <unistd.h> -#include <sys/wait.h> #include <fstream> #include <base/wlc/wlc.h> diff --git a/src/opt/util/util.cpp b/src/opt/util/util.cpp index df5de2fd8c..cdcd6e3fae 100644 --- a/src/opt/util/util.cpp +++ b/src/opt/util/util.cpp @@ -6,7 +6,9 @@ */ #include <iomanip> +#if !defined(__wasm) #include <csignal> +#endif #include <unistd.h> #ifdef __linux__ From 79010216cb87427dd7a0c8d38f156494221be006 Mon Sep 17 00:00:00 2001 From: Miodrag Milanovic <mmicko@gmail.com> Date: Thu, 29 Jan 2026 09:26:58 +0100 Subject: [PATCH 77/79] MINGW proper pthread handling --- src/aig/gia/giaKf.c | 2 +- src/aig/gia/giaTranStoch.c | 2 +- src/base/cmd/cmdAuto.c | 2 +- src/base/cmd/cmdStarter.c | 2 +- src/base/wlc/wlcPth.c | 2 +- src/map/if/ifDsd.c | 2 +- src/map/if/ifTest.c | 2 +- src/misc/util/utilPth.c | 4 ++-- src/opt/ufar/UfarPth.cpp | 2 +- src/proof/abs/absPth.c | 2 +- src/proof/cec/cecProve.c | 2 +- src/proof/cec/cecSplit.c | 2 +- src/proof/ssw/sswPart.c | 2 +- src/sat/bmc/bmcBmcS.c | 2 +- src/sat/cnf/cnfUtil.c | 2 +- 15 files changed, 16 insertions(+), 16 deletions(-) diff --git a/src/aig/gia/giaKf.c b/src/aig/gia/giaKf.c index 6909851f1a..820178d36c 100644 --- a/src/aig/gia/giaKf.c +++ b/src/aig/gia/giaKf.c @@ -29,7 +29,7 @@ #ifdef ABC_USE_PTHREADS -#ifdef _WIN32 +#if defined(_WIN32) && !defined(__MINGW32__) #include "../lib/pthread.h" #else #include <pthread.h> diff --git a/src/aig/gia/giaTranStoch.c b/src/aig/gia/giaTranStoch.c index f7b300c0d0..286eae98b2 100644 --- a/src/aig/gia/giaTranStoch.c +++ b/src/aig/gia/giaTranStoch.c @@ -37,7 +37,7 @@ #ifdef ABC_USE_PTHREADS -#ifdef _WIN32 +#if defined(_WIN32) && !defined(__MINGW32__) #include "../lib/pthread.h" #else #include <pthread.h> diff --git a/src/base/cmd/cmdAuto.c b/src/base/cmd/cmdAuto.c index 8151c2e534..98cb6a5a2a 100644 --- a/src/base/cmd/cmdAuto.c +++ b/src/base/cmd/cmdAuto.c @@ -29,7 +29,7 @@ #ifdef ABC_USE_PTHREADS -#ifdef _WIN32 +#if defined(_WIN32) && !defined(__MINGW32__) #include "../lib/pthread.h" #else #include <pthread.h> diff --git a/src/base/cmd/cmdStarter.c b/src/base/cmd/cmdStarter.c index bfbe5533d2..f0cb121b1d 100644 --- a/src/base/cmd/cmdStarter.c +++ b/src/base/cmd/cmdStarter.c @@ -27,7 +27,7 @@ #ifdef ABC_USE_PTHREADS -#ifdef _WIN32 +#if defined(_WIN32) && !defined(__MINGW32__) #include "../lib/pthread.h" #else #include <pthread.h> diff --git a/src/base/wlc/wlcPth.c b/src/base/wlc/wlcPth.c index ddafab23ca..5a699e888a 100644 --- a/src/base/wlc/wlcPth.c +++ b/src/base/wlc/wlcPth.c @@ -23,7 +23,7 @@ #ifdef ABC_USE_PTHREADS -#ifdef _WIN32 +#if defined(_WIN32) && !defined(__MINGW32__) #include "../lib/pthread.h" #else #include <pthread.h> diff --git a/src/map/if/ifDsd.c b/src/map/if/ifDsd.c index c25873a9f4..f72df0ac55 100644 --- a/src/map/if/ifDsd.c +++ b/src/map/if/ifDsd.c @@ -32,7 +32,7 @@ #ifdef ABC_USE_PTHREADS -#ifdef _WIN32 +#if defined(_WIN32) && !defined(__MINGW32__) #include "../lib/pthread.h" #else #include <pthread.h> diff --git a/src/map/if/ifTest.c b/src/map/if/ifTest.c index fa9a644437..4b1ede06f5 100644 --- a/src/map/if/ifTest.c +++ b/src/map/if/ifTest.c @@ -23,7 +23,7 @@ #ifdef ABC_USE_PTHREADS -#ifdef _WIN32 +#if defined(_WIN32) && !defined(__MINGW32__) #include "../lib/pthread.h" #else #include <pthread.h> diff --git a/src/misc/util/utilPth.c b/src/misc/util/utilPth.c index 940f7929a8..0e7194235e 100644 --- a/src/misc/util/utilPth.c +++ b/src/misc/util/utilPth.c @@ -23,7 +23,7 @@ #include <stdlib.h> #include <assert.h> -#ifdef _WIN32 +#if defined(_WIN32) && !defined(__MINGW32__) #include <windows.h> #include <time.h> // nanosleep implementation for Windows @@ -41,7 +41,7 @@ static inline int nanosleep(const struct timespec *req, struct timespec *rem) { #ifdef ABC_USE_PTHREADS -#ifdef _WIN32 +#if defined(_WIN32) && !defined(__MINGW32__) #include "../lib/pthread.h" #else #include <pthread.h> diff --git a/src/opt/ufar/UfarPth.cpp b/src/opt/ufar/UfarPth.cpp index 6de28dbc55..85bb0e178a 100755 --- a/src/opt/ufar/UfarPth.cpp +++ b/src/opt/ufar/UfarPth.cpp @@ -1,7 +1,7 @@ #include "misc/util/abc_namespaces.h" -#ifdef _WIN32 +#if defined(_WIN32) && !defined(__MINGW32__) #include "../lib/pthread.h" #else #include <pthread.h> diff --git a/src/proof/abs/absPth.c b/src/proof/abs/absPth.c index f04a20d1c6..d8cb343598 100644 --- a/src/proof/abs/absPth.c +++ b/src/proof/abs/absPth.c @@ -25,7 +25,7 @@ #ifdef ABC_USE_PTHREADS -#ifdef _WIN32 +#if defined(_WIN32) && !defined(__MINGW32__) #include "../lib/pthread.h" #else #include <pthread.h> diff --git a/src/proof/cec/cecProve.c b/src/proof/cec/cecProve.c index ff10e8cae1..281dc27043 100644 --- a/src/proof/cec/cecProve.c +++ b/src/proof/cec/cecProve.c @@ -30,7 +30,7 @@ #ifdef ABC_USE_PTHREADS -#ifdef _WIN32 +#if defined(_WIN32) && !defined(__MINGW32__) #include "../lib/pthread.h" #else #include <pthread.h> diff --git a/src/proof/cec/cecSplit.c b/src/proof/cec/cecSplit.c index 34a635b88f..9c402a89f5 100644 --- a/src/proof/cec/cecSplit.c +++ b/src/proof/cec/cecSplit.c @@ -28,7 +28,7 @@ #ifdef ABC_USE_PTHREADS -#ifdef _WIN32 +#if defined(_WIN32) && !defined(__MINGW32__) #include "../lib/pthread.h" #else #include <pthread.h> diff --git a/src/proof/ssw/sswPart.c b/src/proof/ssw/sswPart.c index 3a65f17ddc..094ffab889 100644 --- a/src/proof/ssw/sswPart.c +++ b/src/proof/ssw/sswPart.c @@ -25,7 +25,7 @@ #ifdef ABC_USE_PTHREADS -#ifdef _WIN32 +#if defined(_WIN32) && !defined(__MINGW32__) #include <windows.h> #include <time.h> #include "../lib/pthread.h" diff --git a/src/sat/bmc/bmcBmcS.c b/src/sat/bmc/bmcBmcS.c index 8bfda56ba7..f43722c9e8 100644 --- a/src/sat/bmc/bmcBmcS.c +++ b/src/sat/bmc/bmcBmcS.c @@ -48,7 +48,7 @@ #ifdef ABC_USE_PTHREADS -#ifdef _WIN32 +#if defined(_WIN32) && !defined(__MINGW32__) #include "../lib/pthread.h" #else #include <pthread.h> diff --git a/src/sat/cnf/cnfUtil.c b/src/sat/cnf/cnfUtil.c index 3a47ae796f..0c5791f6fe 100644 --- a/src/sat/cnf/cnfUtil.c +++ b/src/sat/cnf/cnfUtil.c @@ -29,7 +29,7 @@ #ifdef ABC_USE_PTHREADS -#ifdef _WIN32 +#if defined(_WIN32) && !defined(__MINGW32__) #include "../lib/pthread.h" #else #include <pthread.h> From 6dea73ebba6fbb6313512f0dd2c15a7c40112428 Mon Sep 17 00:00:00 2001 From: Rob Taylor <rob.taylor@chipflow.io> Date: Sun, 22 Feb 2026 23:26:11 +0000 Subject: [PATCH 78/79] Fix MinGW/Windows build compatibility - Use stdatomic.h on MinGW instead of MSVC Interlocked functions. MinGW defines _WIN32 but has <stdatomic.h> unlike MSVC. Fix guards in utilPth.c and sswPart.c. - Exclude -ldl and -lrt from link libraries on MINGW/MSYS platforms, as these Unix-only libraries don't exist on MinGW. - Add -lshlwapi on MINGW/MSYS for PathMatchSpec used in sclLiberty.c. Co-developed-by: Claude Code v2.1.50 (claude-opus-4-6) --- Makefile | 19 +++++++++++++++++-- src/misc/util/utilPth.c | 2 +- src/proof/ssw/sswPart.c | 2 +- 3 files changed, 19 insertions(+), 4 deletions(-) diff --git a/Makefile b/Makefile index a9d9b79c51..82ad83407d 100644 --- a/Makefile +++ b/Makefile @@ -141,11 +141,26 @@ endif # LIBS := -ldl -lrt LIBS += -lm ifneq ($(OS), $(filter $(OS), FreeBSD OpenBSD NetBSD)) - LIBS += -ldl + ifeq ($(findstring MINGW,$(OS)),) + ifeq ($(findstring MSYS,$(OS)),) + LIBS += -ldl + endif + endif endif ifneq ($(OS), $(filter $(OS), FreeBSD OpenBSD NetBSD Darwin)) - LIBS += -lrt + ifeq ($(findstring MINGW,$(OS)),) + ifeq ($(findstring MSYS,$(OS)),) + LIBS += -lrt + endif + endif +endif + +ifneq ($(findstring MINGW,$(OS)),) + LIBS += -lshlwapi +endif +ifneq ($(findstring MSYS,$(OS)),) + LIBS += -lshlwapi endif ifdef ABC_USE_LIBSTDCXX diff --git a/src/misc/util/utilPth.c b/src/misc/util/utilPth.c index 0e7194235e..a9070146f0 100644 --- a/src/misc/util/utilPth.c +++ b/src/misc/util/utilPth.c @@ -51,7 +51,7 @@ static inline int nanosleep(const struct timespec *req, struct timespec *rem) { #include <atomic> using namespace std; #else -#ifndef _WIN32 +#if !defined(_WIN32) || defined(__MINGW32__) #include <stdatomic.h> #else // MSVC doesn't have stdatomic.h, use Interlocked functions instead diff --git a/src/proof/ssw/sswPart.c b/src/proof/ssw/sswPart.c index 094ffab889..8e1c054f8d 100644 --- a/src/proof/ssw/sswPart.c +++ b/src/proof/ssw/sswPart.c @@ -49,7 +49,7 @@ static inline int nanosleep(const struct timespec *req, struct timespec *rem) { #include <atomic> using namespace std; #else -#ifndef _WIN32 +#if !defined(_WIN32) || defined(__MINGW32__) #include <stdatomic.h> #else // MSVC doesn't have stdatomic.h, use Interlocked functions instead From 66c03f32a14dc7e4eced6b9f1273e9bd22fa7b56 Mon Sep 17 00:00:00 2001 From: Rob Taylor <rob.taylor@chipflow.io> Date: Sun, 22 Feb 2026 23:26:11 +0000 Subject: [PATCH 79/79] Fix CI: modernize Windows build and prevent duplicate workflow runs - Replace ancient .dsw project upgrade approach with CMake + MSYS2/MinGW on Windows, using the same CMake build system as posix builds - Use MSYS2 with mingw-w64 toolchain for reliable GNU make + GCC support - Use ABC_USE_STDINT_H=1 and -DWIN32 -DWIN32_NO_DLL to handle MinGW platform quirks (32-bit long on 64-bit Windows, missing WIN32 macro) - Update Windows runner from windows-2019 to windows-latest - Restrict push triggers to main/master branches only across all workflows, keeping pull_request triggers for all branches Co-developed-by: Claude Code v2.1.50 (claude-opus-4-6) --- .github/workflows/build-posix-cmake.yml | 1 + .github/workflows/build-posix.yml | 1 + .github/workflows/build-windows.yml | 60 +++++++++++-------------- 3 files changed, 27 insertions(+), 35 deletions(-) diff --git a/.github/workflows/build-posix-cmake.yml b/.github/workflows/build-posix-cmake.yml index 50f9ef902f..d61c16c47d 100644 --- a/.github/workflows/build-posix-cmake.yml +++ b/.github/workflows/build-posix-cmake.yml @@ -2,6 +2,7 @@ name: Build Posix CMake on: push: + branches: [main, master] pull_request: jobs: diff --git a/.github/workflows/build-posix.yml b/.github/workflows/build-posix.yml index 080c6e0453..0d2e8ef424 100644 --- a/.github/workflows/build-posix.yml +++ b/.github/workflows/build-posix.yml @@ -2,6 +2,7 @@ name: Build Posix on: push: + branches: [main, master] pull_request: jobs: diff --git a/.github/workflows/build-windows.yml b/.github/workflows/build-windows.yml index c9f5e2b4fa..dcaed88903 100644 --- a/.github/workflows/build-windows.yml +++ b/.github/workflows/build-windows.yml @@ -2,63 +2,53 @@ name: Build Windows on: push: + branches: [main, master] pull_request: jobs: build-windows: - runs-on: windows-2019 + runs-on: windows-latest + + defaults: + run: + shell: msys2 {0} steps: + - name: Setup MSYS2 + uses: msys2/setup-msys2@v2 + with: + msystem: MINGW64 + update: false + install: >- + mingw-w64-x86_64-gcc + mingw-w64-x86_64-cmake + mingw-w64-x86_64-ninja + make + - name: Git Checkout uses: actions/checkout@v4 with: submodules: recursive - - name: Prepare MSVC - uses: bus1/cabuild/action/msdevshell@v1 - with: - architecture: x86 + - name: Configure CMake + run: | + ABC_USE_STDINT_H=1 cmake -G Ninja -DCMAKE_BUILD_TYPE=Release -DCMAKE_C_FLAGS="-DWIN32 -DWIN32_NO_DLL" -DCMAKE_CXX_FLAGS="-DWIN32 -DWIN32_NO_DLL" -DREADLINE_FOUND=FALSE -DABC_SKIP_TESTS=1 -B build - - name: Upgrade project files to latest Visual Studio, ignoring upgrade errors, and build + - name: Build run: | - devenv abcspace.dsw /upgrade - # Fix the upgraded vcxproj files to use C++17 - if (Test-Path abclib.vcxproj) { - $content = Get-Content abclib.vcxproj -Raw - if ($content -match '<LanguageStandard>') { - $content = $content -replace '<LanguageStandard>Default</LanguageStandard>', '<LanguageStandard>stdcpp17</LanguageStandard>' - } else { - # Add LanguageStandard if it doesn't exist - $content = $content -replace '(<ClCompile>)', '$1<LanguageStandard>stdcpp17</LanguageStandard>' - } - Set-Content abclib.vcxproj -NoNewline $content - } - if (Test-Path abcexe.vcxproj) { - $content = Get-Content abcexe.vcxproj -Raw - if ($content -match '<LanguageStandard>') { - $content = $content -replace '<LanguageStandard>Default</LanguageStandard>', '<LanguageStandard>stdcpp17</LanguageStandard>' - } else { - # Add LanguageStandard if it doesn't exist - $content = $content -replace '(<ClCompile>)', '$1<LanguageStandard>stdcpp17</LanguageStandard>' - } - Set-Content abcexe.vcxproj -NoNewline $content - } - msbuild abcspace.sln /m /nologo /v:m /p:Configuration=Release /p:UseMultiToolTask=true /p:PlatformToolset=v142 /p:PreprocessorDefinitions="_WINSOCKAPI_" - if ($LASTEXITCODE -ne 0) { throw "Build failed with exit code $LASTEXITCODE" } + cmake --build build - name: Test Executable run: | - copy lib\x86\pthreadVC2.dll _TEST\ - .\_TEST\abc.exe -c "r i10.aig; b; ps; b; rw -l; rw -lz; b; rw -lz; b; ps; cec" - if ($LASTEXITCODE -ne 0) { throw "Test failed with exit code $LASTEXITCODE" } + ./build/abc.exe -c "r i10.aig; b; ps; b; rw -l; rw -lz; b; rw -lz; b; ps; cec" - name: Stage Executable run: | - mkdir staging - copy _TEST\abc.exe staging\ + mkdir -p staging + cp build/abc.exe staging/ - name: Upload package artifact uses: actions/upload-artifact@v4